@exabugs/dynamodb-client 1.5.3 → 1.6.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 (38) hide show
  1. package/dist/server/handler.cjs +3 -3
  2. package/dist/server/media/magicBytes.d.ts.map +1 -1
  3. package/dist/server/media/magicBytes.js +17 -14
  4. package/dist/server/media/magicBytes.js.map +1 -1
  5. package/dist/server/media/media-handler.d.ts.map +1 -1
  6. package/dist/server/media/media-handler.js +38 -4
  7. package/dist/server/media/media-handler.js.map +1 -1
  8. package/dist/server/media/metadata.d.ts +29 -0
  9. package/dist/server/media/metadata.d.ts.map +1 -0
  10. package/dist/server/media/metadata.js +57 -0
  11. package/dist/server/media/metadata.js.map +1 -0
  12. package/dist/server/media/presign.d.ts.map +1 -1
  13. package/dist/server/media/presign.js +18 -1
  14. package/dist/server/media/presign.js.map +1 -1
  15. package/dist/server/media/process-handler-dlq.d.ts +0 -13
  16. package/dist/server/media/process-handler-dlq.d.ts.map +1 -1
  17. package/dist/server/media/process-handler-dlq.js +49 -7
  18. package/dist/server/media/process-handler-dlq.js.map +1 -1
  19. package/dist/server/media/process-handler.d.ts.map +1 -1
  20. package/dist/server/media/process-handler.js +40 -25
  21. package/dist/server/media/process-handler.js.map +1 -1
  22. package/dist/server/media/types.d.ts +1 -1
  23. package/dist/server/media/types.d.ts.map +1 -1
  24. package/dist/server/media/upload-handler.d.ts +50 -0
  25. package/dist/server/media/upload-handler.d.ts.map +1 -0
  26. package/dist/server/media/upload-handler.js +115 -0
  27. package/dist/server/media/upload-handler.js.map +1 -0
  28. package/dist/server/media-handler.cjs +27 -3
  29. package/dist/server/media-handler.cjs.map +2 -2
  30. package/dist/server/media-process-handler-dlq.cjs +25496 -13553
  31. package/dist/server/media-process-handler-dlq.cjs.map +4 -4
  32. package/dist/server/media-process-handler.cjs +648 -536
  33. package/dist/server/media-process-handler.cjs.map +4 -4
  34. package/dist/sharp-layer.zip +0 -0
  35. package/package.json +5 -1
  36. package/terraform/media/README.md +4 -1
  37. package/terraform/media/main.tf +86 -13
  38. package/terraform/media/variables.tf +33 -0
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/media/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB;IACzB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,sCAAsC;IACtC,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/server/media/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAEnD;;;;;GAKG;AACH,MAAM,MAAM,WAAW,GAAG,WAAW,GAAG,QAAQ,CAAC;AAEjD;;;;;GAKG;AACH,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,EAAE,WAAW,CAAC;IACpB,qCAAqC;IACrC,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,iBAAiB,EAAE,MAAM,CAAC;IAC1B,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,yBAAyB;IACzB,UAAU,CAAC,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/C,0BAA0B;IAC1B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACxB;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,uEAAuE;IACvE,kBAAkB,EAAE,MAAM,CAAC;IAC3B,6BAA6B;IAC7B,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,oCAAoC;IACpC,aAAa,EAAE,MAAM,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,QAAQ,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;IACpB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,aAAa,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,mDAAmD;IACnD,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,gEAAgE;IAChE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,uCAAuC;IACvC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,GAAG,EAAE,MAAM,CAAC;IACZ,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;CACnB"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * presign発行・署名付きURL発行を扱うLambda Function URL用ハンドラー(標準実装)
3
+ *
4
+ * 「誰にpresign/署名URLを発行してよいか」の判断はこのライブラリの関心事ではない
5
+ * (詳細: docs/media-design.md)。このハンドラーは`authorize`フックを通じて
6
+ * 呼び出し側アプリの認証・認可ロジックを注入できるようにする。
7
+ *
8
+ * より高度な認可(例: リソース単位の権限チェック)が必要な場合、呼び出し側は
9
+ * このハンドラーを使わず、`./presign.js`・`./sign.js`を自前のルーティング・
10
+ * 認可レイヤーから直接呼び出すこともできる。
11
+ */
12
+ import type { APIGatewayProxyEventV2, APIGatewayProxyStructuredResultV2 } from 'aws-lambda';
13
+ import { S3Client } from '@aws-sdk/client-s3';
14
+ import { SSMClient } from '@aws-sdk/client-ssm';
15
+ /**
16
+ * リクエストを認可し、presign発行時にpolicyへ固定するメタデータを返す。
17
+ * 認可できない場合はnullを返す(401を返す)。
18
+ */
19
+ export type AuthorizeFn = (event: APIGatewayProxyEventV2) => Promise<Record<string, string> | null>;
20
+ export interface UploadHandlerConfig {
21
+ bucket: string;
22
+ imagePolicyParam: string;
23
+ signingPrivateKeyParam: string;
24
+ keyPairId: string;
25
+ baseUrl: string;
26
+ authorize: AuthorizeFn;
27
+ signExpiresInSeconds?: number;
28
+ s3Client?: S3Client;
29
+ ssmClient?: SSMClient;
30
+ }
31
+ /**
32
+ * 設定を注入してLambda Function URLハンドラーを生成する。
33
+ *
34
+ * @example
35
+ * ```ts
36
+ * export const handler = createUploadHandler({
37
+ * bucket: process.env.MEDIA_BUCKET!,
38
+ * imagePolicyParam: process.env.IMAGE_POLICY_PARAM!,
39
+ * signingPrivateKeyParam: process.env.SIGNING_PRIVATE_KEY_PARAM!,
40
+ * keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID!,
41
+ * baseUrl: process.env.MEDIA_BASE_URL!,
42
+ * authorize: async (event) => {
43
+ * const userId = await verifyMyAppJwt(event.headers.authorization);
44
+ * return userId ? { ownerId: userId } : null;
45
+ * },
46
+ * });
47
+ * ```
48
+ */
49
+ export declare function createUploadHandler(config: UploadHandlerConfig): (event: APIGatewayProxyEventV2) => Promise<APIGatewayProxyStructuredResultV2>;
50
+ //# sourceMappingURL=upload-handler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-handler.d.ts","sourceRoot":"","sources":["../../../src/server/media/upload-handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,sBAAsB,EAAE,iCAAiC,EAAE,MAAM,YAAY,CAAC;AAE5F,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAWhD;;;GAGG;AACH,MAAM,MAAM,WAAW,GAAG,CAAC,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAAC,CAAC;AAEpG,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAC;IACf,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,CAAC;IAC/B,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,WAAW,CAAC;IACvB,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,SAAS,CAAC,EAAE,SAAS,CAAC;CACvB;AA0FD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,mBAAmB,GAC1B,CAAC,KAAK,EAAE,sBAAsB,KAAK,OAAO,CAAC,iCAAiC,CAAC,CAiC/E"}
@@ -0,0 +1,115 @@
1
+ import { S3Client } from '@aws-sdk/client-s3';
2
+ import { SSMClient } from '@aws-sdk/client-ssm';
3
+ import { createLogger } from '../../shared/index.js';
4
+ import { generatePresignedUpload } from './presign.js';
5
+ import { signMediaUrl } from './sign.js';
6
+ import { getImagePolicy, getSigningPrivateKey } from './ssmParams.js';
7
+ const logger = createLogger({ service: 'media-upload-handler' });
8
+ const DEFAULT_SIGN_EXPIRES_IN_SECONDS = 3600;
9
+ function jsonResponse(statusCode, body) {
10
+ return {
11
+ statusCode,
12
+ headers: { 'Content-Type': 'application/json' },
13
+ body: JSON.stringify(body),
14
+ };
15
+ }
16
+ function parseBody(event) {
17
+ if (!event.body)
18
+ return {};
19
+ const raw = event.isBase64Encoded
20
+ ? Buffer.from(event.body, 'base64').toString('utf-8')
21
+ : event.body;
22
+ return JSON.parse(raw);
23
+ }
24
+ async function handlePresign(event, config, s3Client, ssmClient, metadata, requestId) {
25
+ const body = parseBody(event);
26
+ if (!body.contentType || !body.fileSize) {
27
+ return jsonResponse(400, { error: 'contentType and fileSize are required' });
28
+ }
29
+ const imagePolicy = await getImagePolicy(ssmClient, config.imagePolicyParam);
30
+ const result = await generatePresignedUpload({
31
+ s3Client,
32
+ bucket: config.bucket,
33
+ contentType: body.contentType,
34
+ originalFilename: body.originalFilename,
35
+ fileSize: body.fileSize,
36
+ allowedContentTypes: imagePolicy.allowedContentTypes,
37
+ maxUploadSize: imagePolicy.maxUploadSize,
38
+ metadata,
39
+ });
40
+ logger.info('Presign issued', { requestId, fileId: result.fileId });
41
+ return jsonResponse(200, result);
42
+ }
43
+ async function handleSign(event, config, ssmClient, requestId) {
44
+ const body = parseBody(event);
45
+ if (!Array.isArray(body.items) || body.items.length === 0) {
46
+ return jsonResponse(400, { error: 'items is required and must be non-empty' });
47
+ }
48
+ const privateKey = await getSigningPrivateKey(ssmClient, config.signingPrivateKeyParam);
49
+ const expiresInSeconds = config.signExpiresInSeconds ?? DEFAULT_SIGN_EXPIRES_IN_SECONDS;
50
+ const items = body.items.map((item) => {
51
+ if (!item.fileId) {
52
+ throw new Error('Each item must have fileId');
53
+ }
54
+ return signMediaUrl({
55
+ fileId: item.fileId,
56
+ width: item.width,
57
+ keyPairId: config.keyPairId,
58
+ privateKey,
59
+ baseUrl: config.baseUrl,
60
+ expiresInSeconds,
61
+ });
62
+ });
63
+ logger.info('Media URLs signed', { requestId, count: items.length });
64
+ return jsonResponse(200, { items });
65
+ }
66
+ /**
67
+ * 設定を注入してLambda Function URLハンドラーを生成する。
68
+ *
69
+ * @example
70
+ * ```ts
71
+ * export const handler = createUploadHandler({
72
+ * bucket: process.env.MEDIA_BUCKET!,
73
+ * imagePolicyParam: process.env.IMAGE_POLICY_PARAM!,
74
+ * signingPrivateKeyParam: process.env.SIGNING_PRIVATE_KEY_PARAM!,
75
+ * keyPairId: process.env.CLOUDFRONT_KEY_PAIR_ID!,
76
+ * baseUrl: process.env.MEDIA_BASE_URL!,
77
+ * authorize: async (event) => {
78
+ * const userId = await verifyMyAppJwt(event.headers.authorization);
79
+ * return userId ? { ownerId: userId } : null;
80
+ * },
81
+ * });
82
+ * ```
83
+ */
84
+ export function createUploadHandler(config) {
85
+ const s3Client = config.s3Client ?? new S3Client({ region: process.env.AWS_REGION });
86
+ const ssmClient = config.ssmClient ?? new SSMClient({ region: process.env.AWS_REGION });
87
+ return async (event) => {
88
+ const requestId = event.requestContext.requestId;
89
+ const method = event.requestContext.http.method;
90
+ const path = event.rawPath;
91
+ try {
92
+ const metadata = await config.authorize(event);
93
+ if (!metadata) {
94
+ return jsonResponse(401, { error: 'Unauthorized' });
95
+ }
96
+ if (method === 'POST' && path.endsWith('/presign')) {
97
+ return await handlePresign(event, config, s3Client, ssmClient, metadata, requestId);
98
+ }
99
+ if (method === 'POST' && path.endsWith('/sign')) {
100
+ return await handleSign(event, config, ssmClient, requestId);
101
+ }
102
+ return jsonResponse(404, { error: 'Not found' });
103
+ }
104
+ catch (error) {
105
+ logger.error('Upload handler error', {
106
+ requestId,
107
+ error: error instanceof Error ? error.message : String(error),
108
+ });
109
+ return jsonResponse(400, {
110
+ error: error instanceof Error ? error.message : 'Bad request',
111
+ });
112
+ }
113
+ };
114
+ }
115
+ //# sourceMappingURL=upload-handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"upload-handler.js","sourceRoot":"","sources":["../../../src/server/media/upload-handler.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- // @exabugs/dynamodb-client v1.5.3
2
- // Built: 2026-10-04T14:32:33.468Z
1
+ // @exabugs/dynamodb-client v1.6.0
2
+ // Built: 2026-10-04T23:59:48.659Z
3
3
  "use strict";
