@de-otio/chaoskb-server 0.2.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.
Files changed (157) hide show
  1. package/dist/lib/admin-handler/index.d.ts +22 -0
  2. package/dist/lib/admin-handler/index.d.ts.map +1 -0
  3. package/dist/lib/admin-handler/index.js +92 -0
  4. package/dist/lib/admin-handler/index.js.map +1 -0
  5. package/dist/lib/admin-handler/index.ts +123 -0
  6. package/dist/lib/admin-handler/routes/metrics.d.ts +11 -0
  7. package/dist/lib/admin-handler/routes/metrics.d.ts.map +1 -0
  8. package/dist/lib/admin-handler/routes/metrics.js +200 -0
  9. package/dist/lib/admin-handler/routes/metrics.js.map +1 -0
  10. package/dist/lib/admin-handler/routes/metrics.ts +234 -0
  11. package/dist/lib/admin-handler/routes/overview.d.ts +9 -0
  12. package/dist/lib/admin-handler/routes/overview.d.ts.map +1 -0
  13. package/dist/lib/admin-handler/routes/overview.js +110 -0
  14. package/dist/lib/admin-handler/routes/overview.js.map +1 -0
  15. package/dist/lib/admin-handler/routes/overview.ts +133 -0
  16. package/dist/lib/admin-handler/routes/tenants.d.ts +10 -0
  17. package/dist/lib/admin-handler/routes/tenants.d.ts.map +1 -0
  18. package/dist/lib/admin-handler/routes/tenants.js +108 -0
  19. package/dist/lib/admin-handler/routes/tenants.js.map +1 -0
  20. package/dist/lib/admin-handler/routes/tenants.ts +134 -0
  21. package/dist/lib/chaoskb-stack.d.ts +22 -0
  22. package/dist/lib/chaoskb-stack.d.ts.map +1 -0
  23. package/dist/lib/chaoskb-stack.js +60 -0
  24. package/dist/lib/chaoskb-stack.js.map +1 -0
  25. package/dist/lib/constructs/admin-api.d.ts +16 -0
  26. package/dist/lib/constructs/admin-api.d.ts.map +1 -0
  27. package/dist/lib/constructs/admin-api.js +93 -0
  28. package/dist/lib/constructs/admin-api.js.map +1 -0
  29. package/dist/lib/constructs/admin-dashboard.d.ts +18 -0
  30. package/dist/lib/constructs/admin-dashboard.d.ts.map +1 -0
  31. package/dist/lib/constructs/admin-dashboard.js +172 -0
  32. package/dist/lib/constructs/admin-dashboard.js.map +1 -0
  33. package/dist/lib/constructs/api.d.ts +17 -0
  34. package/dist/lib/constructs/api.d.ts.map +1 -0
  35. package/dist/lib/constructs/api.js +81 -0
  36. package/dist/lib/constructs/api.js.map +1 -0
  37. package/dist/lib/constructs/auth.d.ts +11 -0
  38. package/dist/lib/constructs/auth.d.ts.map +1 -0
  39. package/dist/lib/constructs/auth.js +18 -0
  40. package/dist/lib/constructs/auth.js.map +1 -0
  41. package/dist/lib/constructs/blob-store.d.ts +10 -0
  42. package/dist/lib/constructs/blob-store.d.ts.map +1 -0
  43. package/dist/lib/constructs/blob-store.js +31 -0
  44. package/dist/lib/constructs/blob-store.js.map +1 -0
  45. package/dist/lib/deploy-cli.d.ts +3 -0
  46. package/dist/lib/deploy-cli.d.ts.map +1 -0
  47. package/dist/lib/deploy-cli.js +49 -0
  48. package/dist/lib/deploy-cli.js.map +1 -0
  49. package/dist/lib/handler/index.d.ts +23 -0
  50. package/dist/lib/handler/index.d.ts.map +1 -0
  51. package/dist/lib/handler/index.js +276 -0
  52. package/dist/lib/handler/index.js.map +1 -0
  53. package/dist/lib/handler/index.ts +372 -0
  54. package/dist/lib/handler/logger.d.ts +16 -0
  55. package/dist/lib/handler/logger.d.ts.map +1 -0
  56. package/dist/lib/handler/logger.js +26 -0
  57. package/dist/lib/handler/logger.js.map +1 -0
  58. package/dist/lib/handler/logger.ts +36 -0
  59. package/dist/lib/handler/middleware/input-validation.d.ts +6 -0
  60. package/dist/lib/handler/middleware/input-validation.d.ts.map +1 -0
  61. package/dist/lib/handler/middleware/input-validation.js +36 -0
  62. package/dist/lib/handler/middleware/input-validation.js.map +1 -0
  63. package/dist/lib/handler/middleware/input-validation.ts +44 -0
  64. package/dist/lib/handler/middleware/rate-limit.d.ts +14 -0
  65. package/dist/lib/handler/middleware/rate-limit.d.ts.map +1 -0
  66. package/dist/lib/handler/middleware/rate-limit.js +94 -0
  67. package/dist/lib/handler/middleware/rate-limit.js.map +1 -0
  68. package/dist/lib/handler/middleware/rate-limit.ts +121 -0
  69. package/dist/lib/handler/middleware/ssh-auth.d.ts +48 -0
  70. package/dist/lib/handler/middleware/ssh-auth.d.ts.map +1 -0
  71. package/dist/lib/handler/middleware/ssh-auth.js +256 -0
  72. package/dist/lib/handler/middleware/ssh-auth.js.map +1 -0
  73. package/dist/lib/handler/middleware/ssh-auth.ts +300 -0
  74. package/dist/lib/handler/routes/audit.d.ts +24 -0
  75. package/dist/lib/handler/routes/audit.d.ts.map +1 -0
  76. package/dist/lib/handler/routes/audit.js +94 -0
  77. package/dist/lib/handler/routes/audit.js.map +1 -0
  78. package/dist/lib/handler/routes/audit.ts +101 -0
  79. package/dist/lib/handler/routes/blobs.d.ts +13 -0
  80. package/dist/lib/handler/routes/blobs.d.ts.map +1 -0
  81. package/dist/lib/handler/routes/blobs.js +298 -0
  82. package/dist/lib/handler/routes/blobs.js.map +1 -0
  83. package/dist/lib/handler/routes/blobs.ts +348 -0
  84. package/dist/lib/handler/routes/devices.d.ts +48 -0
  85. package/dist/lib/handler/routes/devices.d.ts.map +1 -0
  86. package/dist/lib/handler/routes/devices.js +394 -0
  87. package/dist/lib/handler/routes/devices.js.map +1 -0
  88. package/dist/lib/handler/routes/devices.ts +458 -0
  89. package/dist/lib/handler/routes/export.d.ts +9 -0
  90. package/dist/lib/handler/routes/export.d.ts.map +1 -0
  91. package/dist/lib/handler/routes/export.js +40 -0
  92. package/dist/lib/handler/routes/export.js.map +1 -0
  93. package/dist/lib/handler/routes/export.ts +55 -0
  94. package/dist/lib/handler/routes/github.d.ts +31 -0
  95. package/dist/lib/handler/routes/github.d.ts.map +1 -0
  96. package/dist/lib/handler/routes/github.js +118 -0
  97. package/dist/lib/handler/routes/github.js.map +1 -0
  98. package/dist/lib/handler/routes/github.ts +162 -0
  99. package/dist/lib/handler/routes/health.d.ts +6 -0
  100. package/dist/lib/handler/routes/health.d.ts.map +1 -0
  101. package/dist/lib/handler/routes/health.js +14 -0
  102. package/dist/lib/handler/routes/health.js.map +1 -0
  103. package/dist/lib/handler/routes/health.ts +10 -0
  104. package/dist/lib/handler/routes/invites.d.ts +24 -0
  105. package/dist/lib/handler/routes/invites.d.ts.map +1 -0
  106. package/dist/lib/handler/routes/invites.js +445 -0
  107. package/dist/lib/handler/routes/invites.js.map +1 -0
  108. package/dist/lib/handler/routes/invites.ts +527 -0
  109. package/dist/lib/handler/routes/notifications.d.ts +39 -0
  110. package/dist/lib/handler/routes/notifications.d.ts.map +1 -0
  111. package/dist/lib/handler/routes/notifications.js +150 -0
  112. package/dist/lib/handler/routes/notifications.js.map +1 -0
  113. package/dist/lib/handler/routes/notifications.ts +163 -0
  114. package/dist/lib/handler/routes/projects.d.ts +24 -0
  115. package/dist/lib/handler/routes/projects.d.ts.map +1 -0
  116. package/dist/lib/handler/routes/projects.js +47 -0
  117. package/dist/lib/handler/routes/projects.js.map +1 -0
  118. package/dist/lib/handler/routes/projects.ts +69 -0
  119. package/dist/lib/handler/routes/register.d.ts +19 -0
  120. package/dist/lib/handler/routes/register.d.ts.map +1 -0
  121. package/dist/lib/handler/routes/register.js +327 -0
  122. package/dist/lib/handler/routes/register.js.map +1 -0
  123. package/dist/lib/handler/routes/register.ts +363 -0
  124. package/dist/lib/handler/routes/restore.d.ts +9 -0
  125. package/dist/lib/handler/routes/restore.d.ts.map +1 -0
  126. package/dist/lib/handler/routes/restore.js +52 -0
  127. package/dist/lib/handler/routes/restore.js.map +1 -0
  128. package/dist/lib/handler/routes/restore.ts +73 -0
  129. package/dist/lib/handler/routes/revocation.d.ts +13 -0
  130. package/dist/lib/handler/routes/revocation.d.ts.map +1 -0
  131. package/dist/lib/handler/routes/revocation.js +63 -0
  132. package/dist/lib/handler/routes/revocation.js.map +1 -0
  133. package/dist/lib/handler/routes/revocation.ts +87 -0
  134. package/dist/lib/handler/routes/rotation.d.ts +24 -0
  135. package/dist/lib/handler/routes/rotation.d.ts.map +1 -0
  136. package/dist/lib/handler/routes/rotation.js +291 -0
  137. package/dist/lib/handler/routes/rotation.js.map +1 -0
  138. package/dist/lib/handler/routes/rotation.ts +336 -0
  139. package/dist/lib/handler/routes/tenants.d.ts +11 -0
  140. package/dist/lib/handler/routes/tenants.d.ts.map +1 -0
  141. package/dist/lib/handler/routes/tenants.js +181 -0
  142. package/dist/lib/handler/routes/tenants.js.map +1 -0
  143. package/dist/lib/handler/routes/tenants.ts +198 -0
  144. package/dist/lib/handler/routes/wrapped-key.d.ts +21 -0
  145. package/dist/lib/handler/routes/wrapped-key.d.ts.map +1 -0
  146. package/dist/lib/handler/routes/wrapped-key.js +76 -0
  147. package/dist/lib/handler/routes/wrapped-key.js.map +1 -0
  148. package/dist/lib/handler/routes/wrapped-key.ts +108 -0
  149. package/dist/lib/index.d.ts +7 -0
  150. package/dist/lib/index.d.ts.map +1 -0
  151. package/dist/lib/index.js +16 -0
  152. package/dist/lib/index.js.map +1 -0
  153. package/dist/vitest.config.d.ts +3 -0
  154. package/dist/vitest.config.d.ts.map +1 -0
  155. package/dist/vitest.config.js +18 -0
  156. package/dist/vitest.config.js.map +1 -0
  157. package/package.json +61 -0
