serverless-ircd 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/ci.yml +3 -3
- package/.gitmodules +1 -1
- package/CHANGELOG.md +273 -29
- package/README.md +155 -55
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +186 -18
- package/apps/aws-stack/tests/stack.test.ts +400 -56
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/src/worker.ts +4 -4
- package/apps/cf-worker/tests/fixtures/web-dist/webclient/index.html +18 -0
- package/apps/cf-worker/tests/smoke.test.ts +5 -5
- package/apps/cf-worker/wrangler.test.toml +6 -6
- package/apps/cf-worker/wrangler.toml +7 -5
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +8 -0
- package/apps/local-cli/src/main.ts +16 -0
- package/apps/local-cli/src/server.ts +1 -0
- package/apps/local-cli/tests/config-loader.test.ts +14 -0
- package/apps/web/landing/index.html +14 -16
- package/apps/web/package.json +2 -2
- package/apps/web/scripts/build.mjs +23 -16
- package/apps/web/src/build-env.ts +1 -1
- package/apps/web/src/config-schema.ts +6 -6
- package/apps/web/tests/build-smoke.test.ts +14 -14
- package/apps/web/tests/config-schema.test.ts +1 -1
- package/docs/AWS-Deployment.md +21 -8
- package/docs/Cloudflare-Deployment-Guide.md +22 -6
- package/docs/PlanExtensions.md +113 -3
- package/docs/Release-Process.md +23 -13
- package/docs/Services.md +546 -0
- package/docs/WebClientGuide.md +32 -31
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +5 -0
- package/packages/aws-adapter/src/cdk-table-defs.ts +6 -0
- package/packages/aws-adapter/src/config-loader.ts +11 -0
- package/packages/aws-adapter/src/connection-counter.ts +89 -0
- package/packages/aws-adapter/src/dynamo-services-store.ts +649 -0
- package/packages/aws-adapter/src/handlers/connect.ts +55 -51
- package/packages/aws-adapter/src/handlers/default.ts +36 -4
- package/packages/aws-adapter/src/handlers/index.ts +15 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +15 -0
- package/packages/aws-adapter/src/handlers/sweeper.ts +5 -1
- package/packages/aws-adapter/src/index.ts +4 -0
- package/packages/aws-adapter/src/stats.ts +6 -1
- package/packages/aws-adapter/src/tables.ts +34 -4
- package/packages/aws-adapter/tests/aws-harness.ts +3 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +8 -0
- package/packages/aws-adapter/tests/connect.test.ts +158 -32
- package/packages/aws-adapter/tests/connection-counter.test.ts +127 -0
- package/packages/aws-adapter/tests/dynamo-services-store-dynamo.test.ts +183 -0
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +568 -0
- package/packages/aws-adapter/tests/handlers.test.ts +105 -3
- package/packages/aws-adapter/tests/tables.test.ts +6 -1
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/config-loader.ts +11 -0
- package/packages/cf-adapter/src/connection-do.ts +112 -2
- package/packages/cf-adapter/src/d1-services-store.ts +703 -0
- package/packages/cf-adapter/src/env.ts +8 -0
- package/packages/cf-adapter/src/index.ts +5 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +19 -0
- package/packages/cf-adapter/tests/connection-do-nickserv-d1.test.ts +128 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +150 -2
- package/packages/cf-adapter/tests/d1-services-store.test.ts +582 -0
- package/packages/cf-adapter/tests/serialize.test.ts +1 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/scripts/generate-build-info.mjs +26 -5
- package/packages/irc-core/src/commands/account-auth.ts +172 -0
- package/packages/irc-core/src/commands/chanserv.ts +882 -0
- package/packages/irc-core/src/commands/hostserv.ts +487 -0
- package/packages/irc-core/src/commands/index.ts +12 -0
- package/packages/irc-core/src/commands/join.ts +164 -8
- package/packages/irc-core/src/commands/markread.ts +202 -0
- package/packages/irc-core/src/commands/memoserv.ts +319 -0
- package/packages/irc-core/src/commands/mode.ts +96 -4
- package/packages/irc-core/src/commands/nickserv.ts +390 -0
- package/packages/irc-core/src/commands/oper.ts +18 -1
- package/packages/irc-core/src/commands/operserv.ts +346 -0
- package/packages/irc-core/src/commands/pre-away.ts +3 -1
- package/packages/irc-core/src/commands/privmsg.ts +42 -0
- package/packages/irc-core/src/commands/read-marker.ts +8 -8
- package/packages/irc-core/src/commands/registration.ts +61 -6
- package/packages/irc-core/src/commands/sasl.ts +18 -49
- package/packages/irc-core/src/commands/tagmsg.ts +41 -6
- package/packages/irc-core/src/commands/topic.ts +37 -0
- package/packages/irc-core/src/config.ts +36 -5
- package/packages/irc-core/src/effects.ts +56 -1
- package/packages/irc-core/src/ports.ts +1653 -84
- package/packages/irc-core/src/protocol/numerics.ts +8 -0
- package/packages/irc-core/src/state/channel.ts +21 -1
- package/packages/irc-core/src/state/connection.ts +25 -1
- package/packages/irc-core/src/types.ts +48 -12
- package/packages/irc-core/tests/commands/chanserv.test.ts +1668 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +6 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +935 -0
- package/packages/irc-core/tests/commands/join.test.ts +393 -1
- package/packages/irc-core/tests/commands/markread.test.ts +361 -0
- package/packages/irc-core/tests/commands/memoserv.test.ts +654 -0
- package/packages/irc-core/tests/commands/mode.test.ts +381 -2
- package/packages/irc-core/tests/commands/nickserv.test.ts +807 -0
- package/packages/irc-core/tests/commands/oper.test.ts +13 -0
- package/packages/irc-core/tests/commands/operserv.test.ts +656 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +147 -0
- package/packages/irc-core/tests/commands/read-marker.test.ts +28 -28
- package/packages/irc-core/tests/commands/registration.test.ts +788 -14
- package/packages/irc-core/tests/commands/sasl.test.ts +185 -12
- package/packages/irc-core/tests/commands/server-info.test.ts +9 -5
- package/packages/irc-core/tests/commands/tagmsg.test.ts +73 -33
- package/packages/irc-core/tests/commands/topic.test.ts +94 -2
- package/packages/irc-core/tests/commands/unified-account.test.ts +416 -0
- package/packages/irc-core/tests/config.test.ts +49 -5
- package/packages/irc-core/tests/effects.test.ts +19 -0
- package/packages/irc-core/tests/message-store.test.ts +63 -0
- package/packages/irc-core/tests/persistent-services-store.test.ts +582 -0
- package/packages/irc-core/tests/services-store.test.ts +1289 -0
- package/packages/irc-core/tests/state/channel.test.ts +3 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +71 -16
- package/packages/irc-server/src/dispatch.ts +94 -7
- package/packages/irc-server/src/routing.ts +19 -0
- package/packages/irc-server/tests/actor.test.ts +623 -12
- package/packages/irc-server/tests/dispatch.test.ts +270 -2
- package/packages/irc-server/tests/routing.test.ts +6 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +29 -3
- package/packages/irc-test-support/src/index.ts +1 -0
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +32 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +2 -2
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +0 -18
- package/packages/irc-core/tests/read-marker-store.test.ts +0 -108
|
@@ -16,9 +16,18 @@
|
|
|
16
16
|
import { resolve } from 'node:path';
|
|
17
17
|
import { fileURLToPath } from 'node:url';
|
|
18
18
|
import { TABLE_DEFS } from '@serverless-ircd/aws-adapter/cdk-table-defs';
|
|
19
|
-
import {
|
|
19
|
+
import {
|
|
20
|
+
CfnOutput,
|
|
21
|
+
CfnParameter,
|
|
22
|
+
Duration,
|
|
23
|
+
RemovalPolicy,
|
|
24
|
+
Stack,
|
|
25
|
+
type StackProps,
|
|
26
|
+
} from 'aws-cdk-lib';
|
|
27
|
+
import { CfnAccount } from 'aws-cdk-lib/aws-apigateway';
|
|
20
28
|
import {
|
|
21
29
|
CfnIntegrationResponse,
|
|
30
|
+
type CfnStage,
|
|
22
31
|
WebSocketApi,
|
|
23
32
|
type WebSocketIntegration,
|
|
24
33
|
WebSocketStage,
|
|
@@ -37,8 +46,10 @@ import {
|
|
|
37
46
|
import { LambdaTarget } from 'aws-cdk-lib/aws-elasticloadbalancingv2-targets';
|
|
38
47
|
import { Rule, Schedule } from 'aws-cdk-lib/aws-events';
|
|
39
48
|
import { LambdaFunction } from 'aws-cdk-lib/aws-events-targets';
|
|
49
|
+
import { PolicyStatement, Role, ServicePrincipal } from 'aws-cdk-lib/aws-iam';
|
|
40
50
|
import { Code, Function as Lambda, Runtime } from 'aws-cdk-lib/aws-lambda';
|
|
41
51
|
import { NodejsFunction, type NodejsFunctionProps } from 'aws-cdk-lib/aws-lambda-nodejs';
|
|
52
|
+
import { LogGroup, RetentionDays } from 'aws-cdk-lib/aws-logs';
|
|
42
53
|
import type { Construct } from 'constructs';
|
|
43
54
|
|
|
44
55
|
/** Stage name served by the auto-deploying WebSocket stage. */
|
|
@@ -154,10 +165,79 @@ export class IrcAwsStack extends Stack {
|
|
|
154
165
|
super(scope, id, props);
|
|
155
166
|
|
|
156
167
|
const environmentName = props.environmentName ?? DEFAULT_ENVIRONMENT_NAME;
|
|
157
|
-
|
|
158
|
-
|
|
168
|
+
|
|
169
|
+
// Server identity is exposed as CloudFormation Parameters so it can be
|
|
170
|
+
// overridden at deploy time (`cdk deploy --parameters ServerName=...`,
|
|
171
|
+
// `aws cloudformation update-stack --parameters`, or the CFN console)
|
|
172
|
+
// without re-synth. The construct prop (and the `bin/aws.ts` context
|
|
173
|
+
// var) flow into the parameter `Default`; a `--parameters` override at
|
|
174
|
+
// deploy time wins over the default. Lambdas consume the value via
|
|
175
|
+
// `{ Ref: ... }` so an update rolls the env vars with no code change.
|
|
176
|
+
const serverNameParam = new CfnParameter(this, 'ServerName', {
|
|
177
|
+
type: 'String',
|
|
178
|
+
description:
|
|
179
|
+
"Server name advertised to IRC clients (the `001` welcome source, ISUPPORT's SERVER_NAME). " +
|
|
180
|
+
'Override at deploy time via `--parameters ServerName=...`.',
|
|
181
|
+
default: props.serverName ?? DEFAULT_SERVER_NAME,
|
|
182
|
+
});
|
|
183
|
+
const networkNameParam = new CfnParameter(this, 'NetworkName', {
|
|
184
|
+
type: 'String',
|
|
185
|
+
description:
|
|
186
|
+
'Network name advertised to IRC clients in ISUPPORT (`NETWORK`). ' +
|
|
187
|
+
'Override at deploy time via `--parameters NetworkName=...`.',
|
|
188
|
+
default: props.networkName ?? DEFAULT_NETWORK_NAME,
|
|
189
|
+
});
|
|
190
|
+
const serverName = serverNameParam.valueAsString;
|
|
191
|
+
const networkName = networkNameParam.valueAsString;
|
|
192
|
+
|
|
193
|
+
// Execution logging for the WebSocket API. INFO enables full
|
|
194
|
+
// request/response tracing (verbose — use for debugging connect/drop
|
|
195
|
+
// issues); ERROR logs only server-side failures; OFF disables. The
|
|
196
|
+
// level is a CFN parameter so it can be flipped at deploy time without
|
|
197
|
+
// a code change: `cdk deploy --parameters ApiGatewayLoggingLevel=OFF`.
|
|
198
|
+
// NB: execution logging also needs the account-level CloudWatch role,
|
|
199
|
+
// provisioned below (`ApiGatewayAccount`); that resource is GLOBAL per
|
|
200
|
+
// account+region, so two stacks with logging ON in the same
|
|
201
|
+
// account+region will conflict — deploy each env in its own account or
|
|
202
|
+
// set this to OFF.
|
|
203
|
+
const apiLoggingLevelParam = new CfnParameter(this, 'ApiGatewayLoggingLevel', {
|
|
204
|
+
type: 'String',
|
|
205
|
+
description:
|
|
206
|
+
'API Gateway WebSocket execution logging level. INFO = full request/response tracing ' +
|
|
207
|
+
'(verbose, for debugging); ERROR = 5xx only; OFF = disabled. Requires the account ' +
|
|
208
|
+
'CloudWatch role (auto-provisioned; global per account+region).',
|
|
209
|
+
default: 'INFO',
|
|
210
|
+
allowedValues: ['OFF', 'INFO', 'ERROR'],
|
|
211
|
+
});
|
|
159
212
|
const motd = (props.motdLines ?? DEFAULT_MOTD_LINES).join('\n');
|
|
160
213
|
|
|
214
|
+
// EventBridge schedule expressions are surfaced as CloudFormation
|
|
215
|
+
// Parameters for the same reason as server identity: they can be
|
|
216
|
+
// overridden at deploy time (`--parameters SweeperScheduleExpression=...`)
|
|
217
|
+
// without a re-synth. The defaults are derived from the same
|
|
218
|
+
// `Schedule.rate(Duration)` calls used previously so the produced
|
|
219
|
+
// strings (`rate(5 minutes)`, `rate(1 minute)` — including the
|
|
220
|
+
// singular/plural handling) are byte-identical to the old behaviour.
|
|
221
|
+
// Validation moves from synth (Duration bounds) to deploy time: an
|
|
222
|
+
// invalid expression (`rate(0 minutes)`, a malformed `cron(...)`)
|
|
223
|
+
// fails the `AWS::Events::Rule` create with an EventBridge error.
|
|
224
|
+
const sweeperScheduleParam = new CfnParameter(this, 'SweeperScheduleExpression', {
|
|
225
|
+
type: 'String',
|
|
226
|
+
description:
|
|
227
|
+
'EventBridge schedule expression for the gone-connection sweeper ' +
|
|
228
|
+
'(`rate(5 minutes)` or a `cron(...)` expression). Override at deploy ' +
|
|
229
|
+
'time via `--parameters SweeperScheduleExpression=...`.',
|
|
230
|
+
default: Schedule.rate(SWEEPER_SCHEDULE_RATE).expressionString,
|
|
231
|
+
});
|
|
232
|
+
const pingCheckerScheduleParam = new CfnParameter(this, 'PingCheckerScheduleExpression', {
|
|
233
|
+
type: 'String',
|
|
234
|
+
description:
|
|
235
|
+
'EventBridge schedule expression for the idle / PING checker ' +
|
|
236
|
+
'(`rate(1 minute)` or a `cron(...)` expression). Override at deploy ' +
|
|
237
|
+
'time via `--parameters PingCheckerScheduleExpression=...`.',
|
|
238
|
+
default: Schedule.rate(PING_CHECKER_SCHEDULE_RATE).expressionString,
|
|
239
|
+
});
|
|
240
|
+
|
|
161
241
|
// Prefix every physical table name with the environment so parallel
|
|
162
242
|
// envs (staging + production) cannot collide in one account+region.
|
|
163
243
|
// The first letter is capitalised to compose cleanly with the
|
|
@@ -248,7 +328,7 @@ export class IrcAwsStack extends Stack {
|
|
|
248
328
|
integrationId: connectIntegration.integrationId,
|
|
249
329
|
integrationResponseKey: '$default',
|
|
250
330
|
responseParameters: {
|
|
251
|
-
'
|
|
331
|
+
'route.response.header.Sec-WebSocket-Protocol':
|
|
252
332
|
'integration.response.header.Sec-WebSocket-Protocol',
|
|
253
333
|
},
|
|
254
334
|
});
|
|
@@ -258,11 +338,90 @@ export class IrcAwsStack extends Stack {
|
|
|
258
338
|
// own WebSocket API), so `prod` in every env resolves to a distinct
|
|
259
339
|
// endpoint. Keeping a fixed stage keeps client connect URLs stable
|
|
260
340
|
// and identical in shape across environments.
|
|
341
|
+
// CloudWatch LogGroup + IAM role for API Gateway access + execution
|
|
342
|
+
// logging. Execution logging (full request/response at INFO) is what
|
|
343
|
+
// makes APIGW-side frame drops diagnosable — the Lambda logs alone
|
|
344
|
+
// can't show a frame that never reached the handler.
|
|
345
|
+
const apiLogGroup = new LogGroup(this, 'ApiGatewayAccessLogs', {
|
|
346
|
+
retention: RetentionDays.TWO_WEEKS,
|
|
347
|
+
removalPolicy: RemovalPolicy.DESTROY,
|
|
348
|
+
});
|
|
349
|
+
const apiLoggingRole = new Role(this, 'ApiGatewayLoggingRole', {
|
|
350
|
+
assumedBy: new ServicePrincipal('apigateway.amazonaws.com'),
|
|
351
|
+
});
|
|
352
|
+
// APIGW validates this role when it is set as the account CloudWatch
|
|
353
|
+
// role, and rejects one scoped to a single log-group ARN: execution
|
|
354
|
+
// logs land in APIGW's OWN log groups (AWS/ApiGateway...), separate
|
|
355
|
+
// from this stack's access-log group. Grant the logs write actions
|
|
356
|
+
// across all log groups. Defined inline (not via the
|
|
357
|
+
// AmazonAPIGatewayPushToCloudWatchLogs managed policy) because that
|
|
358
|
+
// managed policy is not available in every partition.
|
|
359
|
+
apiLoggingRole.addToPolicy(
|
|
360
|
+
new PolicyStatement({
|
|
361
|
+
actions: [
|
|
362
|
+
'logs:CreateLogGroup',
|
|
363
|
+
'logs:CreateLogStream',
|
|
364
|
+
'logs:DescribeLogGroups',
|
|
365
|
+
'logs:DescribeLogStreams',
|
|
366
|
+
'logs:PutLogEvents',
|
|
367
|
+
],
|
|
368
|
+
resources: ['*'],
|
|
369
|
+
}),
|
|
370
|
+
);
|
|
371
|
+
// Account-level CloudWatch role for API Gateway. GLOBAL per
|
|
372
|
+
// account+region (see ApiGatewayLoggingLevel caveat above): two stacks
|
|
373
|
+
// in the same account+region both defining this resource will conflict.
|
|
374
|
+
const apiAccount = new CfnAccount(this, 'ApiGatewayAccount', {
|
|
375
|
+
cloudWatchRoleArn: apiLoggingRole.roleArn,
|
|
376
|
+
});
|
|
377
|
+
// APIGW validates the role's CONTENT — trust AND the attached
|
|
378
|
+
// permissions — when it processes the AWS::ApiGateway::Account
|
|
379
|
+
// resource. `addToPolicy` renders as a SEPARATE AWS::IAM::Policy, and
|
|
380
|
+
// the Account only references the role Arn, so CloudFormation can apply
|
|
381
|
+
// the Account after the Role is created but BEFORE the policy attaches.
|
|
382
|
+
// APIGW then assumes a permission-less role and rejects the update:
|
|
383
|
+
// "The role ARN does not have required permissions configured."
|
|
384
|
+
// The explicit dependency forces the policy to be applied first.
|
|
385
|
+
const loggingRolePolicy = apiLoggingRole.node.findChild('DefaultPolicy');
|
|
386
|
+
apiAccount.node.addDependency(loggingRolePolicy);
|
|
387
|
+
|
|
261
388
|
const stage = new WebSocketStage(this, 'Stage', {
|
|
262
389
|
webSocketApi,
|
|
263
390
|
stageName: STAGE_NAME,
|
|
264
391
|
autoDeploy: true,
|
|
265
392
|
});
|
|
393
|
+
const cfnStage = stage.node.defaultChild as CfnStage;
|
|
394
|
+
// The Stage's logging config requires the account-wide CloudWatch role
|
|
395
|
+
// to exist first; without this explicit dependency CloudFormation can
|
|
396
|
+
// apply the Stage before the Account resource and APIGW rejects the
|
|
397
|
+
// update: "CloudWatch Logs role ARN must be set in account settings".
|
|
398
|
+
cfnStage.addResourceDependency(apiAccount);
|
|
399
|
+
// Access + execution logging on the stage. Set at the L1 (CfnStage)
|
|
400
|
+
// level because the L2 `accessLogSettings.format` expects an ILogFormat
|
|
401
|
+
// object; the raw CFN `AccessLogSettings.Format` is a plain string.
|
|
402
|
+
// Access log: one line per request — enough to correlate a connect /
|
|
403
|
+
// frame with a connectionId without the verbosity of execution logs.
|
|
404
|
+
cfnStage.accessLogSettings = {
|
|
405
|
+
destinationArn: apiLogGroup.logGroupArn,
|
|
406
|
+
format: JSON.stringify({
|
|
407
|
+
requestId: '$context.requestId',
|
|
408
|
+
routeKey: '$context.routeKey',
|
|
409
|
+
eventType: '$context.eventType',
|
|
410
|
+
connectionId: '$context.connectionId',
|
|
411
|
+
status: '$context.status',
|
|
412
|
+
responseLength: '$context.responseLength',
|
|
413
|
+
requestTime: '$context.requestTime',
|
|
414
|
+
sourceIp: '$context.sourceIp',
|
|
415
|
+
}),
|
|
416
|
+
};
|
|
417
|
+
// Execution logging: the parameter drives LoggingLevel (OFF/INFO/ERROR);
|
|
418
|
+
// DataTraceEnabled logs full request/response bodies when a level is
|
|
419
|
+
// active. This is the layer that shows whether APIGW received and routed
|
|
420
|
+
// a client frame or dropped it pre-Lambda.
|
|
421
|
+
cfnStage.defaultRouteSettings = {
|
|
422
|
+
loggingLevel: apiLoggingLevelParam.valueAsString,
|
|
423
|
+
dataTraceEnabled: true,
|
|
424
|
+
};
|
|
266
425
|
|
|
267
426
|
stage.grantManagementApiAccess(handler);
|
|
268
427
|
stage.grantManagementApiAccess(pingChecker);
|
|
@@ -283,31 +442,40 @@ export class IrcAwsStack extends Stack {
|
|
|
283
442
|
pingChecker.addEnvironment(envName, physicalName);
|
|
284
443
|
}
|
|
285
444
|
|
|
286
|
-
// Server identity
|
|
287
|
-
//
|
|
288
|
-
//
|
|
289
|
-
//
|
|
290
|
-
//
|
|
291
|
-
//
|
|
445
|
+
// Server identity is injected into the handler, ping checker, AND the
|
|
446
|
+
// sweeper. All three share `buildDepsFromEnv` at cold start, which calls
|
|
447
|
+
// `loadServerConfigFromLambdaEnv` — and that loader hard-requires
|
|
448
|
+
// SERVER_NAME / NETWORK_NAME (and reads MOTD). Omitting them on the
|
|
449
|
+
// sweeper made every scheduled invocation throw "serverName: Required".
|
|
450
|
+
// Values flow through the CloudFormation parameters ({ Ref }) so a
|
|
451
|
+
// `--parameters` update rolls all four Lambdas with no code change.
|
|
452
|
+
// MOTD is `\n`-joined so `buildLambdaConfigInput` splits it back into
|
|
453
|
+
// the expected `motdLines` array.
|
|
292
454
|
handler.addEnvironment('SERVER_NAME', serverName);
|
|
293
455
|
handler.addEnvironment('NETWORK_NAME', networkName);
|
|
294
456
|
handler.addEnvironment('MOTD', motd);
|
|
295
457
|
pingChecker.addEnvironment('SERVER_NAME', serverName);
|
|
296
458
|
pingChecker.addEnvironment('NETWORK_NAME', networkName);
|
|
297
459
|
pingChecker.addEnvironment('MOTD', motd);
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
460
|
+
sweeper.addEnvironment('SERVER_NAME', serverName);
|
|
461
|
+
sweeper.addEnvironment('NETWORK_NAME', networkName);
|
|
462
|
+
sweeper.addEnvironment('MOTD', motd);
|
|
463
|
+
|
|
464
|
+
// EventBridge schedule → sweeper Lambda. The schedule expression comes
|
|
465
|
+
// from a CloudFormation Parameter (see above), so it can be overridden
|
|
466
|
+
// at deploy time; the sweeper is self-throttling
|
|
467
|
+
// (Scans + per-row cleanupConnection).
|
|
301
468
|
new Rule(this, 'SweeperSchedule', {
|
|
302
|
-
schedule: Schedule.
|
|
469
|
+
schedule: Schedule.expression(sweeperScheduleParam.valueAsString),
|
|
303
470
|
targets: [new LambdaFunction(sweeper)],
|
|
304
471
|
});
|
|
305
472
|
|
|
306
|
-
// EventBridge schedule → idle / PING checker Lambda.
|
|
307
|
-
//
|
|
308
|
-
//
|
|
473
|
+
// EventBridge schedule → idle / PING checker Lambda. Schedule expression
|
|
474
|
+
// is a deploy-time Parameter; the default (`rate(1 minute)`) means an
|
|
475
|
+
// idle connection sees its first PING within one tick of the configured
|
|
476
|
+
// `DEFAULT_PING_INTERVAL_MS` (60 s).
|
|
309
477
|
new Rule(this, 'PingCheckerSchedule', {
|
|
310
|
-
schedule: Schedule.
|
|
478
|
+
schedule: Schedule.expression(pingCheckerScheduleParam.valueAsString),
|
|
311
479
|
targets: [new LambdaFunction(pingChecker)],
|
|
312
480
|
});
|
|
313
481
|
|