@downcity/city 0.2.205 → 0.2.211
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/README.md +63 -5
- package/bin/client/bureau.d.ts +41 -0
- package/bin/client/bureau.d.ts.map +1 -0
- package/bin/client/bureau.js +240 -0
- package/bin/client/bureau.js.map +1 -0
- package/bin/client/city.d.ts +15 -79
- package/bin/client/city.d.ts.map +1 -1
- package/bin/client/city.js +15 -99
- package/bin/client/city.js.map +1 -1
- package/bin/client/federation-admin.d.ts +32 -0
- package/bin/client/federation-admin.d.ts.map +1 -0
- package/bin/client/federation-admin.js +54 -0
- package/bin/client/federation-admin.js.map +1 -0
- package/bin/client/types.d.ts +10 -70
- package/bin/client/types.d.ts.map +1 -1
- package/bin/client/types.js +3 -3
- package/bin/federation/auth/authenticator.d.ts +17 -13
- package/bin/federation/auth/authenticator.d.ts.map +1 -1
- package/bin/federation/auth/authenticator.js +38 -27
- package/bin/federation/auth/authenticator.js.map +1 -1
- package/bin/federation/auth/bureau-token-schema.d.ts +274 -0
- package/bin/federation/auth/bureau-token-schema.d.ts.map +1 -0
- package/bin/federation/auth/bureau-token-schema.js +43 -0
- package/bin/federation/auth/bureau-token-schema.js.map +1 -0
- package/bin/federation/auth/bureau-token-store.d.ts +22 -0
- package/bin/federation/auth/bureau-token-store.d.ts.map +1 -0
- package/bin/federation/auth/bureau-token-store.js +122 -0
- package/bin/federation/auth/bureau-token-store.js.map +1 -0
- package/bin/federation/auth/federation-key-store.d.ts +26 -0
- package/bin/federation/auth/federation-key-store.d.ts.map +1 -0
- package/bin/federation/auth/federation-key-store.js +96 -0
- package/bin/federation/auth/federation-key-store.js.map +1 -0
- package/bin/federation/auth/key-schema.d.ts +275 -0
- package/bin/federation/auth/key-schema.d.ts.map +1 -0
- package/bin/federation/auth/key-schema.js +44 -0
- package/bin/federation/auth/key-schema.js.map +1 -0
- package/bin/federation/auth/types.d.ts +62 -4
- package/bin/federation/auth/types.d.ts.map +1 -1
- package/bin/federation/auth/user-token-authority.d.ts +26 -0
- package/bin/federation/auth/user-token-authority.d.ts.map +1 -0
- package/bin/federation/auth/user-token-authority.js +179 -0
- package/bin/federation/auth/user-token-authority.js.map +1 -0
- package/bin/federation/federation-init.d.ts.map +1 -1
- package/bin/federation/federation-init.js +23 -4
- package/bin/federation/federation-init.js.map +1 -1
- package/bin/federation/federation-router.d.ts +3 -1
- package/bin/federation/federation-router.d.ts.map +1 -1
- package/bin/federation/federation-router.js +17 -0
- package/bin/federation/federation-router.js.map +1 -1
- package/bin/federation/federation-runtime.d.ts.map +1 -1
- package/bin/federation/federation-runtime.js +14 -2
- package/bin/federation/federation-runtime.js.map +1 -1
- package/bin/federation/federation.d.ts.map +1 -1
- package/bin/federation/federation.js +2 -0
- package/bin/federation/federation.js.map +1 -1
- package/bin/federation/runtime.d.ts +4 -0
- package/bin/federation/runtime.d.ts.map +1 -1
- package/bin/index.d.ts +8 -3
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +3 -1
- package/bin/index.js.map +1 -1
- package/bin/pact/admin/index.d.ts +7 -19
- package/bin/pact/admin/index.d.ts.map +1 -1
- package/bin/pact/admin/index.js +10 -18
- package/bin/pact/admin/index.js.map +1 -1
- package/bin/pact/admin/types.d.ts +4 -10
- package/bin/pact/admin/types.d.ts.map +1 -1
- package/bin/pact/invoker/bureaus/index.d.ts +25 -0
- package/bin/pact/invoker/bureaus/index.d.ts.map +1 -0
- package/bin/pact/invoker/bureaus/index.js +36 -0
- package/bin/pact/invoker/bureaus/index.js.map +1 -0
- package/bin/pact/invoker/cities/types.d.ts +2 -2
- package/bin/pact/invoker/cities/types.d.ts.map +1 -1
- package/bin/pact/invoker/user/index.d.ts +19 -0
- package/bin/pact/invoker/user/index.d.ts.map +1 -0
- package/bin/pact/invoker/user/index.js +24 -0
- package/bin/pact/invoker/user/index.js.map +1 -0
- package/bin/pact/user/index.d.ts +2 -1
- package/bin/pact/user/index.d.ts.map +1 -1
- package/bin/pact/user/index.js +7 -3
- package/bin/pact/user/index.js.map +1 -1
- package/bin/pact/user/types.d.ts +0 -2
- package/bin/pact/user/types.d.ts.map +1 -1
- package/bin/service/ai/ai-service.d.ts +1 -1
- package/bin/service/ai/ai-service.d.ts.map +1 -1
- package/bin/service/ai/model-registry.d.ts +1 -1
- package/bin/service/ai/model-registry.d.ts.map +1 -1
- package/bin/service/bureaus/bureaus-service.d.ts +15 -0
- package/bin/service/bureaus/bureaus-service.d.ts.map +1 -0
- package/bin/service/bureaus/bureaus-service.js +55 -0
- package/bin/service/bureaus/bureaus-service.js.map +1 -0
- package/bin/service/installable-service.d.ts +5 -2
- package/bin/service/installable-service.d.ts.map +1 -1
- package/bin/service/installable-service.js +1 -0
- package/bin/service/installable-service.js.map +1 -1
- package/bin/service/instruction.js +1 -1
- package/bin/service/instruction.js.map +1 -1
- package/bin/service/service.d.ts +7 -1
- package/bin/service/service.d.ts.map +1 -1
- package/bin/service/service.js +2 -0
- package/bin/service/service.js.map +1 -1
- package/bin/types/Bureau.d.ts +96 -0
- package/bin/types/Bureau.d.ts.map +1 -0
- package/bin/types/Bureau.js +8 -0
- package/bin/types/Bureau.js.map +1 -0
- package/bin/types/FederationAdmin.d.ts +16 -0
- package/bin/types/FederationAdmin.d.ts.map +1 -0
- package/bin/types/FederationAdmin.js +7 -0
- package/bin/types/FederationAdmin.js.map +1 -0
- package/bin/types/User.d.ts +24 -0
- package/bin/types/User.d.ts.map +1 -0
- package/bin/types/User.js +8 -0
- package/bin/types/User.js.map +1 -0
- package/package.json +4 -3
- package/bin/federation/auth/token-signer.d.ts +0 -27
- package/bin/federation/auth/token-signer.d.ts.map +0 -1
- package/bin/federation/auth/token-signer.js +0 -116
- package/bin/federation/auth/token-signer.js.map +0 -1
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Bureau Token 数据库表。
|
|
3
|
+
*
|
|
4
|
+
* 只保存 Token hash 和权限元数据,不持久化 Bureau Token 明文。
|
|
5
|
+
*/
|
|
6
|
+
/** Federation SQLite Bureau Token 表。 */
|
|
7
|
+
export declare const sqlite_bureau_tokens: import("drizzle-orm/sqlite-core").SQLiteTableWithColumns<{
|
|
8
|
+
name: "federation_bureau_tokens";
|
|
9
|
+
schema: undefined;
|
|
10
|
+
columns: {
|
|
11
|
+
token_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
12
|
+
name: "token_id";
|
|
13
|
+
tableName: "federation_bureau_tokens";
|
|
14
|
+
dataType: "string";
|
|
15
|
+
columnType: "SQLiteText";
|
|
16
|
+
data: string;
|
|
17
|
+
driverParam: string;
|
|
18
|
+
notNull: true;
|
|
19
|
+
hasDefault: false;
|
|
20
|
+
isPrimaryKey: true;
|
|
21
|
+
isAutoincrement: false;
|
|
22
|
+
hasRuntimeDefault: false;
|
|
23
|
+
enumValues: [string, ...string[]];
|
|
24
|
+
baseColumn: never;
|
|
25
|
+
identity: undefined;
|
|
26
|
+
generated: undefined;
|
|
27
|
+
}, {}, {
|
|
28
|
+
length: number | undefined;
|
|
29
|
+
}>;
|
|
30
|
+
city_id: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
31
|
+
name: "city_id";
|
|
32
|
+
tableName: "federation_bureau_tokens";
|
|
33
|
+
dataType: "string";
|
|
34
|
+
columnType: "SQLiteText";
|
|
35
|
+
data: string;
|
|
36
|
+
driverParam: string;
|
|
37
|
+
notNull: true;
|
|
38
|
+
hasDefault: false;
|
|
39
|
+
isPrimaryKey: false;
|
|
40
|
+
isAutoincrement: false;
|
|
41
|
+
hasRuntimeDefault: false;
|
|
42
|
+
enumValues: [string, ...string[]];
|
|
43
|
+
baseColumn: never;
|
|
44
|
+
identity: undefined;
|
|
45
|
+
generated: undefined;
|
|
46
|
+
}, {}, {
|
|
47
|
+
length: number | undefined;
|
|
48
|
+
}>;
|
|
49
|
+
token_hash: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
50
|
+
name: "token_hash";
|
|
51
|
+
tableName: "federation_bureau_tokens";
|
|
52
|
+
dataType: "string";
|
|
53
|
+
columnType: "SQLiteText";
|
|
54
|
+
data: string;
|
|
55
|
+
driverParam: string;
|
|
56
|
+
notNull: true;
|
|
57
|
+
hasDefault: false;
|
|
58
|
+
isPrimaryKey: false;
|
|
59
|
+
isAutoincrement: false;
|
|
60
|
+
hasRuntimeDefault: false;
|
|
61
|
+
enumValues: [string, ...string[]];
|
|
62
|
+
baseColumn: never;
|
|
63
|
+
identity: undefined;
|
|
64
|
+
generated: undefined;
|
|
65
|
+
}, {}, {
|
|
66
|
+
length: number | undefined;
|
|
67
|
+
}>;
|
|
68
|
+
capabilities: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
69
|
+
name: "capabilities";
|
|
70
|
+
tableName: "federation_bureau_tokens";
|
|
71
|
+
dataType: "string";
|
|
72
|
+
columnType: "SQLiteText";
|
|
73
|
+
data: string;
|
|
74
|
+
driverParam: string;
|
|
75
|
+
notNull: true;
|
|
76
|
+
hasDefault: false;
|
|
77
|
+
isPrimaryKey: false;
|
|
78
|
+
isAutoincrement: false;
|
|
79
|
+
hasRuntimeDefault: false;
|
|
80
|
+
enumValues: [string, ...string[]];
|
|
81
|
+
baseColumn: never;
|
|
82
|
+
identity: undefined;
|
|
83
|
+
generated: undefined;
|
|
84
|
+
}, {}, {
|
|
85
|
+
length: number | undefined;
|
|
86
|
+
}>;
|
|
87
|
+
status: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
88
|
+
name: "status";
|
|
89
|
+
tableName: "federation_bureau_tokens";
|
|
90
|
+
dataType: "string";
|
|
91
|
+
columnType: "SQLiteText";
|
|
92
|
+
data: string;
|
|
93
|
+
driverParam: string;
|
|
94
|
+
notNull: true;
|
|
95
|
+
hasDefault: false;
|
|
96
|
+
isPrimaryKey: false;
|
|
97
|
+
isAutoincrement: false;
|
|
98
|
+
hasRuntimeDefault: false;
|
|
99
|
+
enumValues: [string, ...string[]];
|
|
100
|
+
baseColumn: never;
|
|
101
|
+
identity: undefined;
|
|
102
|
+
generated: undefined;
|
|
103
|
+
}, {}, {
|
|
104
|
+
length: number | undefined;
|
|
105
|
+
}>;
|
|
106
|
+
created_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
107
|
+
name: "created_at";
|
|
108
|
+
tableName: "federation_bureau_tokens";
|
|
109
|
+
dataType: "string";
|
|
110
|
+
columnType: "SQLiteText";
|
|
111
|
+
data: string;
|
|
112
|
+
driverParam: string;
|
|
113
|
+
notNull: true;
|
|
114
|
+
hasDefault: false;
|
|
115
|
+
isPrimaryKey: false;
|
|
116
|
+
isAutoincrement: false;
|
|
117
|
+
hasRuntimeDefault: false;
|
|
118
|
+
enumValues: [string, ...string[]];
|
|
119
|
+
baseColumn: never;
|
|
120
|
+
identity: undefined;
|
|
121
|
+
generated: undefined;
|
|
122
|
+
}, {}, {
|
|
123
|
+
length: number | undefined;
|
|
124
|
+
}>;
|
|
125
|
+
updated_at: import("drizzle-orm/sqlite-core").SQLiteColumn<{
|
|
126
|
+
name: "updated_at";
|
|
127
|
+
tableName: "federation_bureau_tokens";
|
|
128
|
+
dataType: "string";
|
|
129
|
+
columnType: "SQLiteText";
|
|
130
|
+
data: string;
|
|
131
|
+
driverParam: string;
|
|
132
|
+
notNull: true;
|
|
133
|
+
hasDefault: false;
|
|
134
|
+
isPrimaryKey: false;
|
|
135
|
+
isAutoincrement: false;
|
|
136
|
+
hasRuntimeDefault: false;
|
|
137
|
+
enumValues: [string, ...string[]];
|
|
138
|
+
baseColumn: never;
|
|
139
|
+
identity: undefined;
|
|
140
|
+
generated: undefined;
|
|
141
|
+
}, {}, {
|
|
142
|
+
length: number | undefined;
|
|
143
|
+
}>;
|
|
144
|
+
};
|
|
145
|
+
dialect: "sqlite";
|
|
146
|
+
}>;
|
|
147
|
+
/** Federation Postgres Bureau Token 表。 */
|
|
148
|
+
export declare const pg_bureau_tokens: import("drizzle-orm/pg-core").PgTableWithColumns<{
|
|
149
|
+
name: "federation_bureau_tokens";
|
|
150
|
+
schema: undefined;
|
|
151
|
+
columns: {
|
|
152
|
+
token_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
153
|
+
name: "token_id";
|
|
154
|
+
tableName: "federation_bureau_tokens";
|
|
155
|
+
dataType: "string";
|
|
156
|
+
columnType: "PgText";
|
|
157
|
+
data: string;
|
|
158
|
+
driverParam: string;
|
|
159
|
+
notNull: true;
|
|
160
|
+
hasDefault: false;
|
|
161
|
+
isPrimaryKey: true;
|
|
162
|
+
isAutoincrement: false;
|
|
163
|
+
hasRuntimeDefault: false;
|
|
164
|
+
enumValues: [string, ...string[]];
|
|
165
|
+
baseColumn: never;
|
|
166
|
+
identity: undefined;
|
|
167
|
+
generated: undefined;
|
|
168
|
+
}, {}, {}>;
|
|
169
|
+
city_id: import("drizzle-orm/pg-core").PgColumn<{
|
|
170
|
+
name: "city_id";
|
|
171
|
+
tableName: "federation_bureau_tokens";
|
|
172
|
+
dataType: "string";
|
|
173
|
+
columnType: "PgText";
|
|
174
|
+
data: string;
|
|
175
|
+
driverParam: string;
|
|
176
|
+
notNull: true;
|
|
177
|
+
hasDefault: false;
|
|
178
|
+
isPrimaryKey: false;
|
|
179
|
+
isAutoincrement: false;
|
|
180
|
+
hasRuntimeDefault: false;
|
|
181
|
+
enumValues: [string, ...string[]];
|
|
182
|
+
baseColumn: never;
|
|
183
|
+
identity: undefined;
|
|
184
|
+
generated: undefined;
|
|
185
|
+
}, {}, {}>;
|
|
186
|
+
token_hash: import("drizzle-orm/pg-core").PgColumn<{
|
|
187
|
+
name: "token_hash";
|
|
188
|
+
tableName: "federation_bureau_tokens";
|
|
189
|
+
dataType: "string";
|
|
190
|
+
columnType: "PgText";
|
|
191
|
+
data: string;
|
|
192
|
+
driverParam: string;
|
|
193
|
+
notNull: true;
|
|
194
|
+
hasDefault: false;
|
|
195
|
+
isPrimaryKey: false;
|
|
196
|
+
isAutoincrement: false;
|
|
197
|
+
hasRuntimeDefault: false;
|
|
198
|
+
enumValues: [string, ...string[]];
|
|
199
|
+
baseColumn: never;
|
|
200
|
+
identity: undefined;
|
|
201
|
+
generated: undefined;
|
|
202
|
+
}, {}, {}>;
|
|
203
|
+
capabilities: import("drizzle-orm/pg-core").PgColumn<{
|
|
204
|
+
name: "capabilities";
|
|
205
|
+
tableName: "federation_bureau_tokens";
|
|
206
|
+
dataType: "string";
|
|
207
|
+
columnType: "PgText";
|
|
208
|
+
data: string;
|
|
209
|
+
driverParam: string;
|
|
210
|
+
notNull: true;
|
|
211
|
+
hasDefault: false;
|
|
212
|
+
isPrimaryKey: false;
|
|
213
|
+
isAutoincrement: false;
|
|
214
|
+
hasRuntimeDefault: false;
|
|
215
|
+
enumValues: [string, ...string[]];
|
|
216
|
+
baseColumn: never;
|
|
217
|
+
identity: undefined;
|
|
218
|
+
generated: undefined;
|
|
219
|
+
}, {}, {}>;
|
|
220
|
+
status: import("drizzle-orm/pg-core").PgColumn<{
|
|
221
|
+
name: "status";
|
|
222
|
+
tableName: "federation_bureau_tokens";
|
|
223
|
+
dataType: "string";
|
|
224
|
+
columnType: "PgText";
|
|
225
|
+
data: string;
|
|
226
|
+
driverParam: string;
|
|
227
|
+
notNull: true;
|
|
228
|
+
hasDefault: false;
|
|
229
|
+
isPrimaryKey: false;
|
|
230
|
+
isAutoincrement: false;
|
|
231
|
+
hasRuntimeDefault: false;
|
|
232
|
+
enumValues: [string, ...string[]];
|
|
233
|
+
baseColumn: never;
|
|
234
|
+
identity: undefined;
|
|
235
|
+
generated: undefined;
|
|
236
|
+
}, {}, {}>;
|
|
237
|
+
created_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
238
|
+
name: "created_at";
|
|
239
|
+
tableName: "federation_bureau_tokens";
|
|
240
|
+
dataType: "string";
|
|
241
|
+
columnType: "PgText";
|
|
242
|
+
data: string;
|
|
243
|
+
driverParam: string;
|
|
244
|
+
notNull: true;
|
|
245
|
+
hasDefault: false;
|
|
246
|
+
isPrimaryKey: false;
|
|
247
|
+
isAutoincrement: false;
|
|
248
|
+
hasRuntimeDefault: false;
|
|
249
|
+
enumValues: [string, ...string[]];
|
|
250
|
+
baseColumn: never;
|
|
251
|
+
identity: undefined;
|
|
252
|
+
generated: undefined;
|
|
253
|
+
}, {}, {}>;
|
|
254
|
+
updated_at: import("drizzle-orm/pg-core").PgColumn<{
|
|
255
|
+
name: "updated_at";
|
|
256
|
+
tableName: "federation_bureau_tokens";
|
|
257
|
+
dataType: "string";
|
|
258
|
+
columnType: "PgText";
|
|
259
|
+
data: string;
|
|
260
|
+
driverParam: string;
|
|
261
|
+
notNull: true;
|
|
262
|
+
hasDefault: false;
|
|
263
|
+
isPrimaryKey: false;
|
|
264
|
+
isAutoincrement: false;
|
|
265
|
+
hasRuntimeDefault: false;
|
|
266
|
+
enumValues: [string, ...string[]];
|
|
267
|
+
baseColumn: never;
|
|
268
|
+
identity: undefined;
|
|
269
|
+
generated: undefined;
|
|
270
|
+
}, {}, {}>;
|
|
271
|
+
};
|
|
272
|
+
dialect: "pg";
|
|
273
|
+
}>;
|
|
274
|
+
//# sourceMappingURL=bureau-token-schema.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bureau-token-schema.d.ts","sourceRoot":"","sources":["../../../src/federation/auth/bureau-token-schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAOH,wCAAwC;AACxC,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe/B,CAAC;AAEH,0CAA0C;AAC1C,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAe3B,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Bureau Token 数据库表。
|
|
3
|
+
*
|
|
4
|
+
* 只保存 Token hash 和权限元数据,不持久化 Bureau Token 明文。
|
|
5
|
+
*/
|
|
6
|
+
import { pgTable, text as pgText } from "drizzle-orm/pg-core";
|
|
7
|
+
import { sqliteTable, text as sqliteText } from "drizzle-orm/sqlite-core";
|
|
8
|
+
const BUREAU_TOKEN_TABLE = "federation_bureau_tokens";
|
|
9
|
+
/** Federation SQLite Bureau Token 表。 */
|
|
10
|
+
export const sqlite_bureau_tokens = sqliteTable(BUREAU_TOKEN_TABLE, {
|
|
11
|
+
/** Bureau Token 查找 ID。 */
|
|
12
|
+
token_id: sqliteText("token_id").primaryKey(),
|
|
13
|
+
/** Token 绑定的 City。 */
|
|
14
|
+
city_id: sqliteText("city_id").notNull(),
|
|
15
|
+
/** Token SHA-256 Base64URL hash。 */
|
|
16
|
+
token_hash: sqliteText("token_hash").notNull(),
|
|
17
|
+
/** JSON capability 列表。 */
|
|
18
|
+
capabilities: sqliteText("capabilities").notNull(),
|
|
19
|
+
/** active 或 revoked。 */
|
|
20
|
+
status: sqliteText("status").notNull(),
|
|
21
|
+
/** 创建时间。 */
|
|
22
|
+
created_at: sqliteText("created_at").notNull(),
|
|
23
|
+
/** 更新时间。 */
|
|
24
|
+
updated_at: sqliteText("updated_at").notNull(),
|
|
25
|
+
});
|
|
26
|
+
/** Federation Postgres Bureau Token 表。 */
|
|
27
|
+
export const pg_bureau_tokens = pgTable(BUREAU_TOKEN_TABLE, {
|
|
28
|
+
/** Bureau Token 查找 ID。 */
|
|
29
|
+
token_id: pgText("token_id").primaryKey(),
|
|
30
|
+
/** Token 绑定的 City。 */
|
|
31
|
+
city_id: pgText("city_id").notNull(),
|
|
32
|
+
/** Token SHA-256 Base64URL hash。 */
|
|
33
|
+
token_hash: pgText("token_hash").notNull(),
|
|
34
|
+
/** JSON capability 列表。 */
|
|
35
|
+
capabilities: pgText("capabilities").notNull(),
|
|
36
|
+
/** active 或 revoked。 */
|
|
37
|
+
status: pgText("status").notNull(),
|
|
38
|
+
/** 创建时间。 */
|
|
39
|
+
created_at: pgText("created_at").notNull(),
|
|
40
|
+
/** 更新时间。 */
|
|
41
|
+
updated_at: pgText("updated_at").notNull(),
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=bureau-token-schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bureau-token-schema.js","sourceRoot":"","sources":["../../../src/federation/auth/bureau-token-schema.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,OAAO,EAAE,IAAI,IAAI,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAE,IAAI,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAE1E,MAAM,kBAAkB,GAAG,0BAA0B,CAAC;AAEtD,wCAAwC;AACxC,MAAM,CAAC,MAAM,oBAAoB,GAAG,WAAW,CAAC,kBAAkB,EAAE;IAClE,0BAA0B;IAC1B,QAAQ,EAAE,UAAU,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE;IAC7C,sBAAsB;IACtB,OAAO,EAAE,UAAU,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACxC,oCAAoC;IACpC,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC9C,0BAA0B;IAC1B,YAAY,EAAE,UAAU,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE;IAClD,wBAAwB;IACxB,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IACtC,YAAY;IACZ,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC9C,YAAY;IACZ,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC/C,CAAC,CAAC;AAEH,0CAA0C;AAC1C,MAAM,CAAC,MAAM,gBAAgB,GAAG,OAAO,CAAC,kBAAkB,EAAE;IAC1D,0BAA0B;IAC1B,QAAQ,EAAE,MAAM,CAAC,UAAU,CAAC,CAAC,UAAU,EAAE;IACzC,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,EAAE;IACpC,oCAAoC;IACpC,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC1C,0BAA0B;IAC1B,YAAY,EAAE,MAAM,CAAC,cAAc,CAAC,CAAC,OAAO,EAAE;IAC9C,wBAAwB;IACxB,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IAClC,YAAY;IACZ,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAC1C,YAAY;IACZ,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC3C,CAAC,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Bureau Token Store。
|
|
3
|
+
*
|
|
4
|
+
* Token 使用 `fb_<token_id>.<secret>` 格式。数据库只保存完整 token 的 SHA-256 hash,
|
|
5
|
+
* 通过 token_id 定位记录后再比较 hash,从而支持注册表状态校验和撤销。
|
|
6
|
+
*/
|
|
7
|
+
import type { CityTableApi } from "../../store/table-api.js";
|
|
8
|
+
import type { BureauTokenRecord, BureauTokenSummary, RegisterBureauTokenInput, RuntimeBureau } from "../../types/Bureau.js";
|
|
9
|
+
/** Bureau Token 持久化与验证入口。 */
|
|
10
|
+
export declare class BureauTokenStore {
|
|
11
|
+
private readonly table;
|
|
12
|
+
constructor(table: CityTableApi<BureauTokenRecord>);
|
|
13
|
+
/** 登记 CLI 生成的 Bureau Token hash,不接触 Token 明文。 */
|
|
14
|
+
register(input: RegisterBureauTokenInput): Promise<BureauTokenSummary>;
|
|
15
|
+
/** 验证 Bureau Token 并返回绑定身份。 */
|
|
16
|
+
resolve(bureau_token: string): Promise<RuntimeBureau | undefined>;
|
|
17
|
+
/** 列出 Bureau Token 元数据,不返回 token hash。 */
|
|
18
|
+
list(): Promise<BureauTokenSummary[]>;
|
|
19
|
+
/** 立即撤销 Bureau Token。 */
|
|
20
|
+
revoke(token_id: string): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
//# sourceMappingURL=bureau-token-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bureau-token-store.d.ts","sourceRoot":"","sources":["../../../src/federation/auth/bureau-token-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAEV,iBAAiB,EACjB,kBAAkB,EAClB,wBAAwB,EACxB,aAAa,EACd,MAAM,uBAAuB,CAAC;AAI/B,6BAA6B;AAC7B,qBAAa,gBAAgB;IACf,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY,CAAC,iBAAiB,CAAC;IAEnE,iDAAiD;IAC3C,QAAQ,CAAC,KAAK,EAAE,wBAAwB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IA4B5E,+BAA+B;IACzB,OAAO,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,SAAS,CAAC;IAavE,0CAA0C;IACpC,IAAI,IAAI,OAAO,CAAC,kBAAkB,EAAE,CAAC;IAW3C,yBAAyB;IACnB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAU9C"}
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Bureau Token Store。
|
|
3
|
+
*
|
|
4
|
+
* Token 使用 `fb_<token_id>.<secret>` 格式。数据库只保存完整 token 的 SHA-256 hash,
|
|
5
|
+
* 通过 token_id 定位记录后再比较 hash,从而支持注册表状态校验和撤销。
|
|
6
|
+
*/
|
|
7
|
+
import { base64UrlEncodeBytes } from "../../utils/helpers.js";
|
|
8
|
+
const DEFAULT_CAPABILITIES = ["accounts:read"];
|
|
9
|
+
/** Bureau Token 持久化与验证入口。 */
|
|
10
|
+
export class BureauTokenStore {
|
|
11
|
+
table;
|
|
12
|
+
constructor(table) {
|
|
13
|
+
this.table = table;
|
|
14
|
+
}
|
|
15
|
+
/** 登记 CLI 生成的 Bureau Token hash,不接触 Token 明文。 */
|
|
16
|
+
async register(input) {
|
|
17
|
+
const token_id = read_token_id_value(input.token_id);
|
|
18
|
+
const token_hash = read_token_hash(input.token_hash);
|
|
19
|
+
const city_id = read_required_string(input.city_id, "city_id");
|
|
20
|
+
const capabilities = normalize_capabilities(input.capabilities);
|
|
21
|
+
if ((await this.table.select({ token_id }))[0]) {
|
|
22
|
+
throw new TypeError(`Bureau token already registered: ${token_id}`);
|
|
23
|
+
}
|
|
24
|
+
const now = new Date().toISOString();
|
|
25
|
+
await this.table.insert({
|
|
26
|
+
token_id,
|
|
27
|
+
city_id,
|
|
28
|
+
token_hash,
|
|
29
|
+
capabilities: JSON.stringify(capabilities),
|
|
30
|
+
status: "active",
|
|
31
|
+
created_at: now,
|
|
32
|
+
updated_at: now,
|
|
33
|
+
});
|
|
34
|
+
return {
|
|
35
|
+
token_id,
|
|
36
|
+
city_id,
|
|
37
|
+
capabilities,
|
|
38
|
+
status: "active",
|
|
39
|
+
created_at: now,
|
|
40
|
+
updated_at: now,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
/** 验证 Bureau Token 并返回绑定身份。 */
|
|
44
|
+
async resolve(bureau_token) {
|
|
45
|
+
const token_id = read_token_id(bureau_token);
|
|
46
|
+
if (!token_id)
|
|
47
|
+
return undefined;
|
|
48
|
+
const record = (await this.table.select({ token_id }))[0];
|
|
49
|
+
if (!record || record.status !== "active")
|
|
50
|
+
return undefined;
|
|
51
|
+
if (record.token_hash !== await hash_token(bureau_token))
|
|
52
|
+
return undefined;
|
|
53
|
+
return {
|
|
54
|
+
token_id: record.token_id,
|
|
55
|
+
city_id: record.city_id,
|
|
56
|
+
capabilities: normalize_capabilities(JSON.parse(record.capabilities)),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** 列出 Bureau Token 元数据,不返回 token hash。 */
|
|
60
|
+
async list() {
|
|
61
|
+
return (await this.table.select()).map((record) => ({
|
|
62
|
+
token_id: record.token_id,
|
|
63
|
+
city_id: record.city_id,
|
|
64
|
+
capabilities: normalize_capabilities(JSON.parse(record.capabilities)),
|
|
65
|
+
status: record.status,
|
|
66
|
+
created_at: record.created_at,
|
|
67
|
+
updated_at: record.updated_at,
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
/** 立即撤销 Bureau Token。 */
|
|
71
|
+
async revoke(token_id) {
|
|
72
|
+
const id = read_required_string(token_id, "token_id");
|
|
73
|
+
if (!(await this.table.select({ token_id: id }))[0]) {
|
|
74
|
+
throw new TypeError(`Unknown Bureau token: ${id}`);
|
|
75
|
+
}
|
|
76
|
+
await this.table.update({
|
|
77
|
+
where: { token_id: id },
|
|
78
|
+
values: { status: "revoked", updated_at: new Date().toISOString() },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
function read_token_id_value(value) {
|
|
83
|
+
const token_id = read_required_string(value, "token_id");
|
|
84
|
+
if (!/^br_[A-Za-z0-9_-]{16,}$/u.test(token_id)) {
|
|
85
|
+
throw new TypeError("token_id must use the br_<random> format");
|
|
86
|
+
}
|
|
87
|
+
return token_id;
|
|
88
|
+
}
|
|
89
|
+
function read_token_hash(value) {
|
|
90
|
+
const token_hash = read_required_string(value, "token_hash");
|
|
91
|
+
if (!/^[A-Za-z0-9_-]{43}$/u.test(token_hash)) {
|
|
92
|
+
throw new TypeError("token_hash must be a SHA-256 Base64URL value");
|
|
93
|
+
}
|
|
94
|
+
return token_hash;
|
|
95
|
+
}
|
|
96
|
+
function read_token_id(token) {
|
|
97
|
+
const match = String(token ?? "").trim().match(/^fb_(br_[A-Za-z0-9_-]+)\.[A-Za-z0-9_-]+$/u);
|
|
98
|
+
return match?.[1];
|
|
99
|
+
}
|
|
100
|
+
async function hash_token(token) {
|
|
101
|
+
const hash = await crypto.subtle.digest("SHA-256", new TextEncoder().encode(token));
|
|
102
|
+
return base64UrlEncodeBytes(new Uint8Array(hash));
|
|
103
|
+
}
|
|
104
|
+
function normalize_capabilities(input) {
|
|
105
|
+
const values = input ?? DEFAULT_CAPABILITIES;
|
|
106
|
+
const allowed = new Set(["accounts:read"]);
|
|
107
|
+
const result = [...new Set(values)];
|
|
108
|
+
if (result.length === 0)
|
|
109
|
+
throw new TypeError("Bureau Token requires at least one capability");
|
|
110
|
+
for (const capability of result) {
|
|
111
|
+
if (!allowed.has(capability))
|
|
112
|
+
throw new TypeError(`Unknown Bureau capability: ${capability}`);
|
|
113
|
+
}
|
|
114
|
+
return result;
|
|
115
|
+
}
|
|
116
|
+
function read_required_string(value, name) {
|
|
117
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
118
|
+
throw new TypeError(`${name} must be a non-empty string`);
|
|
119
|
+
}
|
|
120
|
+
return value.trim();
|
|
121
|
+
}
|
|
122
|
+
//# sourceMappingURL=bureau-token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bureau-token-store.js","sourceRoot":"","sources":["../../../src/federation/auth/bureau-token-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Ed25519 Key Ring 存储模块。
|
|
3
|
+
*
|
|
4
|
+
* 该模块负责生成、持久化和读取 Federation 用户 token 签名密钥。
|
|
5
|
+
* 私钥只通过 get_active_signing_key() 返回给内部签发路径,JWKS 始终剥离私钥字段。
|
|
6
|
+
*/
|
|
7
|
+
import type { CityTableApi } from "../../store/table-api.js";
|
|
8
|
+
import type { FederationAuthKeyRecord, FederationJwks } from "./types.js";
|
|
9
|
+
/** 当前用户 token 使用的 JOSE 算法。 */
|
|
10
|
+
export declare const USER_TOKEN_ALGORITHM: "EdDSA";
|
|
11
|
+
/** Federation Key Ring 数据访问入口。 */
|
|
12
|
+
export declare class FederationKeyStore {
|
|
13
|
+
private readonly table;
|
|
14
|
+
constructor(table: CityTableApi<FederationAuthKeyRecord>);
|
|
15
|
+
/** 确保 Federation 至少存在一把 active signing key。 */
|
|
16
|
+
ensure_active_key(): Promise<FederationAuthKeyRecord>;
|
|
17
|
+
/** 读取当前唯一 active signing key。 */
|
|
18
|
+
get_active_key(): Promise<FederationAuthKeyRecord | undefined>;
|
|
19
|
+
/** 按 kid 读取可用于验签的密钥。 */
|
|
20
|
+
get_verification_key(key_id: string): Promise<FederationAuthKeyRecord | undefined>;
|
|
21
|
+
/** 返回只包含公开字段的 JWKS。 */
|
|
22
|
+
get_public_jwks(): Promise<FederationJwks>;
|
|
23
|
+
/** 生成并保存新的 active Ed25519 signing key。 */
|
|
24
|
+
private create_active_key;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=federation-key-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federation-key-store.d.ts","sourceRoot":"","sources":["../../../src/federation/auth/federation-key-store.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EACV,uBAAuB,EACvB,cAAc,EAEf,MAAM,YAAY,CAAC;AAEpB,8BAA8B;AAC9B,eAAO,MAAM,oBAAoB,EAAG,OAAgB,CAAC;AAErD,kCAAkC;AAClC,qBAAa,kBAAkB;IACjB,OAAO,CAAC,QAAQ,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY,CAAC,uBAAuB,CAAC;IAEzE,+CAA+C;IACzC,iBAAiB,IAAI,OAAO,CAAC,uBAAuB,CAAC;IAK3D,iCAAiC;IAC3B,cAAc,IAAI,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAQpE,wBAAwB;IAClB,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,uBAAuB,GAAG,SAAS,CAAC;IAOxF,uBAAuB;IACjB,eAAe,IAAI,OAAO,CAAC,cAAc,CAAC;IAQhD,0CAA0C;YAC5B,iBAAiB;CAiChC"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Federation Ed25519 Key Ring 存储模块。
|
|
3
|
+
*
|
|
4
|
+
* 该模块负责生成、持久化和读取 Federation 用户 token 签名密钥。
|
|
5
|
+
* 私钥只通过 get_active_signing_key() 返回给内部签发路径,JWKS 始终剥离私钥字段。
|
|
6
|
+
*/
|
|
7
|
+
import { exportJWK, generateKeyPair } from "jose";
|
|
8
|
+
import { randomSecret } from "../../utils/helpers.js";
|
|
9
|
+
/** 当前用户 token 使用的 JOSE 算法。 */
|
|
10
|
+
export const USER_TOKEN_ALGORITHM = "EdDSA";
|
|
11
|
+
/** Federation Key Ring 数据访问入口。 */
|
|
12
|
+
export class FederationKeyStore {
|
|
13
|
+
table;
|
|
14
|
+
constructor(table) {
|
|
15
|
+
this.table = table;
|
|
16
|
+
}
|
|
17
|
+
/** 确保 Federation 至少存在一把 active signing key。 */
|
|
18
|
+
async ensure_active_key() {
|
|
19
|
+
const active_key = await this.get_active_key();
|
|
20
|
+
return active_key ?? this.create_active_key();
|
|
21
|
+
}
|
|
22
|
+
/** 读取当前唯一 active signing key。 */
|
|
23
|
+
async get_active_key() {
|
|
24
|
+
const rows = await this.table.select({ status: "active" });
|
|
25
|
+
if (rows.length > 1) {
|
|
26
|
+
throw new Error("Federation must have exactly one active user token signing key");
|
|
27
|
+
}
|
|
28
|
+
return rows[0];
|
|
29
|
+
}
|
|
30
|
+
/** 按 kid 读取可用于验签的密钥。 */
|
|
31
|
+
async get_verification_key(key_id) {
|
|
32
|
+
const rows = await this.table.select({ key_id });
|
|
33
|
+
const key = rows[0];
|
|
34
|
+
if (!key || key.status === "revoked")
|
|
35
|
+
return undefined;
|
|
36
|
+
return key;
|
|
37
|
+
}
|
|
38
|
+
/** 返回只包含公开字段的 JWKS。 */
|
|
39
|
+
async get_public_jwks() {
|
|
40
|
+
const rows = await this.table.select();
|
|
41
|
+
const keys = rows
|
|
42
|
+
.filter((row) => row.status !== "revoked")
|
|
43
|
+
.map((row) => parse_public_jwk(row));
|
|
44
|
+
return { keys };
|
|
45
|
+
}
|
|
46
|
+
/** 生成并保存新的 active Ed25519 signing key。 */
|
|
47
|
+
async create_active_key() {
|
|
48
|
+
const existing = await this.get_active_key();
|
|
49
|
+
if (existing)
|
|
50
|
+
return existing;
|
|
51
|
+
const key_id = `key_${randomSecret(16)}`;
|
|
52
|
+
const key_pair = await generateKeyPair(USER_TOKEN_ALGORITHM, {
|
|
53
|
+
crv: "Ed25519",
|
|
54
|
+
extractable: true,
|
|
55
|
+
});
|
|
56
|
+
const public_jwk = await exportJWK(key_pair.publicKey);
|
|
57
|
+
const private_jwk = await exportJWK(key_pair.privateKey);
|
|
58
|
+
const record = {
|
|
59
|
+
key_id,
|
|
60
|
+
algorithm: USER_TOKEN_ALGORITHM,
|
|
61
|
+
public_jwk: JSON.stringify({
|
|
62
|
+
...public_jwk,
|
|
63
|
+
alg: USER_TOKEN_ALGORITHM,
|
|
64
|
+
use: "sig",
|
|
65
|
+
kid: key_id,
|
|
66
|
+
}),
|
|
67
|
+
private_jwk: JSON.stringify({
|
|
68
|
+
...private_jwk,
|
|
69
|
+
alg: USER_TOKEN_ALGORITHM,
|
|
70
|
+
use: "sig",
|
|
71
|
+
kid: key_id,
|
|
72
|
+
}),
|
|
73
|
+
status: "active",
|
|
74
|
+
created_at: new Date().toISOString(),
|
|
75
|
+
retired_at: "",
|
|
76
|
+
};
|
|
77
|
+
await this.table.insert(record);
|
|
78
|
+
return record;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** 将数据库 JSON 转成严格的公开 Ed25519 JWK。 */
|
|
82
|
+
function parse_public_jwk(record) {
|
|
83
|
+
const value = JSON.parse(record.public_jwk);
|
|
84
|
+
if (value.kty !== "OKP" || value.crv !== "Ed25519" || typeof value.x !== "string" || !value.x) {
|
|
85
|
+
throw new Error(`Invalid Federation public JWK: ${record.key_id}`);
|
|
86
|
+
}
|
|
87
|
+
return {
|
|
88
|
+
kty: "OKP",
|
|
89
|
+
crv: "Ed25519",
|
|
90
|
+
alg: USER_TOKEN_ALGORITHM,
|
|
91
|
+
use: "sig",
|
|
92
|
+
kid: record.key_id,
|
|
93
|
+
x: value.x,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
//# sourceMappingURL=federation-key-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"federation-key-store.js","sourceRoot":"","sources":["../../../src/federation/auth/federation-key-store.ts"],"names":[],"mappings":"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"}
|