@exabugs/dynamodb-client 1.14.0 → 1.15.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/CHANGELOG.md +34 -0
- package/dist/server/handler.cjs +3 -3
- package/dist/server/media/process-handler-dlq.d.ts.map +1 -1
- package/dist/server/media/process-handler-dlq.js +8 -19
- package/dist/server/media/process-handler-dlq.js.map +1 -1
- package/dist/server/media/process-handler.d.ts.map +1 -1
- package/dist/server/media/process-handler.js +5 -18
- package/dist/server/media/process-handler.js.map +1 -1
- package/dist/server/media/recordsClient.d.ts +13 -0
- package/dist/server/media/recordsClient.d.ts.map +1 -0
- package/dist/server/media/recordsClient.js +55 -0
- package/dist/server/media/recordsClient.js.map +1 -0
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +1143 -3613
- package/dist/server/media-process-handler-dlq.cjs.map +4 -4
- package/dist/server/media-process-handler.cjs +1194 -3679
- package/dist/server/media-process-handler.cjs.map +4 -4
- package/docs/DYNAMODB_ACCESS.md +17 -9
- package/package.json +1 -1
- package/terraform/media/README.md +5 -2
- package/terraform/media/main.tf +35 -27
- package/terraform/media/variables.tf +36 -9
package/docs/DYNAMODB_ACCESS.md
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
# DynamoDB へのアクセスのガードレール
|
|
2
2
|
|
|
3
|
-
##
|
|
3
|
+
## アーキテクチャ(全文。他の文書は、これを要約して参照する)
|
|
4
4
|
|
|
5
|
-
dynamodb-client
|
|
6
|
-
|
|
5
|
+
**DynamoDB の操作は、dynamodb-client(Records)の 1 か所で行う。**
|
|
6
|
+
|
|
7
|
+
- クライアント(BFF、管理画面、media の Lambda、運用スクリプト)は、**ビジネスのリクエスト**を受け、dynamodb-client の API(Records)への操作に展開する。通常の操作の API は `DynamoClient`。**media のアップロードの処理も、ビジネスのリクエストの一つ**であり、BFF など他のクライアントと区別しない。
|
|
8
|
+
- DynamoDB の操作の制御(認可、監査、シャドーの維持、観測、容量)は、Records の **1 か所**で行う。全クライアントが Records に依存する、同じ構造である。
|
|
9
|
+
- **例外を作らない。** 例外(Lambda が本体テーブルを直接読み書きするなど)は、監査ログ(ADR 0004)のような横断機能の穴になる。直接アクセスは、ガードレール(ESLint と IAM の検査。許可リスト)で禁止する。
|
|
10
|
+
- **対象外**(別テーブル `notices` など)は、構造が違う内部用のテーブルで、ビジネスのデータではない。「例外」ではない。
|
|
11
|
+
|
|
12
|
+
### 書き方の規則
|
|
13
|
+
|
|
14
|
+
設計書・ADR では、Records への依存や HTTP 1 回分の遅延を、「限界」「問題」として書かない。**アーキテクチャの帰結(設計上の性質)**として書く。Records にリソースごとの認可がない、という既知の制約は、この構造とは独立に、別に書く([AUTHORIZATION.md](AUTHORIZATION.md))。
|
|
15
|
+
|
|
16
|
+
関連: [ADR 0005](adr/0005-writes-through-records.md)(書き込みを Records に通す。「0. アーキテクチャ(前提)」)。
|
|
7
17
|
|
|
8
18
|
## 検査
|
|
9
19
|
|
|
@@ -128,7 +138,7 @@ ESLint は、エディタでも違反をその場で示す。CLI は ESLint の
|
|
|
128
138
|
|
|
129
139
|
## 許可リストの種別と約束
|
|
130
140
|
|
|
131
|
-
- **恒久(permanent)**: 構造上必要な例外。このライブラリでは、Records の本体、Records が使う共有部品、別テーブル `notices`(`src/notify/store.ts
|
|
141
|
+
- **恒久(permanent)**: 構造上必要な例外。このライブラリでは、Records の本体、Records が使う共有部品、別テーブル `notices`(`src/notify/store.ts`)。`notices` は例外ではなく原則の**対象外**(構造が違う別テーブルで、通知の重複抑制・流量制限の内部用。ビジネスのデータではない)。検査が IAM をテーブルごとに区別できないため permanent として載せる(ADR 0005)
|
|
132
142
|
- **負債(debt)**: 期限つき。消す作業が決まっている
|
|
133
143
|
|
|
134
144
|
運用の約束:
|
|
@@ -145,8 +155,6 @@ ESLint は、エディタでも違反をその場で示す。CLI は ESLint の
|
|
|
145
155
|
|
|
146
156
|
## 現状の負債
|
|
147
157
|
|
|
148
|
-
| 場所
|
|
149
|
-
|
|
|
150
|
-
| `src/
|
|
151
|
-
| `terraform/media/main.tf` の `process_handler_dynamodb`、`process_handler_dlq_dynamodb` | media の Lambda に本体テーブルの権限 | 同上 |
|
|
152
|
-
| `src/dynamodb.ts` | 本体と重複した未使用のクライアント生成(テストからのみ参照) | `src/dynamodb.ts` と `__tests__/dynamodb.test.ts` の削除 |
|
|
158
|
+
| 場所 | 内容 | 消す作業 |
|
|
159
|
+
| ----------------- | ------------------------------------------------------------ | -------------------------------------------------------- |
|
|
160
|
+
| `src/dynamodb.ts` | 本体と重複した未使用のクライアント生成(テストからのみ参照) | `src/dynamodb.ts` と `__tests__/dynamodb.test.ts` の削除 |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exabugs/dynamodb-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.15.0",
|
|
4
4
|
"description": "DynamoDB Single-Table Client SDK with MongoDB-like API, Shadow Records, and Lambda implementation for serverless applications",
|
|
5
5
|
"author": "exabugs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -38,8 +38,11 @@ module "media" {
|
|
|
38
38
|
environment = "dev"
|
|
39
39
|
region = "ap-northeast-1"
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
table_arn
|
|
41
|
+
# Records の IAM 版 Function URL とエイリアスの ARN(本体の出力)。media の Lambda は
|
|
42
|
+
# DynamoDB に直接触れず、Records を呼ぶ(ADR 0005)。table_name / table_arn / shadow_* は
|
|
43
|
+
# deprecated(1.16.0 で削除)で、渡しても無視される
|
|
44
|
+
records_iam_function_url = module.records.iam_function_url
|
|
45
|
+
records_iam_alias_arn = module.records.iam_alias_arn
|
|
43
46
|
|
|
44
47
|
signing_public_key_param = "/my-project/dev/media/signing-public-key"
|
|
45
48
|
image_policy_param = "/my-project/dev/media/image-policy"
|
package/terraform/media/main.tf
CHANGED
|
@@ -104,6 +104,11 @@ resource "aws_sqs_queue" "process_handler_dlq" {
|
|
|
104
104
|
name = "${local.name_prefix}-process-dlq"
|
|
105
105
|
message_retention_seconds = 1209600 # 14日(SQS上限)
|
|
106
106
|
|
|
107
|
+
# Lambda(process-handler-dlq)のイベントソースになるキューの可視性タイムアウトは、関数の
|
|
108
|
+
# timeout 以上が必須(満たさないとマッピングの作成・更新が拒否される)。AWS の推奨は 6 倍
|
|
109
|
+
# (AWS Lambda 開発者ガイド「Amazon SQS でのイベントソースマッピング」、ADR 0005)
|
|
110
|
+
visibility_timeout_seconds = var.process_handler_dlq_timeout * 6
|
|
111
|
+
|
|
107
112
|
tags = {
|
|
108
113
|
Name = "${local.name_prefix}-process-dlq"
|
|
109
114
|
}
|
|
@@ -120,6 +125,15 @@ resource "aws_lambda_layer_version" "sharp" {
|
|
|
120
125
|
compatible_runtimes = ["nodejs22.x"]
|
|
121
126
|
# sharp-layer.zipはlinux/arm64向けにビルドされる(build-sharp-layer.sh参照)
|
|
122
127
|
compatible_architectures = ["arm64"]
|
|
128
|
+
|
|
129
|
+
# filenameのパスは、利用側(pnpm)ではライブラリの版を含む
|
|
130
|
+
# (node_modules/.pnpm/@exabugs+dynamodb-client@<版>_…)ため、版を上げるだけで
|
|
131
|
+
# 変わる。filenameはForceNewなので、zipの中身が同じでもレイヤーが置き換わり、
|
|
132
|
+
# 連鎖して無関係なリソースも置き換わる。中身の変更はsource_code_hashで検知する
|
|
133
|
+
# ので、filenameの変更は無視し、中身が変わったときだけ置き換える
|
|
134
|
+
lifecycle {
|
|
135
|
+
ignore_changes = [filename]
|
|
136
|
+
}
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
# ============================================================
|
|
@@ -171,19 +185,19 @@ resource "aws_iam_role_policy" "process_handler_s3" {
|
|
|
171
185
|
})
|
|
172
186
|
}
|
|
173
187
|
|
|
174
|
-
resource "aws_iam_role_policy" "
|
|
175
|
-
name = "
|
|
188
|
+
resource "aws_iam_role_policy" "process_handler_records" {
|
|
189
|
+
name = "records-invoke"
|
|
176
190
|
role = aws_iam_role.process_handler.id
|
|
177
191
|
|
|
192
|
+
# DynamoDB には直接触れず、Records の IAM 版 Function URL を呼ぶ(ADR 0005)
|
|
178
193
|
policy = jsonencode({
|
|
179
194
|
Version = "2012-10-17"
|
|
180
195
|
Statement = [
|
|
181
196
|
{
|
|
182
197
|
Effect = "Allow"
|
|
183
|
-
#
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
Resource = [var.table_arn, "${var.table_arn}/index/*"]
|
|
198
|
+
# 2025 年 10 月以降の新しい Function URL(AWS_IAM)は両方の権限が必要(AWS 公式ドキュメント urls-auth、ADR 0005)
|
|
199
|
+
Action = ["lambda:InvokeFunction", "lambda:InvokeFunctionUrl"]
|
|
200
|
+
Resource = var.records_iam_alias_arn
|
|
187
201
|
}
|
|
188
202
|
]
|
|
189
203
|
})
|
|
@@ -256,20 +270,16 @@ resource "aws_lambda_function" "process_handler" {
|
|
|
256
270
|
|
|
257
271
|
environment {
|
|
258
272
|
variables = {
|
|
259
|
-
REGION
|
|
260
|
-
|
|
261
|
-
IMAGE_POLICY_PARAM
|
|
262
|
-
SHADOW_CREATED_AT_FIELD = var.shadow_created_at_field
|
|
263
|
-
SHADOW_UPDATED_AT_FIELD = var.shadow_updated_at_field
|
|
264
|
-
SHADOW_STRING_MAX_BYTES = var.shadow_string_max_bytes
|
|
265
|
-
SHADOW_NUMBER_PADDING = var.shadow_number_padding
|
|
273
|
+
REGION = var.region
|
|
274
|
+
RECORDS_IAM_URL = var.records_iam_function_url
|
|
275
|
+
IMAGE_POLICY_PARAM = var.image_policy_param
|
|
266
276
|
}
|
|
267
277
|
}
|
|
268
278
|
|
|
269
279
|
depends_on = [
|
|
270
280
|
aws_cloudwatch_log_group.process_handler,
|
|
271
281
|
aws_iam_role_policy.process_handler_s3,
|
|
272
|
-
aws_iam_role_policy.
|
|
282
|
+
aws_iam_role_policy.process_handler_records,
|
|
273
283
|
aws_iam_role_policy.process_handler_ssm,
|
|
274
284
|
]
|
|
275
285
|
|
|
@@ -379,17 +389,19 @@ resource "aws_iam_role_policy" "process_handler_dlq_s3" {
|
|
|
379
389
|
})
|
|
380
390
|
}
|
|
381
391
|
|
|
382
|
-
resource "aws_iam_role_policy" "
|
|
383
|
-
name = "
|
|
392
|
+
resource "aws_iam_role_policy" "process_handler_dlq_records" {
|
|
393
|
+
name = "records-invoke"
|
|
384
394
|
role = aws_iam_role.process_handler_dlq.id
|
|
385
395
|
|
|
396
|
+
# DynamoDB には直接触れず、Records の IAM 版 Function URL を呼ぶ(ADR 0005)
|
|
386
397
|
policy = jsonencode({
|
|
387
398
|
Version = "2012-10-17"
|
|
388
399
|
Statement = [
|
|
389
400
|
{
|
|
390
|
-
Effect
|
|
391
|
-
|
|
392
|
-
|
|
401
|
+
Effect = "Allow"
|
|
402
|
+
# 2025 年 10 月以降の新しい Function URL(AWS_IAM)は両方の権限が必要(AWS 公式ドキュメント urls-auth、ADR 0005)
|
|
403
|
+
Action = ["lambda:InvokeFunction", "lambda:InvokeFunctionUrl"]
|
|
404
|
+
Resource = var.records_iam_alias_arn
|
|
393
405
|
}
|
|
394
406
|
]
|
|
395
407
|
})
|
|
@@ -422,24 +434,20 @@ resource "aws_lambda_function" "process_handler_dlq" {
|
|
|
422
434
|
architectures = ["arm64"]
|
|
423
435
|
handler = "media-process-handler-dlq.handler"
|
|
424
436
|
|
|
425
|
-
timeout =
|
|
437
|
+
timeout = var.process_handler_dlq_timeout
|
|
426
438
|
memory_size = 256
|
|
427
439
|
|
|
428
440
|
environment {
|
|
429
441
|
variables = {
|
|
430
|
-
REGION
|
|
431
|
-
|
|
432
|
-
SHADOW_CREATED_AT_FIELD = var.shadow_created_at_field
|
|
433
|
-
SHADOW_UPDATED_AT_FIELD = var.shadow_updated_at_field
|
|
434
|
-
SHADOW_STRING_MAX_BYTES = var.shadow_string_max_bytes
|
|
435
|
-
SHADOW_NUMBER_PADDING = var.shadow_number_padding
|
|
442
|
+
REGION = var.region
|
|
443
|
+
RECORDS_IAM_URL = var.records_iam_function_url
|
|
436
444
|
}
|
|
437
445
|
}
|
|
438
446
|
|
|
439
447
|
depends_on = [
|
|
440
448
|
aws_cloudwatch_log_group.process_handler_dlq,
|
|
441
449
|
aws_iam_role_policy.process_handler_dlq_sqs,
|
|
442
|
-
aws_iam_role_policy.
|
|
450
|
+
aws_iam_role_policy.process_handler_dlq_records,
|
|
443
451
|
aws_iam_role_policy.process_handler_dlq_s3,
|
|
444
452
|
]
|
|
445
453
|
|
|
@@ -15,14 +15,37 @@ variable "region" {
|
|
|
15
15
|
type = string
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
variable "records_iam_function_url" {
|
|
19
|
+
description = "Records の IAM 認証版の Function URL(本体の出力 iam_function_url。認証なし版の URL ではない)。media の Lambda はこれを SigV4 署名で呼び、DynamoDB には直接触れない(ADR 0005)"
|
|
20
|
+
type = string
|
|
21
|
+
|
|
22
|
+
# IAM 版と認証なし版は URL では区別できないので、形だけ検査する
|
|
23
|
+
validation {
|
|
24
|
+
condition = can(regex("^https://[a-z0-9]+\\.lambda-url\\.[a-z0-9-]+\\.on\\.aws/$", var.records_iam_function_url))
|
|
25
|
+
error_message = "Records の IAM 認証版の Function URL(dynamodb_client_api_iam_function_url)を渡すこと。形式: https://<id>.lambda-url.<region>.on.aws/(末尾は /)。"
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
variable "records_iam_alias_arn" {
|
|
30
|
+
description = "Records の IAM 版エイリアスの ARN(本体の出力 iam_alias_arn)。lambda:InvokeFunctionUrl と lambda:InvokeFunction の許可の対象(ADR 0005)"
|
|
31
|
+
type = string
|
|
32
|
+
|
|
33
|
+
validation {
|
|
34
|
+
condition = can(regex("^arn:aws[a-z-]*:lambda:[a-z0-9-]+:[0-9]{12}:function:[^:]+:[^:]+$", var.records_iam_alias_arn))
|
|
35
|
+
error_message = "Records の IAM 版エイリアスの ARN(dynamodb_client_api_iam_alias_arn。…:function:<名前>:<エイリアス> の修飾つき)を渡すこと。"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
18
39
|
variable "table_name" {
|
|
19
|
-
description = "
|
|
40
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
20
41
|
type = string
|
|
42
|
+
default = ""
|
|
21
43
|
}
|
|
22
44
|
|
|
23
45
|
variable "table_arn" {
|
|
24
|
-
description = "
|
|
46
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
25
47
|
type = string
|
|
48
|
+
default = ""
|
|
26
49
|
}
|
|
27
50
|
|
|
28
51
|
variable "image_cache_ttl_days" {
|
|
@@ -37,29 +60,27 @@ variable "allowed_upload_origins" {
|
|
|
37
60
|
default = []
|
|
38
61
|
}
|
|
39
62
|
|
|
40
|
-
#
|
|
41
|
-
# 形式は呼び出し側の既存Lambda(records等)の設定と一致させる必要がある
|
|
42
|
-
# (詳細: docs/media-design.md「環境変数の一致」)。既定値は本体(terraform/variables.tf)と揃える。
|
|
63
|
+
# 以下の shadow_* は deprecated(1.16.0 で削除。ADR 0005)。Records 経由になったため無視される。
|
|
43
64
|
variable "shadow_created_at_field" {
|
|
44
|
-
description = "
|
|
65
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
45
66
|
type = string
|
|
46
67
|
default = "createdAt"
|
|
47
68
|
}
|
|
48
69
|
|
|
49
70
|
variable "shadow_updated_at_field" {
|
|
50
|
-
description = "
|
|
71
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
51
72
|
type = string
|
|
52
73
|
default = "updatedAt"
|
|
53
74
|
}
|
|
54
75
|
|
|
55
76
|
variable "shadow_string_max_bytes" {
|
|
56
|
-
description = "
|
|
77
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
57
78
|
type = number
|
|
58
79
|
default = 100
|
|
59
80
|
}
|
|
60
81
|
|
|
61
82
|
variable "shadow_number_padding" {
|
|
62
|
-
description = "
|
|
83
|
+
description = "deprecated。1.16.0 で削除(ADR 0005)。media の Lambda は DynamoDB に直接触れなくなったため無視される。"
|
|
63
84
|
type = number
|
|
64
85
|
default = 15
|
|
65
86
|
}
|
|
@@ -115,3 +136,9 @@ variable "route53_zone_id" {
|
|
|
115
136
|
type = string
|
|
116
137
|
default = null
|
|
117
138
|
}
|
|
139
|
+
|
|
140
|
+
variable "process_handler_dlq_timeout" {
|
|
141
|
+
description = "process-handler-dlq Lambdaのタイムアウト(秒)。Records の読み取り・書き込みのクライアント timeout(各 15 秒)と HeadObject が収まる値にする"
|
|
142
|
+
type = number
|
|
143
|
+
default = 60
|
|
144
|
+
}
|