4
4
  var __create = Object.create;
5
5
  var __defProp = Object.defineProperty;
@@ -57323,6 +57323,16 @@ var ssmClient = new import_client_ssm2.SSMClient({ region: process.env.AWS_REGIO
57323
57323
  var MAX_INPUT_PIXELS = 1e8;
57324
57324
  var CACHE_CONTROL_IMMUTABLE = "public, max-age=31536000, immutable";
57325
57325
  var CACHE_CONTROL_NO_STORE = "no-store";
57326
+ var MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
57327
+ var QUALITY_FALLBACK_STEPS = [80, 60, 40];
57328
+ function payloadTooLargeResponse(message) {
57329
+ return {
57330
+ statusCode: 413,
57331
+ headers: { "Cache-Control": CACHE_CONTROL_NO_STORE, "Content-Type": "text/plain" },
57332
+ body: message
57333
+ };
57334
+ }
57335
+ __name(payloadTooLargeResponse, "payloadTooLargeResponse");
57326
57336
  function notFoundResponse(message) {
57327
57337
  return {
57328
57338
  statusCode: 404,
@@ -57422,7 +57432,13 @@ async function handler(event) {
57422
57432
  }
57423
57433
  let resized;
57424
57434
  try {
57425
- resized = await (0, import_sharp.default)(master, { limitInputPixels: MAX_INPUT_PIXELS }).resize(width, width, { fit: "inside", withoutEnlargement: true }).jpeg().toBuffer();
57435
+ for (const quality of QUALITY_FALLBACK_STEPS) {
57436
+ const buffer = await (0, import_sharp.default)(master, { limitInputPixels: MAX_INPUT_PIXELS }).resize(width, width, { fit: "inside", withoutEnlargement: true }).jpeg({ quality }).toBuffer();
57437
+ if (buffer.length <= MAX_RESPONSE_BYTES) {
57438
+ resized = buffer;
57439
+ break;
57440
+ }
57441
+ }
57426
57442
  } catch (error2) {
57427
57443
  logger2.warn("Resize not applicable (master is not a processable raster image)", {
57428
57444
  requestId,
@@ -57431,6 +57447,14 @@ async function handler(event) {
57431
57447
  });
57432
57448
  return badRequestResponse("Resize is not applicable to this file");
57433
57449
  }
57450
+ if (!resized) {
57451
+ logger2.warn("Resized output exceeds response size limit even at minimum quality", {
57452
+ requestId,
57453
+ fileId,
57454
+ width
57455
+ });
57456
+ return payloadTooLargeResponse("Resized output is too large to return");
57457
+ }
57434
57458
  await s3Client.send(
57435
57459
  new import_client_s3.PutObjectCommand({
57436
57460
  Bucket: bucket,