@exabugs/dynamodb-client 1.22.0 → 1.23.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 +14 -0
- package/README.md +1 -0
- package/bin/dynamodb-admins.js +6 -0
- package/dist/admins/add.d.ts +60 -0
- package/dist/admins/add.d.ts.map +1 -0
- package/dist/admins/add.js +37 -0
- package/dist/admins/add.js.map +1 -0
- package/dist/admins/index.d.ts +12 -0
- package/dist/admins/index.d.ts.map +1 -0
- package/dist/admins/index.js +10 -0
- package/dist/admins/index.js.map +1 -0
- package/dist/admins/record.d.ts +44 -0
- package/dist/admins/record.d.ts.map +1 -0
- package/dist/admins/record.js +44 -0
- package/dist/admins/record.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminDeleteButton.d.ts +12 -0
- package/dist/integrations/react-admin/components/Admins/AdminDeleteButton.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminDeleteButton.js +32 -0
- package/dist/integrations/react-admin/components/Admins/AdminDeleteButton.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsCreate.d.ts +12 -0
- package/dist/integrations/react-admin/components/Admins/AdminsCreate.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsCreate.js +55 -0
- package/dist/integrations/react-admin/components/Admins/AdminsCreate.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsList.d.ts +19 -0
- package/dist/integrations/react-admin/components/Admins/AdminsList.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsList.js +14 -0
- package/dist/integrations/react-admin/components/Admins/AdminsList.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsShow.d.ts +4 -0
- package/dist/integrations/react-admin/components/Admins/AdminsShow.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/AdminsShow.js +11 -0
- package/dist/integrations/react-admin/components/Admins/AdminsShow.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/adminsResource.d.ts +21 -0
- package/dist/integrations/react-admin/components/Admins/adminsResource.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/adminsResource.js +21 -0
- package/dist/integrations/react-admin/components/Admins/adminsResource.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/adminsView.d.ts +30 -0
- package/dist/integrations/react-admin/components/Admins/adminsView.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/adminsView.js +38 -0
- package/dist/integrations/react-admin/components/Admins/adminsView.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/index.d.ts +13 -0
- package/dist/integrations/react-admin/components/Admins/index.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/index.js +8 -0
- package/dist/integrations/react-admin/components/Admins/index.js.map +1 -0
- package/dist/integrations/react-admin/components/Admins/useAdminsMessages.d.ts +7 -0
- package/dist/integrations/react-admin/components/Admins/useAdminsMessages.d.ts.map +1 -0
- package/dist/integrations/react-admin/components/Admins/useAdminsMessages.js +20 -0
- package/dist/integrations/react-admin/components/Admins/useAdminsMessages.js.map +1 -0
- package/dist/integrations/react-admin/components/index.d.ts +2 -1
- package/dist/integrations/react-admin/components/index.d.ts.map +1 -1
- package/dist/integrations/react-admin/components/index.js +2 -1
- package/dist/integrations/react-admin/components/index.js.map +1 -1
- package/dist/integrations/react-admin/i18n/createI18nProvider.d.ts +27 -0
- package/dist/integrations/react-admin/i18n/createI18nProvider.d.ts.map +1 -0
- package/dist/integrations/react-admin/i18n/createI18nProvider.js +42 -0
- package/dist/integrations/react-admin/i18n/createI18nProvider.js.map +1 -0
- package/dist/integrations/react-admin/i18n/index.d.ts +11 -0
- package/dist/integrations/react-admin/i18n/index.d.ts.map +1 -0
- package/dist/integrations/react-admin/i18n/index.js +9 -0
- package/dist/integrations/react-admin/i18n/index.js.map +1 -0
- package/dist/integrations/react-admin/i18n/merge.d.ts +15 -0
- package/dist/integrations/react-admin/i18n/merge.d.ts.map +1 -0
- package/dist/integrations/react-admin/i18n/merge.js +31 -0
- package/dist/integrations/react-admin/i18n/merge.js.map +1 -0
- package/dist/integrations/react-admin/i18n/raJapaneseMessages.d.ts +194 -0
- package/dist/integrations/react-admin/i18n/raJapaneseMessages.d.ts.map +1 -0
- package/dist/integrations/react-admin/i18n/raJapaneseMessages.js +202 -0
- package/dist/integrations/react-admin/i18n/raJapaneseMessages.js.map +1 -0
- package/dist/integrations/react-admin/i18n/resources.d.ts +55 -0
- package/dist/integrations/react-admin/i18n/resources.d.ts.map +1 -0
- package/dist/integrations/react-admin/i18n/resources.js +49 -0
- package/dist/integrations/react-admin/i18n/resources.js.map +1 -0
- package/dist/integrations/react-admin/index.d.ts +1 -0
- package/dist/integrations/react-admin/index.d.ts.map +1 -1
- package/dist/integrations/react-admin/index.js +2 -0
- package/dist/integrations/react-admin/index.js.map +1 -1
- package/dist/notify/admins.d.ts.map +1 -1
- package/dist/notify/admins.js +3 -3
- package/dist/notify/admins.js.map +1 -1
- package/dist/server/handler.cjs +3 -3
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +2 -2
- package/dist/server/media-process-handler.cjs +2 -2
- package/dist/tools/dynamodb-admins/cli.d.ts +39 -0
- package/dist/tools/dynamodb-admins/cli.d.ts.map +1 -0
- package/dist/tools/dynamodb-admins/cli.js +174 -0
- package/dist/tools/dynamodb-admins/cli.js.map +1 -0
- package/dist/tools/dynamodb-admins/index.d.ts +6 -0
- package/dist/tools/dynamodb-admins/index.d.ts.map +1 -0
- package/dist/tools/dynamodb-admins/index.js +5 -0
- package/dist/tools/dynamodb-admins/index.js.map +1 -0
- package/dist/tools/dynamodb-admins/run.d.ts +2 -0
- package/dist/tools/dynamodb-admins/run.d.ts.map +1 -0
- package/dist/tools/dynamodb-admins/run.js +11 -0
- package/dist/tools/dynamodb-admins/run.js.map +1 -0
- package/docs/ADMINS.md +57 -0
- package/package.json +23 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dynamodb-admins add <email> --app <app> --env <env> --name <name>`: 管理者を admins に登録する(ADR 0007 §7)。
|
|
3
|
+
* 最初の 1 人(管理画面にまだ誰もログインできないとき)の登録に使う。2 人目以降は管理画面の「管理者」から。
|
|
4
|
+
*
|
|
5
|
+
* 経路: Records の IAM 版 Function URL を、利用者の AWS の認証情報(SigV4。`AWS_PROFILE` など)で呼ぶ。
|
|
6
|
+
* URL は SSM の `/<app>/<env>/infra/dynamodb-client-api-iam-url`(ライブラリの parameter-store が作る)から読む。
|
|
7
|
+
* 作成の前に `id` で存在を確かめ、既にあれば何も書かずに終了コード 3 で終わる(上書きしない)。
|
|
8
|
+
* 誤操作の防止: `--env prd` で書き込むときは `--confirm prd` が要る(astro の `make … CONFIRM=prd` と同じ流儀。
|
|
9
|
+
* `--dry-run` は書き込まないので要らない)。無ければ何も読まず・書かずに終了コード 2。
|
|
10
|
+
*/
|
|
11
|
+
import { GetParameterCommand, SSMClient } from '@aws-sdk/client-ssm';
|
|
12
|
+
import { addAdmin, adminWriterOf } from '../../admins/add.js';
|
|
13
|
+
import { withCollection } from '../../client/recordsClient.js';
|
|
14
|
+
export const EXIT_OK = 0;
|
|
15
|
+
export const EXIT_ERROR = 1;
|
|
16
|
+
export const EXIT_USAGE = 2;
|
|
17
|
+
/** 既に登録されている(何も書いていない) */
|
|
18
|
+
export const EXIT_EXISTS = 3;
|
|
19
|
+
export const USAGE = `使い方:
|
|
20
|
+
dynamodb-admins add <email> --app <app> --env <env> --name <name> [オプション]
|
|
21
|
+
→ admins に管理者を登録する(id と email は小文字のメール)。既にあれば何も書かない
|
|
22
|
+
|
|
23
|
+
<email> 管理者のメールアドレス(前後の空白を除き、小文字にする)
|
|
24
|
+
--app <app> アプリ名(SSM のパラメータ名に使う)
|
|
25
|
+
--env <env> 環境(dev / prd など)
|
|
26
|
+
--name <name> 表示名(必須)
|
|
27
|
+
オプション:
|
|
28
|
+
--region <region> 既定: AWS_REGION、無ければ ap-northeast-1
|
|
29
|
+
--url <url> Records の IAM 版 Function URL(指定すると SSM を読まない)
|
|
30
|
+
--collection <name> 既定: admins
|
|
31
|
+
--dry-run 存在確認だけして書き込まない(登録する内容を出す)
|
|
32
|
+
--confirm <env> 書き込む環境の名前の確認。--env prd で書き込むときは --confirm prd が要る
|
|
33
|
+
|
|
34
|
+
認証: AWS の認証情報(AWS_PROFILE など)。SSM の読み取りと、Records の Function URL の呼び出しの権限が要る
|
|
35
|
+
終了コード: 0 = 登録した(--dry-run では登録できる)、3 = 既に登録されている、2 = 引数の誤り・確認の不足、1 = そのほかの失敗`;
|
|
36
|
+
/** 書き込みに `--confirm <env>` が要る環境 */
|
|
37
|
+
export const CONFIRM_REQUIRED_ENVS = Object.freeze(['prd']);
|
|
38
|
+
const SINGLE = {
|
|
39
|
+
'--app': 'app',
|
|
40
|
+
'--env': 'env',
|
|
41
|
+
'--name': 'name',
|
|
42
|
+
'--region': 'region',
|
|
43
|
+
'--url': 'url',
|
|
44
|
+
'--collection': 'collection',
|
|
45
|
+
'--confirm': 'confirm',
|
|
46
|
+
};
|
|
47
|
+
/** `add` の引数を解釈する(誤りは Error) */
|
|
48
|
+
export function parseAddArgs(argv) {
|
|
49
|
+
const v = {};
|
|
50
|
+
const positional = [];
|
|
51
|
+
let dryRun = false;
|
|
52
|
+
for (let i = 0; i < argv.length; i++) {
|
|
53
|
+
const a = argv[i];
|
|
54
|
+
if (a in SINGLE) {
|
|
55
|
+
const value = argv[++i];
|
|
56
|
+
if (value === undefined || value.trim() === '' || value.startsWith('--')) {
|
|
57
|
+
throw new Error(`${a} に値が要る`);
|
|
58
|
+
}
|
|
59
|
+
v[SINGLE[a]] = value;
|
|
60
|
+
}
|
|
61
|
+
else if (a === '--dry-run') {
|
|
62
|
+
dryRun = true;
|
|
63
|
+
}
|
|
64
|
+
else if (a.startsWith('--')) {
|
|
65
|
+
throw new Error(`未知の引数: ${a}`);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
positional.push(a);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (positional.length !== 1)
|
|
72
|
+
throw new Error('メールアドレスを 1 つ指定する');
|
|
73
|
+
for (const k of ['app', 'env', 'name']) {
|
|
74
|
+
if (!v[k])
|
|
75
|
+
throw new Error(`--${k} が要る`);
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
email: positional[0],
|
|
79
|
+
app: v.app,
|
|
80
|
+
env: v.env,
|
|
81
|
+
name: v.name,
|
|
82
|
+
region: v.region,
|
|
83
|
+
url: v.url,
|
|
84
|
+
collection: v.collection ?? 'admins',
|
|
85
|
+
dryRun,
|
|
86
|
+
confirm: v.confirm,
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/** Records の IAM 版 Function URL の SSM のパラメータ名(terraform/modules/parameter-store と同じ) */
|
|
90
|
+
export const recordsIamUrlParameterName = (app, env) => `/${app}/${env}/infra/dynamodb-client-api-iam-url`;
|
|
91
|
+
/** SSM のパラメータを読む(復号つき) */
|
|
92
|
+
export async function ssmGetParameter(name, region) {
|
|
93
|
+
const ssm = new SSMClient({ region });
|
|
94
|
+
try {
|
|
95
|
+
const r = await ssm.send(new GetParameterCommand({ Name: name, WithDecryption: true }));
|
|
96
|
+
const value = r.Parameter?.Value;
|
|
97
|
+
if (!value)
|
|
98
|
+
throw new Error(`SSM のパラメータが空です: ${name}`);
|
|
99
|
+
return value;
|
|
100
|
+
}
|
|
101
|
+
finally {
|
|
102
|
+
ssm.destroy();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const describe = (r) => `${r.id}(${r.name})`;
|
|
106
|
+
/** 確認が要るのに無い・違うなら、その理由(要らない・合っていれば null) */
|
|
107
|
+
export function missingConfirmation(args) {
|
|
108
|
+
if (args.dryRun || !CONFIRM_REQUIRED_ENVS.includes(args.env))
|
|
109
|
+
return null;
|
|
110
|
+
if (args.confirm === args.env)
|
|
111
|
+
return null;
|
|
112
|
+
return `${args.env} の ${args.collection} に書き込みます。よければ --confirm ${args.env} を付けて実行してください(--dry-run なら不要)`;
|
|
113
|
+
}
|
|
114
|
+
/** 出力に出す宛先(URL のホスト。--url の指定と --app/--env の食い違いに気づけるように) */
|
|
115
|
+
const hostOf = (url) => {
|
|
116
|
+
try {
|
|
117
|
+
return new URL(url).host;
|
|
118
|
+
}
|
|
119
|
+
catch {
|
|
120
|
+
return url;
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
async function runAdd(argv, io, deps) {
|
|
124
|
+
let args;
|
|
125
|
+
try {
|
|
126
|
+
args = parseAddArgs(argv);
|
|
127
|
+
}
|
|
128
|
+
catch (e) {
|
|
129
|
+
io.err(`引数の誤り: ${e.message}\n\n${USAGE}\n`);
|
|
130
|
+
return EXIT_USAGE;
|
|
131
|
+
}
|
|
132
|
+
const unconfirmed = missingConfirmation(args);
|
|
133
|
+
if (unconfirmed) {
|
|
134
|
+
io.err(`確認が要ります(何も書いていません): ${unconfirmed}\n`);
|
|
135
|
+
return EXIT_USAGE;
|
|
136
|
+
}
|
|
137
|
+
const env = deps.env ?? process.env;
|
|
138
|
+
const region = args.region || env.AWS_REGION || 'ap-northeast-1';
|
|
139
|
+
try {
|
|
140
|
+
const url = args.url ??
|
|
141
|
+
(await (deps.getParameter ?? ssmGetParameter)(recordsIamUrlParameterName(args.app, args.env), region));
|
|
142
|
+
const where = `${args.app}/${args.env} の ${args.collection}、${hostOf(url)}`;
|
|
143
|
+
const r = await withCollection(args.collection, (collection) => addAdmin({ email: args.email, name: args.name }, adminWriterOf(collection), {
|
|
144
|
+
dryRun: args.dryRun,
|
|
145
|
+
}), { url, region });
|
|
146
|
+
if (r.status === 'exists') {
|
|
147
|
+
io.err(`既に登録されています(何も書いていません): ${r.record.id}(${where})\n`);
|
|
148
|
+
return EXIT_EXISTS;
|
|
149
|
+
}
|
|
150
|
+
if (r.status === 'dry-run') {
|
|
151
|
+
io.out(`[dry-run] 登録します: ${describe(r.record)}(${where})\n`);
|
|
152
|
+
return EXIT_OK;
|
|
153
|
+
}
|
|
154
|
+
io.out(`管理者を登録しました: ${describe(r.record)}(${where})\n`);
|
|
155
|
+
return EXIT_OK;
|
|
156
|
+
}
|
|
157
|
+
catch (e) {
|
|
158
|
+
io.err(`登録に失敗しました: ${e.message}\n`);
|
|
159
|
+
return EXIT_ERROR;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
/** CLI 本体。終了コードを返す(process.exitCode は run.ts が設定する) */
|
|
163
|
+
export async function main(argv, io, deps = {}) {
|
|
164
|
+
const [sub, ...rest] = argv;
|
|
165
|
+
if (sub === '--help' || sub === '-h') {
|
|
166
|
+
io.out(`${USAGE}\n`);
|
|
167
|
+
return EXIT_OK;
|
|
168
|
+
}
|
|
169
|
+
if (sub === 'add')
|
|
170
|
+
return runAdd(rest, io, deps);
|
|
171
|
+
io.err(`サブコマンドは add(got: ${sub ?? 'なし'})\n\n${USAGE}\n`);
|
|
172
|
+
return EXIT_USAGE;
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-admins/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dynamodb-admins`(管理者の登録の CLI。ADR 0007 §7)の部品。使い方は docs/ADMINS.md の「最初の管理者の登録」
|
|
3
|
+
*/
|
|
4
|
+
export { CONFIRM_REQUIRED_ENVS, EXIT_ERROR, EXIT_EXISTS, EXIT_OK, EXIT_USAGE, USAGE, main, missingConfirmation, parseAddArgs, recordsIamUrlParameterName, ssmGetParameter, } from './cli.js';
|
|
5
|
+
export type { AddArgs, CliDeps, CliIo } from './cli.js';
|
|
6
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-admins/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,WAAW,EACX,OAAO,EACP,UAAU,EACV,KAAK,EACL,IAAI,EACJ,mBAAmB,EACnB,YAAY,EACZ,0BAA0B,EAC1B,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,YAAY,EAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `dynamodb-admins`(管理者の登録の CLI。ADR 0007 §7)の部品。使い方は docs/ADMINS.md の「最初の管理者の登録」
|
|
3
|
+
*/
|
|
4
|
+
export { CONFIRM_REQUIRED_ENVS, EXIT_ERROR, EXIT_EXISTS, EXIT_OK, EXIT_USAGE, USAGE, main, missingConfirmation, parseAddArgs, recordsIamUrlParameterName, ssmGetParameter, } from './cli.js';
|
|
5
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-admins/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,OAAO,EACL,qBAAqB,EACrB,UAAU,EACV,WAAW,EACX,OAAO,EACP,UAAU,EACV,KAAK,EACL,IAAI,EACJ,mBAAmB,EACnB,YAAY,EACZ,0BAA0B,EAC1B,eAAe,GAChB,MAAM,UAAU,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.d.ts","sourceRoot":"","sources":["../../../src/tools/dynamodb-admins/run.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { main } from './cli.js';
|
|
2
|
+
main(process.argv.slice(2), {
|
|
3
|
+
out: (s) => process.stdout.write(s),
|
|
4
|
+
err: (s) => process.stderr.write(s),
|
|
5
|
+
}).then((code) => {
|
|
6
|
+
process.exitCode = code;
|
|
7
|
+
}, (e) => {
|
|
8
|
+
process.stderr.write(`${e instanceof Error ? e.message : String(e)}\n`);
|
|
9
|
+
process.exitCode = 1;
|
|
10
|
+
});
|
|
11
|
+
//# sourceMappingURL=run.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"run.js","sourceRoot":"","sources":["../../../src/tools/dynamodb-admins/run.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,UAAU,CAAC;AAEhC,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE;IAC1B,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,GAAG,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;CACpC,CAAC,CAAC,IAAI,CACL,CAAC,IAAI,EAAE,EAAE;IACP,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC;AAC1B,CAAC,EACD,CAAC,CAAU,EAAE,EAAE;IACb,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;IACxE,OAAO,CAAC,QAAQ,GAAG,CAAC,CAAC;AACvB,CAAC,CACF,CAAC"}
|
package/docs/ADMINS.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# 管理者(admins)
|
|
2
|
+
|
|
3
|
+
管理画面にログインできる人の正本は、Records の `admins` コレクションのレコードである([ADR 0007](adr/0007-app-basic-features.md) §7)。このライブラリは、レコードの形・メールの正規化・存在確認つきの作成(`@exabugs/dynamodb-client/admins`)、管理画面(`adminsResource`。[react-admin-integration.md](react-admin-integration.md) の「管理者の画面」)、最初の 1 人の登録の CLI(`dynamodb-admins`)を持つ。通知の宛先(`createAdminDirectory`。[NOTIFY.md](NOTIFY.md))も同じレコードを読む。
|
|
4
|
+
|
|
5
|
+
## レコードの形
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import type { AdminRecord } from '@exabugs/dynamodb-client/admins';
|
|
9
|
+
|
|
10
|
+
interface AdminRecord {
|
|
11
|
+
id: string; // 不変。作成時の小文字のメール
|
|
12
|
+
name: string; // 表示名(必須)
|
|
13
|
+
email: string; // 不変。常に id と同じ値
|
|
14
|
+
createdAt?: string;
|
|
15
|
+
updatedAt?: string;
|
|
16
|
+
}
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
- `id` が主キーなので、同じメールで 2 件はできない。ログインの判定(Pre Token Generation)は、正規化したメールで `id` を直接引く
|
|
20
|
+
- `email` を持たない既存のレコード(asanowa の `{ id, name }`)は、`id` を `email` として扱う(`adminEmailOf(record)`)。補完の書き込みは任意
|
|
21
|
+
- `name` は必須。ただし `name` を持たない既存のレコード(astro)も、画面は表示できる(「—」)
|
|
22
|
+
- メールアドレスは変えられない。新しい管理者を作り、古い管理者を削除する
|
|
23
|
+
|
|
24
|
+
## 関数
|
|
25
|
+
|
|
26
|
+
| 関数 | 内容 |
|
|
27
|
+
| -------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
28
|
+
| `normalizeAdminEmail(raw)` | 前後の空白を除き小文字にする(形式は検査しない)。画面・CLI・`createAdminDirectory` が共有する |
|
|
29
|
+
| `isAdminEmail(normalized)` | メールの形式か |
|
|
30
|
+
| `adminEmailOf(record)` | `email`(無ければ `id`) |
|
|
31
|
+
| `buildAdminRecord({ email, name })` | 作るレコード `{ id, email, name }`。形式の誤り・空の名前は `AdminInputError`(`field` は `'email'` か `'name'`) |
|
|
32
|
+
| `addAdmin(input, writer, { dryRun? })` | 存在確認つきの作成。`{ status: 'created' \| 'exists' \| 'dry-run', record }`。既にあれば書き込まない |
|
|
33
|
+
| `adminWriterOf(collection)` | Records のコレクションでの存在確認(`findAdmin`)と `insertOne` |
|
|
34
|
+
| `findAdmin(collection, id)` | `find({ id })` + id の昇順 + 1 件(無ければ null) |
|
|
35
|
+
|
|
36
|
+
**作成の前に、必ず `id` で存在を確かめる。** 同じ `id` で `insertOne` すると、Records は存在確認をせずに黙って上書きする(条件付き書き込みが無い既知の制約)。`findOne` は、無い `id` で 404 と `ITEM_NOT_FOUND` の警告を出すので、存在確認には使わない。
|
|
37
|
+
|
|
38
|
+
## 最初の管理者の登録(CLI)
|
|
39
|
+
|
|
40
|
+
管理画面には admins にいる人しかログインできないので、最初の 1 人は CLI で登録する(2 人目以降は管理画面の「管理者」から)。
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
AWS_PROFILE=<profile> npx dynamodb-admins add you@example.com --app <app> --env dev --name 山田 --dry-run
|
|
44
|
+
AWS_PROFILE=<profile> npx dynamodb-admins add you@example.com --app <app> --env dev --name 山田
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- 経路: Records の IAM 版 Function URL を、利用者の AWS の認証情報(SigV4)で呼ぶ。URL は SSM の `/<app>/<env>/infra/dynamodb-client-api-iam-url`(`terraform/modules/parameter-store` が作る)から読む(`--url` で直接も渡せる)。要る権限は、SSM の `GetParameter` と、Function URL の呼び出し
|
|
48
|
+
- オプション: `--region`(既定 `AWS_REGION`、無ければ `ap-northeast-1`)、`--collection`(既定 `admins`)、`--dry-run`(存在確認だけして書き込まない)、`--confirm <env>`
|
|
49
|
+
- **prd の確認**: `--env prd` で書き込むときは `--confirm prd` が要る(例: `… --env prd --name 山田 --confirm prd`。astro の `make … CONFIRM=prd` と同じ流儀)。無い・違う値なら、SSM も Records も呼ばずに終了コード 2。`--dry-run` は書き込まないので要らない
|
|
50
|
+
- 出力には、宛先の `app/env`・コレクションと、Records の URL のホストを出す(`--url` を渡したときの食い違いに気づけるように)
|
|
51
|
+
- 終了コード: 0 = 登録した(`--dry-run` では登録できる)、**3 = 既に登録されている(何も書いていない)**、2 = 引数の誤り・prd の確認の不足、1 = そのほかの失敗(メールの形式の誤りを含む)
|
|
52
|
+
|
|
53
|
+
## アプリに残るもの
|
|
54
|
+
|
|
55
|
+
- 管理画面: `<Resource {...adminsResource({ icon })} />`(アイコンはアプリが選ぶ)
|
|
56
|
+
- 最初の管理者のメール(CLI の引数)
|
|
57
|
+
- 誰を admins に入れるか(データ)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exabugs/dynamodb-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.23.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",
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
"types": "./dist/index.d.ts",
|
|
10
10
|
"bin": {
|
|
11
11
|
"dynamodb-access-check": "./bin/dynamodb-access-check.js",
|
|
12
|
+
"dynamodb-admins": "./bin/dynamodb-admins.js",
|
|
12
13
|
"dynamodb-system-change": "./bin/dynamodb-system-change.js"
|
|
13
14
|
},
|
|
14
15
|
"keywords": [
|
|
@@ -46,7 +47,8 @@
|
|
|
46
47
|
"docs/NOTIFY.md",
|
|
47
48
|
"docs/DYNAMODB_ACCESS.md",
|
|
48
49
|
"docs/RECORDS_CLIENT.md",
|
|
49
|
-
"docs/SYSTEM_CHANGES.md"
|
|
50
|
+
"docs/SYSTEM_CHANGES.md",
|
|
51
|
+
"docs/ADMINS.md"
|
|
50
52
|
],
|
|
51
53
|
"exports": {
|
|
52
54
|
".": {
|
|
@@ -89,6 +91,10 @@
|
|
|
89
91
|
"import": "./dist/integrations/react-admin/index.js",
|
|
90
92
|
"types": "./dist/integrations/react-admin/index.d.ts"
|
|
91
93
|
},
|
|
94
|
+
"./integrations/react-admin/i18n": {
|
|
95
|
+
"import": "./dist/integrations/react-admin/i18n/index.js",
|
|
96
|
+
"types": "./dist/integrations/react-admin/i18n/index.d.ts"
|
|
97
|
+
},
|
|
92
98
|
"./server/media/presign": {
|
|
93
99
|
"import": "./dist/server/media/presign.js",
|
|
94
100
|
"types": "./dist/server/media/presign.d.ts"
|
|
@@ -138,11 +144,17 @@
|
|
|
138
144
|
"./system-changes/cli": {
|
|
139
145
|
"import": "./dist/tools/dynamodb-system-change/index.js",
|
|
140
146
|
"types": "./dist/tools/dynamodb-system-change/index.d.ts"
|
|
147
|
+
},
|
|
148
|
+
"./admins": {
|
|
149
|
+
"import": "./dist/admins/index.js",
|
|
150
|
+
"types": "./dist/admins/index.d.ts"
|
|
141
151
|
}
|
|
142
152
|
},
|
|
143
153
|
"peerDependencies": {
|
|
144
154
|
"@mui/material": "^5.16.12 || ^6.0.0 || ^7.0.0",
|
|
145
|
-
"react-admin": "^5.0.0"
|
|
155
|
+
"react-admin": "^5.0.0",
|
|
156
|
+
"ra-i18n-polyglot": "^5.0.0",
|
|
157
|
+
"ra-language-english": "^5.0.0"
|
|
146
158
|
},
|
|
147
159
|
"peerDependenciesMeta": {
|
|
148
160
|
"@mui/material": {
|
|
@@ -150,6 +162,12 @@
|
|
|
150
162
|
},
|
|
151
163
|
"react-admin": {
|
|
152
164
|
"optional": true
|
|
165
|
+
},
|
|
166
|
+
"ra-i18n-polyglot": {
|
|
167
|
+
"optional": true
|
|
168
|
+
},
|
|
169
|
+
"ra-language-english": {
|
|
170
|
+
"optional": true
|
|
153
171
|
}
|
|
154
172
|
},
|
|
155
173
|
"scripts": {
|
|
@@ -210,6 +228,8 @@
|
|
|
210
228
|
"jsdom": "^26.1.0",
|
|
211
229
|
"orval": "^8.2.0",
|
|
212
230
|
"prettier": "^3.0.0",
|
|
231
|
+
"ra-i18n-polyglot": "^5.0.0",
|
|
232
|
+
"ra-language-english": "^5.0.0",
|
|
213
233
|
"react-admin": "^5.0.0",
|
|
214
234
|
"sharp": "^0.35.5",
|
|
215
235
|
"tsx": "^4.21.0",
|