@@ -0,0 +1,134 @@
1
+ import { DynamoDBDocumentClient, ScanCommand, QueryCommand } from '@aws-sdk/lib-dynamodb';
2
+ import { logger } from '../../handler/logger.js';
3
+
4
+ interface RouteResult {
5
+ statusCode: number;
6
+ body: string;
7
+ headers: Record<string, string>;
8
+ }
9
+
10
+ const PAGE_SIZE = 25;
11
+
12
+ export async function handleListTenants(
13
+ page: number,
14
+ ddb: DynamoDBDocumentClient,
15
+ tableName: string,
16
+ ): Promise<RouteResult> {
17
+ try {
18
+ const validPage = Math.max(1, page);
19
+
20
+ // Scan for all META records to get tenant list
21
+ const allTenants: Record<string, unknown>[] = [];
22
+ let exclusiveStartKey: Record<string, unknown> | undefined;
23
+
24
+ do {
25
+ const result = await ddb.send(
26
+ new ScanCommand({
27
+ TableName: tableName,
28
+ FilterExpression: 'SK = :sk',
29
+ ExpressionAttributeValues: { ':sk': 'META' },
30
+ ProjectionExpression: 'PK, publicKey, createdAt, updatedAt, storageUsedBytes',
31
+ ExclusiveStartKey: exclusiveStartKey,
32
+ }),
33
+ );
34
+
35
+ if (result.Items) {
36
+ allTenants.push(...result.Items);
37
+ }
38
+ exclusiveStartKey = result.LastEvaluatedKey;
39
+ } while (exclusiveStartKey);
40
+
41
+ const total = allTenants.length;
42
+ const startIndex = (validPage - 1) * PAGE_SIZE;
43
+ const paginatedTenants = allTenants.slice(startIndex, startIndex + PAGE_SIZE).map((item) => ({
44
+ tenantId: (item['PK'] as string).replace('TENANT#', ''),
45
+ publicKey: item['publicKey'],
46
+ createdAt: item['createdAt'],
47
+ updatedAt: item['updatedAt'],
48
+ storageUsedBytes: item['storageUsedBytes'] ?? 0,
49
+ }));
50
+
51
+ return {
52
+ statusCode: 200,
53
+ body: JSON.stringify({
54
+ tenants: paginatedTenants,
55
+ total,
56
+ page: validPage,
57
+ pageSize: PAGE_SIZE,
58
+ }),
59
+ headers: { 'Content-Type': 'application/json' },
60
+ };
61
+ } catch (err) {
62
+ logger.error('Failed to list tenants', { error: String(err) });
63
+ return {
64
+ statusCode: 500,
65
+ body: JSON.stringify({ error: 'internal_error', message: 'Failed to list tenants' }),
66
+ headers: { 'Content-Type': 'application/json' },
67
+ };
68
+ }
69
+ }
70
+
71
+ export async function handleGetTenantDetail(
72
+ tenantId: string,
73
+ ddb: DynamoDBDocumentClient,
74
+ tableName: string,
75
+ ): Promise<RouteResult> {
76
+ try {
77
+ // Query for the META record
78
+ const metaResult = await ddb.send(
79
+ new QueryCommand({
80
+ TableName: tableName,
81
+ KeyConditionExpression: 'PK = :pk AND SK = :sk',
82
+ ExpressionAttributeValues: {
83
+ ':pk': `TENANT#${tenantId}`,
84
+ ':sk': 'META',
85
+ },
86
+ }),
87
+ );
88
+
89
+ if (!metaResult.Items || metaResult.Items.length === 0) {
90
+ return {
91
+ statusCode: 404,
92
+ body: JSON.stringify({ error: 'not_found', message: 'Tenant not found' }),
93
+ headers: { 'Content-Type': 'application/json' },
94
+ };
95
+ }
96
+
97
+ const meta = metaResult.Items[0];
98
+
99
+ // Count blobs for this tenant
100
+ const blobResult = await ddb.send(
101
+ new QueryCommand({
102
+ TableName: tableName,
103
+ KeyConditionExpression: 'PK = :pk AND begins_with(SK, :sk)',
104
+ ExpressionAttributeValues: {
105
+ ':pk': `TENANT#${tenantId}`,
106
+ ':sk': 'BLOB#',
107
+ },
108
+ Select: 'COUNT',
109
+ }),
110
+ );
111
+
112
+ const blobCount = blobResult.Count ?? 0;
113
+
114
+ return {
115
+ statusCode: 200,
116
+ body: JSON.stringify({
117
+ tenantId,
118
+ publicKey: meta['publicKey'],
119
+ createdAt: meta['createdAt'],
120
+ updatedAt: meta['updatedAt'],
121
+ storageUsedBytes: meta['storageUsedBytes'] ?? 0,
122
+ blobCount,
123
+ }),
124
+ headers: { 'Content-Type': 'application/json' },
125
+ };
126
+ } catch (err) {
127
+ logger.error('Failed to get tenant detail', { error: String(err) });
128
+ return {
129
+ statusCode: 500,
130
+ body: JSON.stringify({ error: 'internal_error', message: 'Failed to get tenant detail' }),
131
+ headers: { 'Content-Type': 'application/json' },
132
+ };
133
+ }
134
+ }
@@ -0,0 +1,22 @@
1
+ import { Stack, StackProps } from 'aws-cdk-lib';
2
+ import { Construct } from 'constructs';
3
+ import { BlobStore } from './constructs/blob-store.js';
4
+ import { Auth } from './constructs/auth.js';
5
+ import { Api } from './constructs/api.js';
6
+ import { AdminDashboard } from './constructs/admin-dashboard.js';
7
+ import { AdminApi } from './constructs/admin-api.js';
8
+ export interface ChaosKBStackProps extends StackProps {
9
+ readonly environment: string;
10
+ readonly signupsEnabled?: boolean;
11
+ readonly reservedConcurrency?: number;
12
+ readonly allowedOrigins?: string[];
13
+ }
14
+ export declare class ChaosKBStack extends Stack {
15
+ readonly blobStore: BlobStore;
16
+ readonly auth: Auth;
17
+ readonly api: Api;
18
+ readonly adminDashboard: AdminDashboard;
19
+ readonly adminApi: AdminApi;
20
+ constructor(scope: Construct, id: string, props: ChaosKBStackProps);
21
+ }
22
+ //# sourceMappingURL=chaoskb-stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chaoskb-stack.d.ts","sourceRoot":"","sources":["../../lib/chaoskb-stack.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,UAAU,EAAa,MAAM,aAAa,CAAC;AAC3D,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC5C,OAAO,EAAE,GAAG,EAAE,MAAM,qBAAqB,CAAC;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACjE,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,MAAM,WAAW,iBAAkB,SAAQ,UAAU;IACnD,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,CAAC,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;CACpC;AAED,qBAAa,YAAa,SAAQ,KAAK;IACrC,SAAgB,SAAS,EAAE,SAAS,CAAC;IACrC,SAAgB,IAAI,EAAE,IAAI,CAAC;IAC3B,SAAgB,GAAG,EAAE,GAAG,CAAC;IACzB,SAAgB,cAAc,EAAE,cAAc,CAAC;IAC/C,SAAgB,QAAQ,EAAE,QAAQ,CAAC;gBAEvB,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,iBAAiB;CAmDnE"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ChaosKBStack = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ const blob_store_js_1 = require("./constructs/blob-store.js");
6
+ const auth_js_1 = require("./constructs/auth.js");
7
+ const api_js_1 = require("./constructs/api.js");
8
+ const admin_dashboard_js_1 = require("./constructs/admin-dashboard.js");
9
+ const admin_api_js_1 = require("./constructs/admin-api.js");
10
+ class ChaosKBStack extends aws_cdk_lib_1.Stack {
11
+ blobStore;
12
+ auth;
13
+ api;
14
+ adminDashboard;
15
+ adminApi;
16
+ constructor(scope, id, props) {
17
+ super(scope, id, props);
18
+ this.blobStore = new blob_store_js_1.BlobStore(this, 'BlobStore', {
19
+ environment: props.environment,
20
+ });
21
+ this.auth = new auth_js_1.Auth(this, 'Auth', {
22
+ environment: props.environment,
23
+ signupsEnabled: props.signupsEnabled ?? true,
24
+ });
25
+ this.api = new api_js_1.Api(this, 'Api', {
26
+ table: this.blobStore.table,
27
+ environment: props.environment,
28
+ signupsEnabledParam: this.auth.signupsEnabledParam,
29
+ reservedConcurrency: props.reservedConcurrency,
30
+ });
31
+ this.adminDashboard = new admin_dashboard_js_1.AdminDashboard(this, 'AdminDashboard', {
32
+ environment: props.environment,
33
+ lambdaFunction: this.api.handler,
34
+ table: this.blobStore.table,
35
+ });
36
+ this.adminApi = new admin_api_js_1.AdminApi(this, 'AdminApi', {
37
+ table: this.blobStore.table,
38
+ environment: props.environment,
39
+ allowedOrigins: props.allowedOrigins ?? [],
40
+ });
41
+ new aws_cdk_lib_1.CfnOutput(this, 'FunctionUrl', {
42
+ value: this.api.functionUrl.url,
43
+ description: 'ChaosKB API Function URL',
44
+ });
45
+ new aws_cdk_lib_1.CfnOutput(this, 'TableName', {
46
+ value: this.blobStore.table.tableName,
47
+ description: 'ChaosKB DynamoDB table name',
48
+ });
49
+ new aws_cdk_lib_1.CfnOutput(this, 'AlarmTopicArn', {
50
+ value: this.adminDashboard.alarmTopic.topicArn,
51
+ description: 'ChaosKB alarm SNS topic ARN',
52
+ });
53
+ new aws_cdk_lib_1.CfnOutput(this, 'AdminApiUrl', {
54
+ value: this.adminApi.functionUrl.url,
55
+ description: 'ChaosKB Admin API Function URL',
56
+ });
57
+ }
58
+ }
59
+ exports.ChaosKBStack = ChaosKBStack;
60
+ //# sourceMappingURL=chaoskb-stack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chaoskb-stack.js","sourceRoot":"","sources":["../../lib/chaoskb-stack.ts"],"names":[],"mappings":";;;AAAA,6CAA2D;AAE3D,8DAAuD;AACvD,kDAA4C;AAC5C,gDAA0C;AAC1C,wEAAiE;AACjE,4DAAqD;AASrD,MAAa,YAAa,SAAQ,mBAAK;IACrB,SAAS,CAAY;IACrB,IAAI,CAAO;IACX,GAAG,CAAM;IACT,cAAc,CAAiB;IAC/B,QAAQ,CAAW;IAEnC,YAAY,KAAgB,EAAE,EAAU,EAAE,KAAwB;QAChE,KAAK,CAAC,KAAK,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;QAExB,IAAI,CAAC,SAAS,GAAG,IAAI,yBAAS,CAAC,IAAI,EAAE,WAAW,EAAE;YAChD,WAAW,EAAE,KAAK,CAAC,WAAW;SAC/B,CAAC,CAAC;QAEH,IAAI,CAAC,IAAI,GAAG,IAAI,cAAI,CAAC,IAAI,EAAE,MAAM,EAAE;YACjC,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,IAAI;SAC7C,CAAC,CAAC;QAEH,IAAI,CAAC,GAAG,GAAG,IAAI,YAAG,CAAC,IAAI,EAAE,KAAK,EAAE;YAC9B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YAC3B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,mBAAmB,EAAE,IAAI,CAAC,IAAI,CAAC,mBAAmB;YAClD,mBAAmB,EAAE,KAAK,CAAC,mBAAmB;SAC/C,CAAC,CAAC;QAEH,IAAI,CAAC,cAAc,GAAG,IAAI,mCAAc,CAAC,IAAI,EAAE,gBAAgB,EAAE;YAC/D,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,cAAc,EAAE,IAAI,CAAC,GAAG,CAAC,OAAO;YAChC,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;SAC5B,CAAC,CAAC;QAEH,IAAI,CAAC,QAAQ,GAAG,IAAI,uBAAQ,CAAC,IAAI,EAAE,UAAU,EAAE;YAC7C,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK;YAC3B,WAAW,EAAE,KAAK,CAAC,WAAW;YAC9B,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,EAAE;SAC3C,CAAC,CAAC;QAEH,IAAI,uBAAS,CAAC,IAAI,EAAE,aAAa,EAAE;YACjC,KAAK,EAAE,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,GAAG;YAC/B,WAAW,EAAE,0BAA0B;SACxC,CAAC,CAAC;QAEH,IAAI,uBAAS,CAAC,IAAI,EAAE,WAAW,EAAE;YAC/B,KAAK,EAAE,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,SAAS;YACrC,WAAW,EAAE,6BAA6B;SAC3C,CAAC,CAAC;QAEH,IAAI,uBAAS,CAAC,IAAI,EAAE,eAAe,EAAE;YACnC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,UAAU,CAAC,QAAQ;YAC9C,WAAW,EAAE,6BAA6B;SAC3C,CAAC,CAAC;QAEH,IAAI,uBAAS,CAAC,IAAI,EAAE,aAAa,EAAE;YACjC,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,WAAW,CAAC,GAAG;YACpC,WAAW,EAAE,gCAAgC;SAC9C,CAAC,CAAC;IACL,CAAC;CACF;AA1DD,oCA0DC"}
@@ -0,0 +1,16 @@
1
+ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
2
+ import { FunctionUrl } from 'aws-cdk-lib/aws-lambda';
3
+ import { TableV2 } from 'aws-cdk-lib/aws-dynamodb';
4
+ import { Construct } from 'constructs';
5
+ export interface AdminApiProps {
6
+ readonly environment: string;
7
+ readonly table: TableV2;
8
+ readonly allowedOrigins: string[];
9
+ readonly reservedConcurrency?: number;
10
+ }
11
+ export declare class AdminApi extends Construct {
12
+ readonly handler: NodejsFunction;
13
+ readonly functionUrl: FunctionUrl;
14
+ constructor(scope: Construct, id: string, props: AdminApiProps);
15
+ }
16
+ //# sourceMappingURL=admin-api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-api.d.ts","sourceRoot":"","sources":["../../../lib/constructs/admin-api.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,WAAW,EAAuD,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,cAAc,EAAE,MAAM,EAAE,CAAC;IAClC,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED,qBAAa,QAAS,SAAQ,SAAS;IACrC,SAAgB,OAAO,EAAE,cAAc,CAAC;IACxC,SAAgB,WAAW,EAAE,WAAW,CAAC;gBAE7B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa;CAwD/D"}
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.AdminApi = void 0;
37
+ const cdk = __importStar(require("aws-cdk-lib"));
38
+ const aws_lambda_nodejs_1 = require("aws-cdk-lib/aws-lambda-nodejs");
39
+ const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
40
+ const aws_iam_1 = require("aws-cdk-lib/aws-iam");
41
+ const constructs_1 = require("constructs");
42
+ const path = __importStar(require("path"));
43
+ class AdminApi extends constructs_1.Construct {
44
+ handler;
45
+ functionUrl;
46
+ constructor(scope, id, props) {
47
+ super(scope, id);
48
+ this.handler = new aws_lambda_nodejs_1.NodejsFunction(this, 'Handler', {
49
+ runtime: aws_lambda_1.Runtime.NODEJS_22_X,
50
+ architecture: aws_lambda_1.Architecture.ARM_64,
51
+ entry: path.join(__dirname, '../admin-handler/index.ts'),
52
+ handler: 'handler',
53
+ memorySize: 256,
54
+ timeout: cdk.Duration.seconds(30),
55
+ reservedConcurrentExecutions: props.reservedConcurrency ?? 5,
56
+ tracing: aws_lambda_1.Tracing.ACTIVE,
57
+ environment: {
58
+ TABLE_NAME: props.table.tableName,
59
+ ENVIRONMENT: props.environment,
60
+ ALLOWED_ORIGINS: props.allowedOrigins.join(','),
61
+ },
62
+ bundling: {
63
+ minify: true,
64
+ sourceMap: true,
65
+ target: 'node22',
66
+ format: undefined, // default cjs for Lambda
67
+ },
68
+ });
69
+ this.functionUrl = this.handler.addFunctionUrl({
70
+ authType: aws_lambda_1.FunctionUrlAuthType.NONE,
71
+ });
72
+ // DynamoDB read-only access
73
+ this.handler.addToRolePolicy(new aws_iam_1.PolicyStatement({
74
+ effect: aws_iam_1.Effect.ALLOW,
75
+ actions: ['dynamodb:Scan', 'dynamodb:Query', 'dynamodb:GetItem'],
76
+ resources: [props.table.tableArn],
77
+ }));
78
+ // CloudWatch read access
79
+ this.handler.addToRolePolicy(new aws_iam_1.PolicyStatement({
80
+ effect: aws_iam_1.Effect.ALLOW,
81
+ actions: ['cloudwatch:DescribeAlarms', 'cloudwatch:GetMetricData'],
82
+ resources: ['*'],
83
+ }));
84
+ // Cost Explorer read access
85
+ this.handler.addToRolePolicy(new aws_iam_1.PolicyStatement({
86
+ effect: aws_iam_1.Effect.ALLOW,
87
+ actions: ['ce:GetCostAndUsage', 'ce:GetCostForecast'],
88
+ resources: ['*'],
89
+ }));
90
+ }
91
+ }
92
+ exports.AdminApi = AdminApi;
93
+ //# sourceMappingURL=admin-api.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-api.js","sourceRoot":"","sources":["../../../lib/constructs/admin-api.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,iDAAmC;AACnC,qEAA+D;AAC/D,uDAA0G;AAE1G,iDAA8D;AAC9D,2CAAuC;AACvC,2CAA6B;AAS7B,MAAa,QAAS,SAAQ,sBAAS;IACrB,OAAO,CAAiB;IACxB,WAAW,CAAc;IAEzC,YAAY,KAAgB,EAAE,EAAU,EAAE,KAAoB;QAC5D,KAAK,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAEjB,IAAI,CAAC,OAAO,GAAG,IAAI,kCAAc,CAAC,IAAI,EAAE,SAAS,EAAE;YACjD,OAAO,EAAE,oBAAO,CAAC,WAAW;YAC5B,YAAY,EAAE,yBAAY,CAAC,MAAM;YACjC,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,EAAE,2BAA2B,CAAC;YACxD,OAAO,EAAE,SAAS;YAClB,UAAU,EAAE,GAAG;YACf,OAAO,EAAE,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;YACjC,4BAA4B,EAAE,KAAK,CAAC,mBAAmB,IAAI,CAAC;YAC5D,OAAO,EAAE,oBAAO,CAAC,MAAM;YACvB,WAAW,EAAE;gBACX,UAAU,EAAE,KAAK,CAAC,KAAK,CAAC,SAAS;gBACjC,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,eAAe,EAAE,KAAK,CAAC,cAAc,CAAC,IAAI,CAAC,GAAG,CAAC;aAChD;YACD,QAAQ,EAAE;gBACR,MAAM,EAAE,IAAI;gBACZ,SAAS,EAAE,IAAI;gBACf,MAAM,EAAE,QAAQ;gBAChB,MAAM,EAAE,SAAS,EAAE,yBAAyB;aAC7C;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;YAC7C,QAAQ,EAAE,gCAAmB,CAAC,IAAI;SACnC,CAAC,CAAC;QAEH,4BAA4B;QAC5B,IAAI,CAAC,OAAO,CAAC,eAAe,CAC1B,IAAI,yBAAe,CAAC;YAClB,MAAM,EAAE,gBAAM,CAAC,KAAK;YACpB,OAAO,EAAE,CAAC,eAAe,EAAE,gBAAgB,EAAE,kBAAkB,CAAC;YAChE,SAAS,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,QAAQ,CAAC;SAClC,CAAC,CACH,CAAC;QAEF,yBAAyB;QACzB,IAAI,CAAC,OAAO,CAAC,eAAe,CAC1B,IAAI,yBAAe,CAAC;YAClB,MAAM,EAAE,gBAAM,CAAC,KAAK;YACpB,OAAO,EAAE,CAAC,2BAA2B,EAAE,0BAA0B,CAAC;YAClE,SAAS,EAAE,CAAC,GAAG,CAAC;SACjB,CAAC,CACH,CAAC;QAEF,4BAA4B;QAC5B,IAAI,CAAC,OAAO,CAAC,eAAe,CAC1B,IAAI,yBAAe,CAAC;YAClB,MAAM,EAAE,gBAAM,CAAC,KAAK;YACpB,OAAO,EAAE,CAAC,oBAAoB,EAAE,oBAAoB,CAAC;YACrD,SAAS,EAAE,CAAC,GAAG,CAAC;SACjB,CAAC,CACH,CAAC;IACJ,CAAC;CACF;AA5DD,4BA4DC"}
@@ -0,0 +1,18 @@
1
+ import { Dashboard } from 'aws-cdk-lib/aws-cloudwatch';
2
+ import { Topic } from 'aws-cdk-lib/aws-sns';
3
+ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
4
+ import { TableV2 } from 'aws-cdk-lib/aws-dynamodb';
5
+ import { LogGroup } from 'aws-cdk-lib/aws-logs';
6
+ import { Construct } from 'constructs';
7
+ export interface AdminDashboardProps {
8
+ readonly environment: string;
9
+ readonly lambdaFunction: NodejsFunction;
10
+ readonly table: TableV2;
11
+ readonly lambdaLogGroup?: LogGroup;
12
+ }
13
+ export declare class AdminDashboard extends Construct {
14
+ readonly alarmTopic: Topic;
15
+ readonly dashboard: Dashboard;
16
+ constructor(scope: Construct, id: string, props: AdminDashboardProps);
17
+ }
18
+ //# sourceMappingURL=admin-dashboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-dashboard.d.ts","sourceRoot":"","sources":["../../../lib/constructs/admin-dashboard.ts"],"names":[],"mappings":"AACA,OAAO,EACL,SAAS,EAQV,MAAM,4BAA4B,CAAC;AAEpC,OAAO,EAAE,KAAK,EAAE,MAAM,qBAAqB,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,QAAQ,EAA+B,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEvC,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC;CACpC;AAED,qBAAa,cAAe,SAAQ,SAAS;IAC3C,SAAgB,UAAU,EAAE,KAAK,CAAC;IAClC,SAAgB,SAAS,EAAE,SAAS,CAAC;gBAEzB,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,mBAAmB;CAkLrE"}
@@ -0,0 +1,172 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AdminDashboard = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
5
+ const aws_cloudwatch_1 = require("aws-cdk-lib/aws-cloudwatch");
6
+ const aws_cloudwatch_actions_1 = require("aws-cdk-lib/aws-cloudwatch-actions");
7
+ const aws_sns_1 = require("aws-cdk-lib/aws-sns");
8
+ const aws_logs_1 = require("aws-cdk-lib/aws-logs");
9
+ const constructs_1 = require("constructs");
10
+ class AdminDashboard extends constructs_1.Construct {
11
+ alarmTopic;
12
+ dashboard;
13
+ constructor(scope, id, props) {
14
+ super(scope, id);
15
+ this.alarmTopic = new aws_sns_1.Topic(this, 'AlarmTopic', {
16
+ displayName: `ChaosKB ${props.environment} Alarms`,
17
+ });
18
+ const snsAction = new aws_cloudwatch_actions_1.SnsAction(this.alarmTopic);
19
+ // Lambda metrics
20
+ const invocations = props.lambdaFunction.metricInvocations({ period: aws_cdk_lib_1.Duration.minutes(5) });
21
+ const errors = props.lambdaFunction.metricErrors({ period: aws_cdk_lib_1.Duration.minutes(5) });
22
+ const durationP50 = props.lambdaFunction.metricDuration({ period: aws_cdk_lib_1.Duration.minutes(5), statistic: 'p50' });
23
+ const durationP95 = props.lambdaFunction.metricDuration({ period: aws_cdk_lib_1.Duration.minutes(5), statistic: 'p95' });
24
+ const durationP99 = props.lambdaFunction.metricDuration({ period: aws_cdk_lib_1.Duration.minutes(5), statistic: 'p99' });
25
+ // DynamoDB metrics
26
+ const readCapacity = new aws_cloudwatch_1.Metric({
27
+ namespace: 'AWS/DynamoDB',
28
+ metricName: 'ConsumedReadCapacityUnits',
29
+ dimensionsMap: { TableName: props.table.tableName },
30
+ period: aws_cdk_lib_1.Duration.minutes(5),
31
+ statistic: 'Sum',
32
+ });
33
+ const writeCapacity = new aws_cloudwatch_1.Metric({
34
+ namespace: 'AWS/DynamoDB',
35
+ metricName: 'ConsumedWriteCapacityUnits',
36
+ dimensionsMap: { TableName: props.table.tableName },
37
+ period: aws_cdk_lib_1.Duration.minutes(5),
38
+ statistic: 'Sum',
39
+ });
40
+ const throttledRequests = new aws_cloudwatch_1.Metric({
41
+ namespace: 'AWS/DynamoDB',
42
+ metricName: 'ThrottledRequests',
43
+ dimensionsMap: { TableName: props.table.tableName },
44
+ period: aws_cdk_lib_1.Duration.minutes(1),
45
+ statistic: 'Sum',
46
+ });
47
+ // Dashboard
48
+ this.dashboard = new aws_cloudwatch_1.Dashboard(this, 'Dashboard', {
49
+ // CDK auto-generates the dashboard name
50
+ widgets: [
51
+ [
52
+ new aws_cloudwatch_1.GraphWidget({
53
+ title: 'Lambda Invocations & Errors',
54
+ left: [invocations],
55
+ right: [errors],
56
+ width: 12,
57
+ }),
58
+ new aws_cloudwatch_1.GraphWidget({
59
+ title: 'Lambda Duration (p50/p95/p99)',
60
+ left: [durationP50, durationP95, durationP99],
61
+ width: 12,
62
+ }),
63
+ ],
64
+ [
65
+ new aws_cloudwatch_1.GraphWidget({
66
+ title: 'DynamoDB Read/Write Capacity',
67
+ left: [readCapacity],
68
+ right: [writeCapacity],
69
+ width: 12,
70
+ }),
71
+ new aws_cloudwatch_1.GraphWidget({
72
+ title: 'DynamoDB Throttled Requests',
73
+ left: [throttledRequests],
74
+ width: 12,
75
+ }),
76
+ ],
77
+ ],
78
+ });
79
+ // Alarms
80
+ // Lambda error rate > 1% over 5 min
81
+ const errorRate = new aws_cloudwatch_1.MathExpression({
82
+ expression: 'IF(invocations > 0, 100 * errors / invocations, 0)',
83
+ usingMetrics: {
84
+ errors,
85
+ invocations,
86
+ },
87
+ period: aws_cdk_lib_1.Duration.minutes(5),
88
+ });
89
+ const errorRateAlarm = new aws_cloudwatch_1.Alarm(this, 'LambdaErrorRateAlarm', {
90
+ alarmDescription: 'Lambda error rate exceeds 1%',
91
+ metric: errorRate,
92
+ threshold: 1,
93
+ evaluationPeriods: 1,
94
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
95
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
96
+ });
97
+ errorRateAlarm.addAlarmAction(snsAction);
98
+ // DynamoDB throttles > 0 over 1 min
99
+ const throttleAlarm = new aws_cloudwatch_1.Alarm(this, 'DynamoThrottleAlarm', {
100
+ alarmDescription: 'DynamoDB throttled requests detected',
101
+ metric: throttledRequests,
102
+ threshold: 0,
103
+ evaluationPeriods: 1,
104
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
105
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
106
+ });
107
+ throttleAlarm.addAlarmAction(snsAction);
108
+ // Lambda duration p99 > 5s
109
+ const durationAlarm = new aws_cloudwatch_1.Alarm(this, 'LambdaDurationAlarm', {
110
+ alarmDescription: 'Lambda p99 duration exceeds 5 seconds',
111
+ metric: props.lambdaFunction.metricDuration({
112
+ period: aws_cdk_lib_1.Duration.minutes(5),
113
+ statistic: 'p99',
114
+ unit: aws_cloudwatch_1.Unit.MILLISECONDS,
115
+ }),
116
+ threshold: 5000,
117
+ evaluationPeriods: 1,
118
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
119
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
120
+ });
121
+ durationAlarm.addAlarmAction(snsAction);
122
+ // Metric filters for sync operations and auth failures (if log group provided)
123
+ if (props.lambdaLogGroup) {
124
+ const _syncOpsFilter = new aws_logs_1.MetricFilter(this, 'SyncOpsMetricFilter', {
125
+ logGroup: props.lambdaLogGroup,
126
+ filterPattern: aws_logs_1.FilterPattern.literal('{ $.operation = "sync" }'),
127
+ metricNamespace: 'ChaosKB',
128
+ metricName: 'SyncOperations',
129
+ metricValue: '1',
130
+ });
131
+ const _authFailuresFilter = new aws_logs_1.MetricFilter(this, 'AuthFailuresMetricFilter', {
132
+ logGroup: props.lambdaLogGroup,
133
+ filterPattern: aws_logs_1.FilterPattern.literal('{ $.error = "auth_error" }'),
134
+ metricNamespace: 'ChaosKB',
135
+ metricName: 'AuthFailures',
136
+ metricValue: '1',
137
+ });
138
+ const syncOpsMetric = new aws_cloudwatch_1.Metric({
139
+ namespace: 'ChaosKB',
140
+ metricName: 'SyncOperations',
141
+ period: aws_cdk_lib_1.Duration.minutes(5),
142
+ statistic: 'Sum',
143
+ });
144
+ const authFailuresMetric = new aws_cloudwatch_1.Metric({
145
+ namespace: 'ChaosKB',
146
+ metricName: 'AuthFailures',
147
+ period: aws_cdk_lib_1.Duration.minutes(5),
148
+ statistic: 'Sum',
149
+ });
150
+ const authFailuresAlarm = new aws_cloudwatch_1.Alarm(this, 'AuthFailuresAlarm', {
151
+ alarmDescription: 'Auth failures exceed 10 in 5 minutes',
152
+ metric: authFailuresMetric,
153
+ threshold: 10,
154
+ evaluationPeriods: 1,
155
+ comparisonOperator: aws_cloudwatch_1.ComparisonOperator.GREATER_THAN_THRESHOLD,
156
+ treatMissingData: aws_cloudwatch_1.TreatMissingData.NOT_BREACHING,
157
+ });
158
+ authFailuresAlarm.addAlarmAction(snsAction);
159
+ this.dashboard.addWidgets(new aws_cloudwatch_1.GraphWidget({
160
+ title: 'Sync Operations',
161
+ left: [syncOpsMetric],
162
+ width: 12,
163
+ }), new aws_cloudwatch_1.GraphWidget({
164
+ title: 'Auth Failures',
165
+ left: [authFailuresMetric],
166
+ width: 12,
167
+ }));
168
+ }
169
+ }
170
+ }
171
+ exports.AdminDashboard = AdminDashboard;
172
+ //# sourceMappingURL=admin-dashboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"admin-dashboard.js","sourceRoot":"","sources":["../../../lib/constructs/admin-dashboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,17 @@
1
+ import { FunctionUrl } from 'aws-cdk-lib/aws-lambda';
2
+ import { NodejsFunction } from 'aws-cdk-lib/aws-lambda-nodejs';
3
+ import { TableV2 } from 'aws-cdk-lib/aws-dynamodb';
4
+ import { StringParameter } from 'aws-cdk-lib/aws-ssm';
5
+ import { Construct } from 'constructs';
6
+ export interface ApiProps {
7
+ readonly table: TableV2;
8
+ readonly environment: string;
9
+ readonly signupsEnabledParam: StringParameter;
10
+ readonly reservedConcurrency?: number;
11
+ }
12
+ export declare class Api extends Construct {
13
+ readonly functionUrl: FunctionUrl;
14
+ readonly handler: NodejsFunction;
15
+ constructor(scope: Construct, id: string, props: ApiProps);
16
+ }
17
+ //# sourceMappingURL=api.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api.d.ts","sourceRoot":"","sources":["../../../lib/constructs/api.ts"],"names":[],"mappings":"AACA,OAAO,EAAgB,WAAW,EAAyC,MAAM,wBAAwB,CAAC;AAC1G,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAEnD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAGvC,MAAM,WAAW,QAAQ;IACvB,QAAQ,CAAC,KAAK,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,mBAAmB,EAAE,eAAe,CAAC;IAC9C,QAAQ,CAAC,mBAAmB,CAAC,EAAE,MAAM,CAAC;CACvC;AAED,qBAAa,GAAI,SAAQ,SAAS;IAChC,SAAgB,WAAW,EAAE,WAAW,CAAC;IACzC,SAAgB,OAAO,EAAE,cAAc,CAAC;gBAE5B,KAAK,EAAE,SAAS,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,QAAQ;CAqC1D"}
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.Api = void 0;
37
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
38
+ const aws_lambda_1 = require("aws-cdk-lib/aws-lambda");
39
+ const aws_lambda_nodejs_1 = require("aws-cdk-lib/aws-lambda-nodejs");
40
+ const aws_sqs_1 = require("aws-cdk-lib/aws-sqs");
41
+ const constructs_1 = require("constructs");
42
+ const path = __importStar(require("path"));
43
+ class Api extends constructs_1.Construct {
44
+ functionUrl;
45
+ handler;
46
+ constructor(scope, id, props) {
47
+ super(scope, id);
48
+ const deadLetterQueue = new aws_sqs_1.Queue(this, 'DeadLetterQueue', {
49
+ retentionPeriod: aws_cdk_lib_1.Duration.days(14),
50
+ });
51
+ this.handler = new aws_lambda_nodejs_1.NodejsFunction(this, 'Handler', {
52
+ runtime: aws_lambda_1.Runtime.NODEJS_22_X,
53
+ architecture: aws_lambda_1.Architecture.ARM_64,
54
+ entry: path.join(__dirname, '..', 'handler', 'index.ts'),
55
+ handler: 'handler',
56
+ memorySize: 256,
57
+ timeout: aws_cdk_lib_1.Duration.seconds(30),
58
+ environment: {
59
+ TABLE_NAME: props.table.tableName,
60
+ ENVIRONMENT: props.environment,
61
+ SIGNUPS_ENABLED_PARAM: props.signupsEnabledParam.parameterName,
62
+ },
63
+ reservedConcurrentExecutions: props.reservedConcurrency ?? 10,
64
+ deadLetterQueue,
65
+ tracing: aws_lambda_1.Tracing.ACTIVE,
66
+ bundling: {
67
+ minify: true,
68
+ sourceMap: true,
69
+ target: 'node22',
70
+ format: undefined, // default cjs for Lambda
71
+ },
72
+ });
73
+ this.functionUrl = this.handler.addFunctionUrl({
74
+ authType: aws_lambda_1.FunctionUrlAuthType.NONE,
75
+ });
76
+ props.table.grantReadWriteData(this.handler);
77
+ props.signupsEnabledParam.grantRead(this.handler);
78
+ }
79
+ }
80
+ exports.Api = Api;
81
+ //# sourceMappingURL=api.js.map