@downcity/city 0.2.17
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 +182 -0
- package/bin/core/auth/authenticator.d.ts +79 -0
- package/bin/core/auth/authenticator.d.ts.map +1 -0
- package/bin/core/auth/authenticator.js +125 -0
- package/bin/core/auth/authenticator.js.map +1 -0
- package/bin/core/auth/token-signer.d.ts +27 -0
- package/bin/core/auth/token-signer.d.ts.map +1 -0
- package/bin/core/auth/token-signer.js +116 -0
- package/bin/core/auth/token-signer.js.map +1 -0
- package/bin/core/auth/types.d.ts +94 -0
- package/bin/core/auth/types.d.ts.map +1 -0
- package/bin/core/auth/types.js +7 -0
- package/bin/core/auth/types.js.map +1 -0
- package/bin/core/base/base-env-catalog.d.ts +13 -0
- package/bin/core/base/base-env-catalog.d.ts.map +1 -0
- package/bin/core/base/base-env-catalog.js +85 -0
- package/bin/core/base/base-env-catalog.js.map +1 -0
- package/bin/core/base/base-init.d.ts +48 -0
- package/bin/core/base/base-init.d.ts.map +1 -0
- package/bin/core/base/base-init.js +105 -0
- package/bin/core/base/base-init.js.map +1 -0
- package/bin/core/base/base-instruction.d.ts +11 -0
- package/bin/core/base/base-instruction.d.ts.map +1 -0
- package/bin/core/base/base-instruction.js +37 -0
- package/bin/core/base/base-instruction.js.map +1 -0
- package/bin/core/base/base-router.d.ts +41 -0
- package/bin/core/base/base-router.d.ts.map +1 -0
- package/bin/core/base/base-router.js +222 -0
- package/bin/core/base/base-router.js.map +1 -0
- package/bin/core/base/base-runtime.d.ts +13 -0
- package/bin/core/base/base-runtime.d.ts.map +1 -0
- package/bin/core/base/base-runtime.js +116 -0
- package/bin/core/base/base-runtime.js.map +1 -0
- package/bin/core/base/base.d.ts +82 -0
- package/bin/core/base/base.d.ts.map +1 -0
- package/bin/core/base/base.js +156 -0
- package/bin/core/base/base.js.map +1 -0
- package/bin/core/runtime.d.ts +120 -0
- package/bin/core/runtime.d.ts.map +1 -0
- package/bin/core/runtime.js +13 -0
- package/bin/core/runtime.js.map +1 -0
- package/bin/core/types.d.ts +60 -0
- package/bin/core/types.d.ts.map +1 -0
- package/bin/core/types.js +7 -0
- package/bin/core/types.js.map +1 -0
- package/bin/index.d.ts +33 -0
- package/bin/index.d.ts.map +1 -0
- package/bin/index.js +37 -0
- package/bin/index.js.map +1 -0
- package/bin/service/action.d.ts +44 -0
- package/bin/service/action.d.ts.map +1 -0
- package/bin/service/action.js +48 -0
- package/bin/service/action.js.map +1 -0
- package/bin/service/ai/ai-service.d.ts +56 -0
- package/bin/service/ai/ai-service.d.ts.map +1 -0
- package/bin/service/ai/ai-service.js +262 -0
- package/bin/service/ai/ai-service.js.map +1 -0
- package/bin/service/ai/openai-compatible-provider.d.ts +54 -0
- package/bin/service/ai/openai-compatible-provider.d.ts.map +1 -0
- package/bin/service/ai/openai-compatible-provider.js +175 -0
- package/bin/service/ai/openai-compatible-provider.js.map +1 -0
- package/bin/service/ai/provider.d.ts +43 -0
- package/bin/service/ai/provider.d.ts.map +1 -0
- package/bin/service/ai/provider.js +50 -0
- package/bin/service/ai/provider.js.map +1 -0
- package/bin/service/ai/types.d.ts +94 -0
- package/bin/service/ai/types.d.ts.map +1 -0
- package/bin/service/ai/types.js +7 -0
- package/bin/service/ai/types.js.map +1 -0
- package/bin/service/env/env-service.d.ts +12 -0
- package/bin/service/env/env-service.d.ts.map +1 -0
- package/bin/service/env/env-service.js +39 -0
- package/bin/service/env/env-service.js.map +1 -0
- package/bin/service/env/env-store.d.ts +15 -0
- package/bin/service/env/env-store.d.ts.map +1 -0
- package/bin/service/env/env-store.js +35 -0
- package/bin/service/env/env-store.js.map +1 -0
- package/bin/service/env/schema.d.ts +171 -0
- package/bin/service/env/schema.d.ts.map +1 -0
- package/bin/service/env/schema.js +52 -0
- package/bin/service/env/schema.js.map +1 -0
- package/bin/service/env/types.d.ts +79 -0
- package/bin/service/env/types.d.ts.map +1 -0
- package/bin/service/env/types.js +8 -0
- package/bin/service/env/types.js.map +1 -0
- package/bin/service/hook.d.ts +23 -0
- package/bin/service/hook.d.ts.map +1 -0
- package/bin/service/hook.js +49 -0
- package/bin/service/hook.js.map +1 -0
- package/bin/service/installable-service.d.ts +75 -0
- package/bin/service/installable-service.d.ts.map +1 -0
- package/bin/service/installable-service.js +89 -0
- package/bin/service/installable-service.js.map +1 -0
- package/bin/service/instruction.d.ts +106 -0
- package/bin/service/instruction.d.ts.map +1 -0
- package/bin/service/instruction.js +70 -0
- package/bin/service/instruction.js.map +1 -0
- package/bin/service/service.d.ts +140 -0
- package/bin/service/service.d.ts.map +1 -0
- package/bin/service/service.js +94 -0
- package/bin/service/service.js.map +1 -0
- package/bin/service/studios/schema.d.ts +207 -0
- package/bin/service/studios/schema.d.ts.map +1 -0
- package/bin/service/studios/schema.js +60 -0
- package/bin/service/studios/schema.js.map +1 -0
- package/bin/service/studios/studio-store.d.ts +17 -0
- package/bin/service/studios/studio-store.d.ts.map +1 -0
- package/bin/service/studios/studio-store.js +49 -0
- package/bin/service/studios/studio-store.js.map +1 -0
- package/bin/service/studios/studios-service.d.ts +15 -0
- package/bin/service/studios/studios-service.d.ts.map +1 -0
- package/bin/service/studios/studios-service.js +51 -0
- package/bin/service/studios/studios-service.js.map +1 -0
- package/bin/service/studios/types.d.ts +51 -0
- package/bin/service/studios/types.d.ts.map +1 -0
- package/bin/service/studios/types.js +7 -0
- package/bin/service/studios/types.js.map +1 -0
- package/bin/service/types.d.ts +11 -0
- package/bin/service/types.d.ts.map +1 -0
- package/bin/service/types.js +5 -0
- package/bin/service/types.js.map +1 -0
- package/bin/store/db.d.ts +61 -0
- package/bin/store/db.d.ts.map +1 -0
- package/bin/store/db.js +29 -0
- package/bin/store/db.js.map +1 -0
- package/bin/store/table-api.d.ts +42 -0
- package/bin/store/table-api.d.ts.map +1 -0
- package/bin/store/table-api.js +100 -0
- package/bin/store/table-api.js.map +1 -0
- package/bin/store/types.d.ts +14 -0
- package/bin/store/types.d.ts.map +1 -0
- package/bin/store/types.js +7 -0
- package/bin/store/types.js.map +1 -0
- package/bin/utils/helpers.d.ts +101 -0
- package/bin/utils/helpers.d.ts.map +1 -0
- package/bin/utils/helpers.js +192 -0
- package/bin/utils/helpers.js.map +1 -0
- package/bin/utils/validation.d.ts +18 -0
- package/bin/utils/validation.d.ts.map +1 -0
- package/bin/utils/validation.js +28 -0
- package/bin/utils/validation.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio 数据库 schema 模块。
|
|
3
|
+
*
|
|
4
|
+
* 定义 Downcity 内置的 Studio 表结构(SQLite + Postgres)。
|
|
5
|
+
* Studio 是 City 多租户隔离的基本单位。
|
|
6
|
+
*/
|
|
7
|
+
import { pgTable, text as pgText } from "drizzle-orm/pg-core";
|
|
8
|
+
import { sqliteTable, text as sqliteText } from "drizzle-orm/sqlite-core";
|
|
9
|
+
const DEFAULT_STUDIO_TABLE = "studios";
|
|
10
|
+
/**
|
|
11
|
+
* 默认 SQLite Studio 表。
|
|
12
|
+
*/
|
|
13
|
+
export const sqliteStudios = sqliteTable(DEFAULT_STUDIO_TABLE, {
|
|
14
|
+
/**
|
|
15
|
+
* Studio ID。
|
|
16
|
+
*/
|
|
17
|
+
studio_id: sqliteText("studio_id").primaryKey(),
|
|
18
|
+
/**
|
|
19
|
+
* Studio 名称。
|
|
20
|
+
*/
|
|
21
|
+
name: sqliteText("name").notNull(),
|
|
22
|
+
/**
|
|
23
|
+
* Studio 状态。
|
|
24
|
+
*/
|
|
25
|
+
status: sqliteText("status").notNull(),
|
|
26
|
+
/**
|
|
27
|
+
* Studio 创建时间。
|
|
28
|
+
*/
|
|
29
|
+
created_at: sqliteText("created_at").notNull(),
|
|
30
|
+
/**
|
|
31
|
+
* Studio 更新时间。
|
|
32
|
+
*/
|
|
33
|
+
updated_at: sqliteText("updated_at").notNull(),
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* 默认 Postgres Studio 表。
|
|
37
|
+
*/
|
|
38
|
+
export const pgStudios = pgTable(DEFAULT_STUDIO_TABLE, {
|
|
39
|
+
/**
|
|
40
|
+
* Studio ID。
|
|
41
|
+
*/
|
|
42
|
+
studio_id: pgText("studio_id").primaryKey(),
|
|
43
|
+
/**
|
|
44
|
+
* Studio 名称。
|
|
45
|
+
*/
|
|
46
|
+
name: pgText("name").notNull(),
|
|
47
|
+
/**
|
|
48
|
+
* Studio 状态。
|
|
49
|
+
*/
|
|
50
|
+
status: pgText("status").notNull(),
|
|
51
|
+
/**
|
|
52
|
+
* Studio 创建时间。
|
|
53
|
+
*/
|
|
54
|
+
created_at: pgText("created_at").notNull(),
|
|
55
|
+
/**
|
|
56
|
+
* Studio 更新时间。
|
|
57
|
+
*/
|
|
58
|
+
updated_at: pgText("updated_at").notNull(),
|
|
59
|
+
});
|
|
60
|
+
//# sourceMappingURL=schema.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/service/studios/schema.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;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,oBAAoB,GAAG,SAAS,CAAC;AAEvC;;GAEG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,WAAW,CAAC,oBAAoB,EAAE;IAC7D;;OAEG;IACH,SAAS,EAAE,UAAU,CAAC,WAAW,CAAC,CAAC,UAAU,EAAE;IAE/C;;OAEG;IACH,IAAI,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;IAElC;;OAEG;IACH,MAAM,EAAE,UAAU,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IAEtC;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAE9C;;OAEG;IACH,UAAU,EAAE,UAAU,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC/C,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,OAAO,CAAC,oBAAoB,EAAE;IACrD;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,UAAU,EAAE;IAE3C;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE;IAE9B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC,QAAQ,CAAC,CAAC,OAAO,EAAE;IAElC;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;IAE1C;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC,YAAY,CAAC,CAAC,OAAO,EAAE;CAC3C,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio 数据存储。
|
|
3
|
+
*
|
|
4
|
+
* 基于 CityTableApi,不直接依赖 Drizzle。
|
|
5
|
+
*/
|
|
6
|
+
import type { CityTableApi } from "../../store/table-api.js";
|
|
7
|
+
import type { Studio, StudioCreateInput, StudioStatus } from "./types.js";
|
|
8
|
+
export declare class StudioStore {
|
|
9
|
+
private table;
|
|
10
|
+
constructor(table: CityTableApi<Studio>);
|
|
11
|
+
list(): Promise<Studio[]>;
|
|
12
|
+
get(studio_id: string): Promise<Studio | undefined>;
|
|
13
|
+
create(input: StudioCreateInput): Promise<Studio>;
|
|
14
|
+
setStatus(studio_id: string, status: StudioStatus): Promise<Studio>;
|
|
15
|
+
remove(studio_id: string): Promise<void>;
|
|
16
|
+
}
|
|
17
|
+
//# sourceMappingURL=studio-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio-store.d.ts","sourceRoot":"","sources":["../../../src/service/studios/studio-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAE,MAAM,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1E,qBAAa,WAAW;IACV,OAAO,CAAC,KAAK;gBAAL,KAAK,EAAE,YAAY,CAAC,MAAM,CAAC;IAEzC,IAAI,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAIzB,GAAG,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAKnD,MAAM,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAAC,MAAM,CAAC;IAejD,SAAS,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC;IAWnE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAG/C"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio 数据存储。
|
|
3
|
+
*
|
|
4
|
+
* 基于 CityTableApi,不直接依赖 Drizzle。
|
|
5
|
+
*/
|
|
6
|
+
import { randomSecret } from "../../utils/helpers.js";
|
|
7
|
+
export class StudioStore {
|
|
8
|
+
table;
|
|
9
|
+
constructor(table) {
|
|
10
|
+
this.table = table;
|
|
11
|
+
}
|
|
12
|
+
async list() {
|
|
13
|
+
return this.table.select();
|
|
14
|
+
}
|
|
15
|
+
async get(studio_id) {
|
|
16
|
+
const rows = await this.table.select({ studio_id });
|
|
17
|
+
return rows[0];
|
|
18
|
+
}
|
|
19
|
+
async create(input) {
|
|
20
|
+
const name = String(input.name ?? "").trim();
|
|
21
|
+
if (!name)
|
|
22
|
+
throw new TypeError("Studio name is required");
|
|
23
|
+
const now = new Date().toISOString();
|
|
24
|
+
const studio = {
|
|
25
|
+
studio_id: input.studio_id ?? `studio_${randomSecret(12)}`,
|
|
26
|
+
name,
|
|
27
|
+
status: "active",
|
|
28
|
+
created_at: now,
|
|
29
|
+
updated_at: now,
|
|
30
|
+
};
|
|
31
|
+
await this.table.insert(studio);
|
|
32
|
+
return studio;
|
|
33
|
+
}
|
|
34
|
+
async setStatus(studio_id, status) {
|
|
35
|
+
if (status !== "active" && status !== "paused") {
|
|
36
|
+
throw new TypeError(`Invalid studio status: ${String(status)}`);
|
|
37
|
+
}
|
|
38
|
+
const existing = await this.get(studio_id);
|
|
39
|
+
if (!existing)
|
|
40
|
+
throw new Error(`Unknown studio: ${studio_id}`);
|
|
41
|
+
const next = { ...existing, status, updated_at: new Date().toISOString() };
|
|
42
|
+
await this.table.update({ where: { studio_id }, values: next });
|
|
43
|
+
return next;
|
|
44
|
+
}
|
|
45
|
+
async remove(studio_id) {
|
|
46
|
+
await this.table.delete({ studio_id });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=studio-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studio-store.js","sourceRoot":"","sources":["../../../src/service/studios/studio-store.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AAItD,MAAM,OAAO,WAAW;IACF;IAApB,YAAoB,KAA2B;QAA3B,UAAK,GAAL,KAAK,CAAsB;IAAG,CAAC;IAEnD,KAAK,CAAC,IAAI;QACR,OAAO,IAAI,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;IAC7B,CAAC;IAED,KAAK,CAAC,GAAG,CAAC,SAAiB;QACzB,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,EAAqB,CAAC,CAAC;QACvE,OAAO,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,KAAwB;QACnC,MAAM,IAAI,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;QAC7C,IAAI,CAAC,IAAI;YAAE,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAC;QAC1D,MAAM,GAAG,GAAG,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,CAAC;QACrC,MAAM,MAAM,GAAW;YACrB,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,UAAU,YAAY,CAAC,EAAE,CAAC,EAAE;YAC1D,IAAI;YACJ,MAAM,EAAE,QAAQ;YAChB,UAAU,EAAE,GAAG;YACf,UAAU,EAAE,GAAG;SAChB,CAAC;QACF,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QAChC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,SAAiB,EAAE,MAAoB;QACrD,IAAI,MAAM,KAAK,QAAQ,IAAI,MAAM,KAAK,QAAQ,EAAE,CAAC;YAC/C,MAAM,IAAI,SAAS,CAAC,0BAA0B,MAAM,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QAClE,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAC3C,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,SAAS,EAAE,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAW,EAAE,GAAG,QAAQ,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,CAAC;QACnF,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,EAAE,SAAS,EAAqB,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACnF,OAAO,IAAI,CAAC;IACd,CAAC;IAED,KAAK,CAAC,MAAM,CAAC,SAAiB;QAC5B,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,SAAS,EAAqB,CAAC,CAAC;IAC5D,CAAC;CACF"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studios 内置服务。
|
|
3
|
+
*
|
|
4
|
+
* 关键说明(中文)
|
|
5
|
+
* - City 默认注册该 service。
|
|
6
|
+
* - StudioStore 由 City 初始化并注入,确保鉴权和管理 API 使用同一份 studio 状态。
|
|
7
|
+
*/
|
|
8
|
+
import { Service } from "../service.js";
|
|
9
|
+
export declare class StudiosService extends Service {
|
|
10
|
+
private store;
|
|
11
|
+
private auth;
|
|
12
|
+
constructor();
|
|
13
|
+
_onInit(): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=studios-service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studios-service.d.ts","sourceRoot":"","sources":["../../../src/service/studios/studios-service.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,OAAO,EAAE,MAAM,eAAe,CAAC;AAIxC,qBAAa,cAAe,SAAQ,OAAO;IACzC,OAAO,CAAC,KAAK,CAAe;IAC5B,OAAO,CAAC,IAAI,CAAiB;;IAuCvB,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAS/B"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* studios 内置服务。
|
|
3
|
+
*
|
|
4
|
+
* 关键说明(中文)
|
|
5
|
+
* - City 默认注册该 service。
|
|
6
|
+
* - StudioStore 由 City 初始化并注入,确保鉴权和管理 API 使用同一份 studio 状态。
|
|
7
|
+
*/
|
|
8
|
+
import { Service } from "../service.js";
|
|
9
|
+
export class StudiosService extends Service {
|
|
10
|
+
store;
|
|
11
|
+
auth;
|
|
12
|
+
constructor() {
|
|
13
|
+
super({ id: "studios", name: "Studios" });
|
|
14
|
+
this.instruction = [
|
|
15
|
+
"管理 City 中的 studio 实体与用户 token 签发。",
|
|
16
|
+
"studio 是用户侧调用的边界,user_token 会绑定到具体 studio。",
|
|
17
|
+
"管理端通常先创建 studio,再通过 tokens/apply 为某个 user_id 签发可调用的 user_token。",
|
|
18
|
+
].join("\n");
|
|
19
|
+
this.action("list", async () => ({ items: await this.store.list() }), { method: "GET", auth: ["admin"] });
|
|
20
|
+
this.action("create", async (ctx) => {
|
|
21
|
+
return await this.store.create({ name: String(ctx.input.name ?? "") });
|
|
22
|
+
}, { auth: ["admin"] });
|
|
23
|
+
this.action("pause", async (ctx) => {
|
|
24
|
+
return await this.store.setStatus(String(ctx.input.studio_id ?? ""), "paused");
|
|
25
|
+
}, { auth: ["admin"] });
|
|
26
|
+
this.action("activate", async (ctx) => {
|
|
27
|
+
return await this.store.setStatus(String(ctx.input.studio_id ?? ""), "active");
|
|
28
|
+
}, { auth: ["admin"] });
|
|
29
|
+
this.action("remove", async (ctx) => {
|
|
30
|
+
await this.store.remove(String(ctx.input.studio_id ?? ""));
|
|
31
|
+
return { success: true };
|
|
32
|
+
}, { auth: ["admin"] });
|
|
33
|
+
this.action("tokens/apply", async (ctx) => {
|
|
34
|
+
return await this.auth.createToken({
|
|
35
|
+
studio_id: String(ctx.input.studio_id ?? ""),
|
|
36
|
+
user_id: String(ctx.input.user_id ?? ""),
|
|
37
|
+
metadata: ctx.input.metadata,
|
|
38
|
+
ttl: ctx.input.ttl,
|
|
39
|
+
});
|
|
40
|
+
}, { auth: ["admin"] });
|
|
41
|
+
}
|
|
42
|
+
async _onInit() {
|
|
43
|
+
this.store = this._studioStore;
|
|
44
|
+
this.auth = this._authenticator;
|
|
45
|
+
const existing = await this.store.get("studio_downcity");
|
|
46
|
+
if (!existing) {
|
|
47
|
+
await this.store.create({ studio_id: "studio_downcity", name: "Downcity" });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=studios-service.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"studios-service.js","sourceRoot":"","sources":["../../../src/service/studios/studios-service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Studio 层公共类型。
|
|
3
|
+
*
|
|
4
|
+
* Studio 是 City 多租户隔离的基本单位,每个 API 调用都绑定到一个 Studio。
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Studio 当前状态。
|
|
8
|
+
*/
|
|
9
|
+
export type StudioStatus = "active" | "paused";
|
|
10
|
+
/**
|
|
11
|
+
* City 中的 Studio 记录。
|
|
12
|
+
*/
|
|
13
|
+
export interface Studio extends Record<string, unknown> {
|
|
14
|
+
/**
|
|
15
|
+
* Studio 的唯一 ID。
|
|
16
|
+
*/
|
|
17
|
+
studio_id: string;
|
|
18
|
+
/**
|
|
19
|
+
* Studio 展示名称。
|
|
20
|
+
*/
|
|
21
|
+
name: string;
|
|
22
|
+
/**
|
|
23
|
+
* Studio 当前状态。
|
|
24
|
+
*/
|
|
25
|
+
status: StudioStatus;
|
|
26
|
+
/**
|
|
27
|
+
* Studio 创建时间。
|
|
28
|
+
*/
|
|
29
|
+
created_at: string;
|
|
30
|
+
/**
|
|
31
|
+
* Studio 最后更新时间。
|
|
32
|
+
*/
|
|
33
|
+
updated_at: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* 创建 Studio 时的输入。
|
|
37
|
+
*/
|
|
38
|
+
export interface StudioCreateInput {
|
|
39
|
+
/**
|
|
40
|
+
* Studio 展示名称。
|
|
41
|
+
*/
|
|
42
|
+
name: string;
|
|
43
|
+
/**
|
|
44
|
+
* 自定义 Studio ID。
|
|
45
|
+
*
|
|
46
|
+
* 未传入时由 City 自动生成 `studio_${randomSecret(12)}` 格式的 ID。
|
|
47
|
+
* 传入时直接采用该值,便于在种子场景中使用固定 ID。
|
|
48
|
+
*/
|
|
49
|
+
studio_id?: string;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/service/studios/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;GAEG;AACH,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/C;;GAEG;AACH,MAAM,WAAW,MAAO,SAAQ,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IACrD;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,MAAM,EAAE,YAAY,CAAC;IAErB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;;;;OAKG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/service/studios/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/service/types.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAE5C;;;;GAIG;AACH,MAAM,MAAM,MAAM,GAAG,CAAC,GAAG,EAAE,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/service/types.ts"],"names":[],"mappings":"AAAA;;GAEG"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库连接接口模块。
|
|
3
|
+
*
|
|
4
|
+
* 定义 Database / DbClient 抽象接口和 executeDDL 工具函数。
|
|
5
|
+
* 不包含任何运行时特定的数据库驱动;外部只需要传入 Drizzle db。
|
|
6
|
+
*/
|
|
7
|
+
import type { SQL } from "drizzle-orm";
|
|
8
|
+
/**
|
|
9
|
+
* 底层数据库客户端接口。
|
|
10
|
+
*
|
|
11
|
+
* SQLite 用 exec()(同步),D1 用 exec()(异步),Postgres 用 unsafe()(异步)。
|
|
12
|
+
* 适配器只需要提供其中一个方法即可。
|
|
13
|
+
*/
|
|
14
|
+
export interface DbClient {
|
|
15
|
+
/** SQLite / D1 执行 DDL(D1 返回 Promise<void>,SQLite 返回 void) */
|
|
16
|
+
exec?(sql: string): void | Promise<void>;
|
|
17
|
+
/** Postgres 异步执行 DDL */
|
|
18
|
+
unsafe?(sql: string, params?: unknown[]): Promise<unknown>;
|
|
19
|
+
/** 关闭连接(可选) */
|
|
20
|
+
close?(): void;
|
|
21
|
+
/** 关闭连接(异步,可选) */
|
|
22
|
+
end?(): Promise<void>;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Drizzle select / insert / update / delete 的公共子集。
|
|
26
|
+
*
|
|
27
|
+
* 所有 Drizzle 方言实例这 4 个方法签名完全相同,
|
|
28
|
+
* 只是泛型参数不同。此接口用 unknown 替代泛型参数,
|
|
29
|
+
* 返回值用具体类型。各 Store 构造时从 DrizzleDB 转一次。
|
|
30
|
+
*/
|
|
31
|
+
export interface Database {
|
|
32
|
+
select(): {
|
|
33
|
+
from(t: unknown): Promise<Record<string, unknown>[]> | {
|
|
34
|
+
where(c: SQL | undefined): Promise<Record<string, unknown>[]>;
|
|
35
|
+
};
|
|
36
|
+
};
|
|
37
|
+
insert(t: unknown): {
|
|
38
|
+
values(v: Record<string, unknown> | Record<string, unknown>[]): Promise<unknown>;
|
|
39
|
+
};
|
|
40
|
+
update(t: unknown): {
|
|
41
|
+
set(v: Record<string, unknown>): {
|
|
42
|
+
where(c: SQL | undefined): Promise<unknown>;
|
|
43
|
+
};
|
|
44
|
+
};
|
|
45
|
+
delete(t: unknown): {
|
|
46
|
+
where(c: SQL | undefined): Promise<unknown>;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* 执行 DDL 语句。
|
|
51
|
+
*
|
|
52
|
+
* 自动适配同步(SQLite exec)、异步(D1 exec、Postgres unsafe)驱动。
|
|
53
|
+
* D1 的 exec() 返回 Promise,统一 await 兼容所有情况。
|
|
54
|
+
*
|
|
55
|
+
* @param db - 包含 $client 的数据库实例
|
|
56
|
+
* @param ddl - 要执行的 DDL SQL 字符串
|
|
57
|
+
*/
|
|
58
|
+
export declare function executeDDL(db: {
|
|
59
|
+
$client: DbClient;
|
|
60
|
+
}, ddl: string): Promise<void>;
|
|
61
|
+
//# sourceMappingURL=db.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../../src/store/db.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAMvC;;;;;GAKG;AACH,MAAM,WAAW,QAAQ;IACvB,6DAA6D;IAC7D,IAAI,CAAC,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACzC,wBAAwB;IACxB,MAAM,CAAC,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IAC3D,eAAe;IACf,KAAK,CAAC,IAAI,IAAI,CAAC;IACf,kBAAkB;IAClB,GAAG,CAAC,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;CACvB;AAgBD;;;;;;GAMG;AACH,MAAM,WAAW,QAAQ;IACvB,MAAM,IAAI;QAAE,IAAI,CAAC,CAAC,EAAE,OAAO,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,GAAG;YAAE,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;IACvI,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG;QAAE,MAAM,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;IACzG,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG;QAAE,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG;YAAE,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;SAAE,CAAA;KAAE,CAAC;IACzG,MAAM,CAAC,CAAC,EAAE,OAAO,GAAG;QAAE,KAAK,CAAC,CAAC,EAAE,GAAG,GAAG,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;KAAE,CAAC;CACrE;AAMD;;;;;;;;GAQG;AACH,wBAAsB,UAAU,CAAC,EAAE,EAAE;IAAE,OAAO,EAAE,QAAQ,CAAA;CAAE,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAItF"}
|
package/bin/store/db.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库连接接口模块。
|
|
3
|
+
*
|
|
4
|
+
* 定义 Database / DbClient 抽象接口和 executeDDL 工具函数。
|
|
5
|
+
* 不包含任何运行时特定的数据库驱动;外部只需要传入 Drizzle db。
|
|
6
|
+
*/
|
|
7
|
+
// ===========================================================================
|
|
8
|
+
// executeDDL
|
|
9
|
+
// ===========================================================================
|
|
10
|
+
/**
|
|
11
|
+
* 执行 DDL 语句。
|
|
12
|
+
*
|
|
13
|
+
* 自动适配同步(SQLite exec)、异步(D1 exec、Postgres unsafe)驱动。
|
|
14
|
+
* D1 的 exec() 返回 Promise,统一 await 兼容所有情况。
|
|
15
|
+
*
|
|
16
|
+
* @param db - 包含 $client 的数据库实例
|
|
17
|
+
* @param ddl - 要执行的 DDL SQL 字符串
|
|
18
|
+
*/
|
|
19
|
+
export async function executeDDL(db, ddl) {
|
|
20
|
+
const c = db.$client;
|
|
21
|
+
if (typeof c?.exec === "function") {
|
|
22
|
+
await c.exec(ddl);
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (typeof c?.unsafe === "function") {
|
|
26
|
+
await c.unsafe(ddl);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=db.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"db.js","sourceRoot":"","sources":["../../src/store/db.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAqDH,8EAA8E;AAC9E,aAAa;AACb,8EAA8E;AAE9E;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,EAAyB,EAAE,GAAW;IACrE,MAAM,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC;IACrB,IAAI,OAAO,CAAC,EAAE,IAAI,KAAK,UAAU,EAAE,CAAC;QAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAAC,OAAO;IAAC,CAAC;IACjE,IAAI,OAAO,CAAC,EAAE,MAAM,KAAK,UAAU,EAAE,CAAC;QAAC,MAAM,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;IAAC,CAAC;AAC/D,CAAC"}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用表操作模块。
|
|
3
|
+
*
|
|
4
|
+
* 基于 Drizzle query builder 提供动态用户表的 CRUD。
|
|
5
|
+
* 构造时做一次 Database 转换,后续方法零类型断言。
|
|
6
|
+
*/
|
|
7
|
+
import type { AnyPgTable } from "drizzle-orm/pg-core";
|
|
8
|
+
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
9
|
+
import type { Database } from "./db.js";
|
|
10
|
+
export interface CityTableApi<TRow = Record<string, unknown>> {
|
|
11
|
+
/** 表在数据库中的真实名称 */
|
|
12
|
+
readonly name: string;
|
|
13
|
+
/** 原始 Drizzle table 对象 */
|
|
14
|
+
readonly schema: AnySQLiteTable | AnyPgTable;
|
|
15
|
+
/** 读取表数据 */
|
|
16
|
+
select(where?: Partial<TRow>): Promise<TRow[]>;
|
|
17
|
+
/** 插入一行或多行数据 */
|
|
18
|
+
insert(values: Partial<TRow> | Partial<TRow>[]): Promise<void>;
|
|
19
|
+
/** 按 where 等值条件更新数据 */
|
|
20
|
+
update(input: {
|
|
21
|
+
where: Partial<TRow>;
|
|
22
|
+
values: Partial<TRow>;
|
|
23
|
+
}): Promise<number>;
|
|
24
|
+
/** 按 where 等值条件删除数据 */
|
|
25
|
+
delete(where: Partial<TRow>): Promise<number>;
|
|
26
|
+
}
|
|
27
|
+
export declare class TableApi implements CityTableApi {
|
|
28
|
+
readonly name: string;
|
|
29
|
+
readonly schema: AnySQLiteTable | AnyPgTable;
|
|
30
|
+
private readonly db;
|
|
31
|
+
constructor(db: Database, schema: AnySQLiteTable | AnyPgTable);
|
|
32
|
+
select(where?: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
33
|
+
insert(values: Record<string, unknown> | Record<string, unknown>[]): Promise<void>;
|
|
34
|
+
update(input: {
|
|
35
|
+
where: Record<string, unknown>;
|
|
36
|
+
values: Record<string, unknown>;
|
|
37
|
+
}): Promise<number>;
|
|
38
|
+
delete(where: Record<string, unknown>): Promise<number>;
|
|
39
|
+
}
|
|
40
|
+
/** 用户表建表 DDL */
|
|
41
|
+
export declare function buildCreateUserTableSQL(table: AnySQLiteTable | AnyPgTable): string;
|
|
42
|
+
//# sourceMappingURL=table-api.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-api.d.ts","sourceRoot":"","sources":["../../src/store/table-api.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAQ9D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOxC,MAAM,WAAW,YAAY,CAAC,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;IAC1D,kBAAkB;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAAC;IAC7C,YAAY;IACZ,MAAM,CAAC,KAAK,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC;IAC/C,gBAAgB;IAChB,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/D,uBAAuB;IACvB,MAAM,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IAChF,uBAAuB;IACvB,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;CAC/C;AAMD,qBAAa,QAAS,YAAW,YAAY;IAC3C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAAC;IAE7C,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAW;gBAElB,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,cAAc,GAAG,UAAU;IAMvD,MAAM,CAAC,KAAK,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC;IAS/E,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAMlF,MAAM,CAAC,KAAK,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QAC/B,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;KACjC,GAAG,OAAO,CAAC,MAAM,CAAC;IAWb,MAAM,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;CAM9D;AAMD,gBAAgB;AAChB,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,cAAc,GAAG,UAAU,GAAG,MAAM,CA4BlF"}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 通用表操作模块。
|
|
3
|
+
*
|
|
4
|
+
* 基于 Drizzle query builder 提供动态用户表的 CRUD。
|
|
5
|
+
* 构造时做一次 Database 转换,后续方法零类型断言。
|
|
6
|
+
*/
|
|
7
|
+
import { getTableColumns, getTableName, eq, and, } from "drizzle-orm";
|
|
8
|
+
// ===========================================================================
|
|
9
|
+
// TableApi — 动态用户表 CRUD
|
|
10
|
+
// ===========================================================================
|
|
11
|
+
export class TableApi {
|
|
12
|
+
name;
|
|
13
|
+
schema;
|
|
14
|
+
db;
|
|
15
|
+
constructor(db, schema) {
|
|
16
|
+
this.db = db;
|
|
17
|
+
this.schema = schema;
|
|
18
|
+
this.name = getTableName(schema);
|
|
19
|
+
}
|
|
20
|
+
async select(where = {}) {
|
|
21
|
+
const cond = buildCondition(this.schema, where);
|
|
22
|
+
const query = this.db.select().from(this.schema);
|
|
23
|
+
const rows = cond
|
|
24
|
+
? await query.where(cond)
|
|
25
|
+
: await query;
|
|
26
|
+
return rows.map((row) => ({ ...row }));
|
|
27
|
+
}
|
|
28
|
+
async insert(values) {
|
|
29
|
+
const rows = Array.isArray(values) ? values : [values];
|
|
30
|
+
if (rows.length === 0)
|
|
31
|
+
throw new TypeError("insert() values cannot be empty");
|
|
32
|
+
await this.db.insert(this.schema).values(rows);
|
|
33
|
+
}
|
|
34
|
+
async update(input) {
|
|
35
|
+
if (Object.keys(input.values).length === 0) {
|
|
36
|
+
throw new TypeError("update() values cannot be empty");
|
|
37
|
+
}
|
|
38
|
+
const cond = buildCondition(this.schema, input.where);
|
|
39
|
+
if (!cond)
|
|
40
|
+
throw new TypeError("update() where cannot be empty");
|
|
41
|
+
// Drizzle update 返回类型因方言而异
|
|
42
|
+
const result = await this.db.update(this.schema).set(input.values).where(cond);
|
|
43
|
+
return typeof result.changes === "number" ? result.changes : Array.isArray(result) ? result.length : 0;
|
|
44
|
+
}
|
|
45
|
+
async delete(where) {
|
|
46
|
+
const cond = buildCondition(this.schema, where);
|
|
47
|
+
if (!cond)
|
|
48
|
+
throw new TypeError("delete() where cannot be empty");
|
|
49
|
+
const result = await this.db.delete(this.schema).where(cond);
|
|
50
|
+
return typeof result.changes === "number" ? result.changes : Array.isArray(result) ? result.length : 0;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
// ===========================================================================
|
|
54
|
+
// DDL 生成(仅在 store init 时使用)
|
|
55
|
+
// ===========================================================================
|
|
56
|
+
/** 用户表建表 DDL */
|
|
57
|
+
export function buildCreateUserTableSQL(table) {
|
|
58
|
+
const tableName = getTableName(table);
|
|
59
|
+
const colDefs = [];
|
|
60
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
61
|
+
for (const key of Object.keys(table)) {
|
|
62
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
63
|
+
const col = table[key];
|
|
64
|
+
if (!col || typeof col.name !== "string")
|
|
65
|
+
continue;
|
|
66
|
+
const name = String(col.name);
|
|
67
|
+
const dataType = String(col.dataType ?? "string");
|
|
68
|
+
const isPrimary = Boolean(col.primary ?? false);
|
|
69
|
+
const isNotNull = Boolean(col.notNull ?? false);
|
|
70
|
+
const sqlType = dataType === "number" ? "INTEGER"
|
|
71
|
+
: dataType === "boolean" ? "INTEGER"
|
|
72
|
+
: dataType === "json" ? "TEXT"
|
|
73
|
+
: "TEXT";
|
|
74
|
+
const parts = [`"${name}"`, sqlType];
|
|
75
|
+
if (isPrimary)
|
|
76
|
+
parts.push("PRIMARY KEY");
|
|
77
|
+
if (isNotNull && !isPrimary)
|
|
78
|
+
parts.push("NOT NULL");
|
|
79
|
+
colDefs.push(parts.join(" "));
|
|
80
|
+
}
|
|
81
|
+
if (colDefs.length === 0)
|
|
82
|
+
return "";
|
|
83
|
+
return `CREATE TABLE IF NOT EXISTS "${tableName}" (${colDefs.join(", ")})`;
|
|
84
|
+
}
|
|
85
|
+
// ===========================================================================
|
|
86
|
+
// 内部辅助
|
|
87
|
+
// ===========================================================================
|
|
88
|
+
function buildCondition(table, where) {
|
|
89
|
+
const entries = Object.entries(where);
|
|
90
|
+
if (entries.length === 0)
|
|
91
|
+
return undefined;
|
|
92
|
+
const columns = getTableColumns(table);
|
|
93
|
+
return and(...entries.map(([key, value]) => {
|
|
94
|
+
const col = columns[key];
|
|
95
|
+
if (!col)
|
|
96
|
+
throw new TypeError(`Unknown column for ${getTableName(table)}: ${key}`);
|
|
97
|
+
return eq(col, value);
|
|
98
|
+
}));
|
|
99
|
+
}
|
|
100
|
+
//# sourceMappingURL=table-api.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"table-api.js","sourceRoot":"","sources":["../../src/store/table-api.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 数据库 schema 相关类型模块。
|
|
3
|
+
*
|
|
4
|
+
* 定义 City 和用户业务表的数据库 schema 输入类型。
|
|
5
|
+
*/
|
|
6
|
+
import type { AnyPgTable } from "drizzle-orm/pg-core";
|
|
7
|
+
import type { AnySQLiteTable } from "drizzle-orm/sqlite-core";
|
|
8
|
+
/**
|
|
9
|
+
* 用户传给 City 的业务数据库 schema。
|
|
10
|
+
*
|
|
11
|
+
* key 是业务表在 `city.table(key)` 中使用的名称,value 是 Drizzle table 对象。
|
|
12
|
+
*/
|
|
13
|
+
export type CityUserSchemaInput = Record<string, AnySQLiteTable | AnyPgTable>;
|
|
14
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,qBAAqB,CAAC;AACtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAE9D;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,cAAc,GAAG,UAAU,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/store/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|