@ai0x0/utils 0.5.0 → 0.6.1
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/es/backend/actions/create-delete-action.d.ts +2 -3
- package/es/backend/actions/create-delete-action.js +8 -44
- package/es/backend/actions/create-put-action.d.ts +1 -1
- package/es/backend/actions/create-put-action.js +15 -39
- package/es/backend/actions/get-list-query-core.d.ts +46 -0
- package/es/backend/actions/get-list-query-core.js +236 -0
- package/es/backend/actions/get-list-query.d.ts +5 -18
- package/es/backend/actions/get-list-query.js +20 -220
- package/es/backend/actions/write-action-core.d.ts +18 -0
- package/es/backend/actions/write-action-core.js +119 -0
- package/es/backend/route-operation/operation-common.d.ts +13 -1
- package/es/backend/route-operation/operation-common.js +25 -0
- package/es/backend/route-operation/read-operation-core.js +46 -33
- package/es/backend/route-operation/write-operation-core.d.ts +3 -1
- package/es/backend/route-operation/write-operation-core.js +69 -26
- package/es/backend/schemas/common.d.ts +112 -0
- package/es/backend/schemas/common.js +158 -0
- package/es/backend/schemas/index.d.ts +735 -364
- package/es/backend/schemas/index.js +8 -139
- package/es/backend/sqlite/actions/create-delete-action.d.ts +1 -2
- package/es/backend/sqlite/actions/create-delete-action.js +7 -43
- package/es/backend/sqlite/actions/create-put-action.d.ts +1 -1
- package/es/backend/sqlite/actions/create-put-action.js +13 -36
- package/es/backend/sqlite/actions/get-list-query.d.ts +4 -9
- package/es/backend/sqlite/actions/get-list-query.js +20 -217
- package/es/backend/sqlite/errors.d.ts +1 -4
- package/es/backend/sqlite/errors.js +6 -31
- package/es/backend/sqlite/schemas/index.d.ts +1 -111
- package/es/backend/sqlite/schemas/index.js +8 -139
- package/lib/backend/actions/create-delete-action.d.ts +2 -3
- package/lib/backend/actions/create-delete-action.js +2 -14
- package/lib/backend/actions/create-put-action.d.ts +1 -1
- package/lib/backend/actions/create-put-action.js +3 -16
- package/lib/backend/actions/get-list-query-core.d.ts +46 -0
- package/lib/backend/actions/get-list-query-core.js +151 -0
- package/lib/backend/actions/get-list-query.d.ts +5 -18
- package/lib/backend/actions/get-list-query.js +16 -141
- package/lib/backend/actions/write-action-core.d.ts +18 -0
- package/lib/backend/actions/write-action-core.js +70 -0
- package/lib/backend/route-operation/operation-common.d.ts +13 -1
- package/lib/backend/route-operation/operation-common.js +8 -0
- package/lib/backend/route-operation/read-operation-core.js +14 -4
- package/lib/backend/route-operation/write-operation-core.d.ts +3 -1
- package/lib/backend/route-operation/write-operation-core.js +24 -4
- package/lib/backend/schemas/common.d.ts +112 -0
- package/lib/backend/schemas/common.js +112 -0
- package/lib/backend/schemas/index.d.ts +735 -364
- package/lib/backend/schemas/index.js +12 -83
- package/lib/backend/sqlite/actions/create-delete-action.d.ts +1 -2
- package/lib/backend/sqlite/actions/create-delete-action.js +2 -14
- package/lib/backend/sqlite/actions/create-put-action.d.ts +1 -1
- package/lib/backend/sqlite/actions/create-put-action.js +3 -16
- package/lib/backend/sqlite/actions/get-list-query.d.ts +4 -9
- package/lib/backend/sqlite/actions/get-list-query.js +16 -141
- package/lib/backend/sqlite/errors.d.ts +1 -4
- package/lib/backend/sqlite/errors.js +2 -8
- package/lib/backend/sqlite/schemas/index.d.ts +1 -111
- package/lib/backend/sqlite/schemas/index.js +12 -83
- package/package.json +1 -1
|
@@ -6,11 +6,11 @@ var __export = (target, all) => {
|
|
|
6
6
|
for (var name in all)
|
|
7
7
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
8
|
};
|
|
9
|
-
var __copyProps = (to, from, except,
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
10
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
11
|
for (let key of __getOwnPropNames(from))
|
|
12
12
|
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
-
__defProp(to, key, { get: () => from[key], enumerable: !(
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
14
|
}
|
|
15
15
|
return to;
|
|
16
16
|
};
|
|
@@ -23,145 +23,20 @@ __export(get_list_query_exports, {
|
|
|
23
23
|
});
|
|
24
24
|
module.exports = __toCommonJS(get_list_query_exports);
|
|
25
25
|
var import_drizzle_orm = require("drizzle-orm");
|
|
26
|
-
var
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
jsonArrayFields
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
orderDir = "desc",
|
|
41
|
-
...filters
|
|
42
|
-
} = params;
|
|
43
|
-
function buildBaseQuery(table2, fields2, relations2) {
|
|
44
|
-
let queryFields = { ...fields2 };
|
|
45
|
-
if (relations2 == null ? void 0 : relations2.length) {
|
|
46
|
-
for (const { select } of relations2) {
|
|
47
|
-
queryFields = { ...queryFields, ...select };
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
const query2 = db.select(queryFields).from(table2);
|
|
51
|
-
if (relations2 == null ? void 0 : relations2.length) {
|
|
52
|
-
query2.groupBy(
|
|
53
|
-
table2.id,
|
|
54
|
-
...relations2.filter(({ groupBy }) => groupBy).filter(({ table: table3 }) => table3).map(({ table: table3 }) => table3.id)
|
|
55
|
-
);
|
|
56
|
-
}
|
|
57
|
-
return query2;
|
|
58
|
-
}
|
|
59
|
-
function findTargetColumn(key, table2, relations2) {
|
|
60
|
-
var _a;
|
|
61
|
-
return (_a = [table2].concat(
|
|
62
|
-
(relations2 == null ? void 0 : relations2.filter(({ table: table3 }) => table3).map(({ table: table3 }) => table3)) || []
|
|
63
|
-
).find((table3) => table3[key])) == null ? void 0 : _a[key];
|
|
64
|
-
}
|
|
65
|
-
function addCondition(conditions2, key, value, targetColumn) {
|
|
66
|
-
const isIdField = /id/i.test(key);
|
|
67
|
-
const isJsonArray = jsonArrayFields == null ? void 0 : jsonArrayFields.includes(key);
|
|
68
|
-
if (typeof value === "string") {
|
|
69
|
-
if (value.includes(",")) {
|
|
70
|
-
const values = value.split(",").map((val) => val.trim()).filter(Boolean);
|
|
71
|
-
if (values.length > 0) {
|
|
72
|
-
if (isJsonArray) {
|
|
73
|
-
conditions2.push(
|
|
74
|
-
import_drizzle_orm.sql`
|
|
75
|
-
EXISTS (
|
|
76
|
-
SELECT 1 FROM jsonb_array_elements_text(${targetColumn}) tag
|
|
77
|
-
WHERE tag LIKE ${`%${values[0]}%`}
|
|
78
|
-
)
|
|
79
|
-
`
|
|
80
|
-
);
|
|
81
|
-
} else if (isIdField) {
|
|
82
|
-
conditions2.push((0, import_drizzle_orm.inArray)(targetColumn, values));
|
|
83
|
-
} else {
|
|
84
|
-
conditions2.push(
|
|
85
|
-
(0, import_drizzle_orm.or)(
|
|
86
|
-
...values.map((val) => (0, import_drizzle_orm.ilike)(targetColumn, `%${val}%`))
|
|
87
|
-
)
|
|
88
|
-
);
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
if (isJsonArray) {
|
|
93
|
-
conditions2.push(
|
|
94
|
-
import_drizzle_orm.sql`
|
|
95
|
-
EXISTS (
|
|
96
|
-
SELECT 1 FROM jsonb_array_elements_text(${targetColumn}) tag
|
|
97
|
-
WHERE tag LIKE ${`%${value}%`}
|
|
98
|
-
)
|
|
99
|
-
`
|
|
100
|
-
);
|
|
101
|
-
} else if (isIdField) {
|
|
102
|
-
conditions2.push((0, import_drizzle_orm.eq)(targetColumn, value));
|
|
103
|
-
} else {
|
|
104
|
-
conditions2.push((0, import_drizzle_orm.ilike)(targetColumn, `%${value}%`));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
} else {
|
|
108
|
-
conditions2.push((0, import_drizzle_orm.eq)(targetColumn, value));
|
|
109
|
-
}
|
|
110
|
-
}
|
|
111
|
-
function buildConditions(filters2, table2, relations2) {
|
|
112
|
-
const conditions2 = [];
|
|
113
|
-
for (const [key, value] of Object.entries(filters2)) {
|
|
114
|
-
if (!value) {
|
|
115
|
-
continue;
|
|
116
|
-
}
|
|
117
|
-
if (key.endsWith("AtFrom") || key.endsWith("AtTo")) {
|
|
118
|
-
const baseFieldName = key.replace(/(AtFrom|AtTo)$/, "");
|
|
119
|
-
const targetColumn2 = findTargetColumn(
|
|
120
|
-
`${baseFieldName}At`,
|
|
121
|
-
table2,
|
|
122
|
-
relations2
|
|
123
|
-
);
|
|
124
|
-
if (!targetColumn2) {
|
|
125
|
-
continue;
|
|
126
|
-
}
|
|
127
|
-
if (key.endsWith("AtFrom")) {
|
|
128
|
-
conditions2.push((0, import_drizzle_orm.gte)(targetColumn2, new Date(value)));
|
|
129
|
-
} else if (key.endsWith("AtTo")) {
|
|
130
|
-
conditions2.push((0, import_drizzle_orm.lte)(targetColumn2, new Date(value)));
|
|
131
|
-
}
|
|
132
|
-
continue;
|
|
133
|
-
}
|
|
134
|
-
const targetColumn = findTargetColumn(
|
|
135
|
-
key,
|
|
136
|
-
table2,
|
|
137
|
-
relations2
|
|
138
|
-
);
|
|
139
|
-
if (!targetColumn) {
|
|
140
|
-
continue;
|
|
141
|
-
}
|
|
142
|
-
addCondition(conditions2, key, value, targetColumn);
|
|
143
|
-
}
|
|
144
|
-
return conditions2;
|
|
145
|
-
}
|
|
146
|
-
function buildOrderBy(table2, orderBy2, orderDir2) {
|
|
147
|
-
const orderColumn = table2[orderBy2];
|
|
148
|
-
return orderDir2 === "asc" ? (0, import_drizzle_orm.asc)(orderColumn) : (0, import_drizzle_orm.desc)(orderColumn);
|
|
149
|
-
}
|
|
150
|
-
const baseQuery = buildBaseQuery(table, fields, relations);
|
|
151
|
-
const scopeSql = (0, import_scope.scopeCondition)(table, scope);
|
|
152
|
-
const conditions = scopeSql ? [scopeSql] : [];
|
|
153
|
-
conditions.push(...buildConditions(filters, table, relations));
|
|
154
|
-
const query = baseQuery.where(conditions.length > 0 ? (0, import_drizzle_orm.and)(...conditions) : void 0).orderBy(buildOrderBy(table, orderBy, orderDir)).limit(pageSize).offset((page - 1) * pageSize);
|
|
155
|
-
const countQuery = db.select({ count: import_drizzle_orm.sql`COUNT(DISTINCT ${table.id})` }).from(table).where(conditions.length > 0 ? (0, import_drizzle_orm.and)(...conditions) : void 0);
|
|
156
|
-
if (relations == null ? void 0 : relations.length) {
|
|
157
|
-
for (const { table: relationTable, sql: joinSql } of relations) {
|
|
158
|
-
if (relationTable) {
|
|
159
|
-
query.leftJoin(relationTable, joinSql);
|
|
160
|
-
countQuery.leftJoin(relationTable, joinSql);
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
return { countQuery, query };
|
|
26
|
+
var import_get_list_query_core = require("./get-list-query-core");
|
|
27
|
+
var run = (0, import_get_list_query_core.createGetListQuery)({
|
|
28
|
+
contains: (column, keyword) => (0, import_drizzle_orm.ilike)(column, `%${keyword}%`),
|
|
29
|
+
// jsonb 数组展开成行再逐个 LIKE。列存的是 text[] 形状的 jsonb(见 createTableSchema 的
|
|
30
|
+
// jsonArrayFields),所以用 _text 那个变体,取到的 tag 直接就是字符串。
|
|
31
|
+
jsonArrayContains: (column, keyword) => import_drizzle_orm.sql`
|
|
32
|
+
EXISTS (
|
|
33
|
+
SELECT 1 FROM jsonb_array_elements_text(${column}) tag
|
|
34
|
+
WHERE tag LIKE ${`%${keyword}%`}
|
|
35
|
+
)
|
|
36
|
+
`
|
|
37
|
+
});
|
|
38
|
+
function getListQuery(args) {
|
|
39
|
+
return run(args);
|
|
165
40
|
}
|
|
166
41
|
// Annotate the CommonJS export names for ESM import in node:
|
|
167
42
|
0 && (module.exports = {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ScopeArg } from "../scope.js";
|
|
2
|
+
/** 核心用得到表的哪一部分:只有 id。dialect 各自的 Table 类型不进来。 */
|
|
3
|
+
export interface WriteActionTable {
|
|
4
|
+
id: unknown;
|
|
5
|
+
}
|
|
6
|
+
interface WriteActionDeps<TTable extends WriteActionTable> {
|
|
7
|
+
db: any;
|
|
8
|
+
table: TTable;
|
|
9
|
+
}
|
|
10
|
+
export declare function createPutActionCore<TTable extends WriteActionTable>({ db, table, }: WriteActionDeps<TTable>): (body: Record<string, unknown>, { scope }: {
|
|
11
|
+
scope: ScopeArg;
|
|
12
|
+
}) => Promise<unknown>;
|
|
13
|
+
export declare function createDeleteActionCore<TTable extends WriteActionTable>({ db, table, }: WriteActionDeps<TTable>): ({ id }: {
|
|
14
|
+
id: string;
|
|
15
|
+
}, { scope }: {
|
|
16
|
+
scope: ScopeArg;
|
|
17
|
+
}) => Promise<any>;
|
|
18
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/backend/actions/write-action-core.ts
|
|
20
|
+
var write_action_core_exports = {};
|
|
21
|
+
__export(write_action_core_exports, {
|
|
22
|
+
createDeleteActionCore: () => createDeleteActionCore,
|
|
23
|
+
createPutActionCore: () => createPutActionCore
|
|
24
|
+
});
|
|
25
|
+
module.exports = __toCommonJS(write_action_core_exports);
|
|
26
|
+
var import_drizzle_orm = require("drizzle-orm");
|
|
27
|
+
var import_scope = require("../scope");
|
|
28
|
+
var import_errors = require("../errors");
|
|
29
|
+
var import_transform_body = require("./transform-body");
|
|
30
|
+
function byId(table, id, scope) {
|
|
31
|
+
const guard = (0, import_scope.scopeCondition)(table, scope);
|
|
32
|
+
const same = (0, import_drizzle_orm.eq)(table.id, id);
|
|
33
|
+
return guard ? (0, import_drizzle_orm.and)(same, guard) : same;
|
|
34
|
+
}
|
|
35
|
+
function createPutActionCore({
|
|
36
|
+
db,
|
|
37
|
+
table
|
|
38
|
+
}) {
|
|
39
|
+
return async (body, { scope }) => {
|
|
40
|
+
const id = body.id;
|
|
41
|
+
if (!id) {
|
|
42
|
+
throw new import_errors.HttpError(400, "缺少 id");
|
|
43
|
+
}
|
|
44
|
+
const [data] = await db.update(table).set((0, import_transform_body.transformBody)(body)).where(byId(table, id, scope)).returning();
|
|
45
|
+
if (!data && scope !== import_scope.NO_SCOPE) {
|
|
46
|
+
throw new import_errors.HttpError(404, "未找到编辑对象,或没有权限");
|
|
47
|
+
}
|
|
48
|
+
return data;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function createDeleteActionCore({
|
|
52
|
+
db,
|
|
53
|
+
table
|
|
54
|
+
}) {
|
|
55
|
+
return async ({ id }, { scope }) => {
|
|
56
|
+
if (!id) {
|
|
57
|
+
throw new import_errors.HttpError(400, "缺少 id");
|
|
58
|
+
}
|
|
59
|
+
const rows = await db.delete(table).where(byId(table, id, scope)).returning();
|
|
60
|
+
if (rows.length === 0 && scope !== import_scope.NO_SCOPE) {
|
|
61
|
+
throw new import_errors.HttpError(404, "未找到删除对象,或没有权限");
|
|
62
|
+
}
|
|
63
|
+
return rows;
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
67
|
+
0 && (module.exports = {
|
|
68
|
+
createDeleteActionCore,
|
|
69
|
+
createPutActionCore
|
|
70
|
+
});
|
|
@@ -8,6 +8,18 @@ export type RouteCatch = (error: Error) => Promise<JsonResponse | undefined>;
|
|
|
8
8
|
export interface DefaultSession {
|
|
9
9
|
userId?: string;
|
|
10
10
|
}
|
|
11
|
+
/**
|
|
12
|
+
* 「这次请求是谁发的」。四个方法都会把它盖进 params.callerId,自定义 handler 判权、署名、
|
|
13
|
+
* 判「是不是本人」一律读它。
|
|
14
|
+
*
|
|
15
|
+
* **不要拿 params.creatorId 当调用者用。** 那是另一件事:POST 上的 creatorId 是**归属戳**
|
|
16
|
+
* (access.scope 指的那一列,换成 ownerId 时它就不叫 creatorId 了),PUT 上的 editorId 是要
|
|
17
|
+
* 写进行里的**审计列**。两者都恰好等于调用者只是默认配置下的巧合。
|
|
18
|
+
*
|
|
19
|
+
* 会话形状是泛型的(调用方可以塞自己的 Scope 进来),所以这里按 DefaultSession 取 userId ——
|
|
20
|
+
* 自定义会话只要带着这个字段就通用。
|
|
21
|
+
*/
|
|
22
|
+
export declare function callerIdOf(session: unknown): string | undefined;
|
|
11
23
|
/**
|
|
12
24
|
* 会话读取器。泛型化是为了让调用方能在这里一次性解析出「你是谁 + 你现在站在哪个空间 +
|
|
13
25
|
* 你在那个空间里是什么角色」,后面 access.scope / access.can 都读同一份,不必各自再查一遍库。
|
|
@@ -99,7 +111,7 @@ export declare function getDefaultTags<TTable>({ getTableName, table, }: {
|
|
|
99
111
|
table?: TTable;
|
|
100
112
|
}): string[];
|
|
101
113
|
export type GetOperationParams<Q extends ZodSchema> = z.infer<Q> & Record<string, unknown> & {
|
|
102
|
-
|
|
114
|
+
callerId?: string;
|
|
103
115
|
};
|
|
104
116
|
/**
|
|
105
117
|
* 读操作的参数与作用域。
|
|
@@ -19,6 +19,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
19
19
|
// src/backend/route-operation/operation-common.ts
|
|
20
20
|
var operation_common_exports = {};
|
|
21
21
|
__export(operation_common_exports, {
|
|
22
|
+
callerIdOf: () => callerIdOf,
|
|
22
23
|
getDefaultTags: () => getDefaultTags,
|
|
23
24
|
getReadParams: () => getReadParams,
|
|
24
25
|
handleOperationError: () => handleOperationError,
|
|
@@ -30,6 +31,9 @@ module.exports = __toCommonJS(operation_common_exports);
|
|
|
30
31
|
var import_next_rest_framework = require("next-rest-framework");
|
|
31
32
|
var import_errors = require("../errors");
|
|
32
33
|
var import_scope = require("../scope");
|
|
34
|
+
function callerIdOf(session) {
|
|
35
|
+
return session == null ? void 0 : session.userId;
|
|
36
|
+
}
|
|
33
37
|
var DEFAULT_SCOPE_COLUMN = "creatorId";
|
|
34
38
|
function sessionLoader(getSession, req) {
|
|
35
39
|
let pending;
|
|
@@ -124,6 +128,9 @@ async function getReadParams({
|
|
|
124
128
|
Object.fromEntries(new URL(req.url).searchParams)
|
|
125
129
|
);
|
|
126
130
|
return {
|
|
131
|
+
// 这一份**不带 callerId**:它会被当成筛选条件送进查询构造器,而 callerId 不是任何表的列。
|
|
132
|
+
// 列表那边认不出的键会跳过(get-list-query 的 `if (!targetColumn) continue`),但别指望
|
|
133
|
+
// 这条兜底 —— 交给 handler 的那一份在 read-operation-core 里单独拼,见那里。
|
|
127
134
|
params: { ...queryParams, ...extraParams },
|
|
128
135
|
scope,
|
|
129
136
|
session
|
|
@@ -131,6 +138,7 @@ async function getReadParams({
|
|
|
131
138
|
}
|
|
132
139
|
// Annotate the CommonJS export names for ESM import in node:
|
|
133
140
|
0 && (module.exports = {
|
|
141
|
+
callerIdOf,
|
|
134
142
|
getDefaultTags,
|
|
135
143
|
getReadParams,
|
|
136
144
|
handleOperationError,
|
|
@@ -60,7 +60,11 @@ function createGetOperationFactory({
|
|
|
60
60
|
}
|
|
61
61
|
]).handler(async (req) => {
|
|
62
62
|
try {
|
|
63
|
-
const {
|
|
63
|
+
const {
|
|
64
|
+
params: mergedParams,
|
|
65
|
+
scope,
|
|
66
|
+
session
|
|
67
|
+
} = await (0, import_operation_common.getReadParams)({
|
|
64
68
|
access,
|
|
65
69
|
getSession,
|
|
66
70
|
req,
|
|
@@ -78,7 +82,8 @@ function createGetOperationFactory({
|
|
|
78
82
|
if (handler) {
|
|
79
83
|
result = await handler({
|
|
80
84
|
data: result,
|
|
81
|
-
params
|
|
85
|
+
// callerId 只给 handler,不进上面那份筛选/写入用的 params(见 getReadParams)。
|
|
86
|
+
params: { ...mergedParams, callerId: (0, import_operation_common.callerIdOf)(session) },
|
|
82
87
|
req
|
|
83
88
|
});
|
|
84
89
|
}
|
|
@@ -121,7 +126,11 @@ function createGetListOperationFactory({
|
|
|
121
126
|
}
|
|
122
127
|
]).handler(async (req) => {
|
|
123
128
|
try {
|
|
124
|
-
const {
|
|
129
|
+
const {
|
|
130
|
+
params: mergedParams,
|
|
131
|
+
scope,
|
|
132
|
+
session
|
|
133
|
+
} = await (0, import_operation_common.getReadParams)({
|
|
125
134
|
access,
|
|
126
135
|
getSession,
|
|
127
136
|
req,
|
|
@@ -138,7 +147,8 @@ function createGetListOperationFactory({
|
|
|
138
147
|
if (handler) {
|
|
139
148
|
result = await handler({
|
|
140
149
|
data: result,
|
|
141
|
-
params
|
|
150
|
+
// callerId 只给 handler,不进上面那份筛选/写入用的 params(见 getReadParams)。
|
|
151
|
+
params: { ...mergedParams, callerId: (0, import_operation_common.callerIdOf)(session) },
|
|
142
152
|
req
|
|
143
153
|
});
|
|
144
154
|
}
|
|
@@ -5,6 +5,7 @@ import { type RouteOpenApiOperation } from "./open-api-operation.js";
|
|
|
5
5
|
import { AccessOptions, ActionFactory, DefaultSession, RouteCatch, SessionGetter } from "./operation-common.js";
|
|
6
6
|
import { ScopeArg } from "../scope.js";
|
|
7
7
|
export type PostOperationParams<IB extends ZodSchema> = z.infer<IB> & Record<string, unknown> & {
|
|
8
|
+
callerId?: string;
|
|
8
9
|
creatorId?: string;
|
|
9
10
|
};
|
|
10
11
|
export interface PostOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable, TSession = DefaultSession> {
|
|
@@ -37,6 +38,7 @@ export declare function createPostOperationFactory<TTable>({ createAction, getTa
|
|
|
37
38
|
getSession: SessionGetter<TSession>;
|
|
38
39
|
}) => <IB extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, OB extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>({ schemas, access, contentType, openApiOperation, parseBody, setBody, table, handler, catch: catchHandler, }: PostOperationOptions<IB, OB, TTable, TSession>) => import("next-rest-framework").RouteOperationDefinition<"POST">;
|
|
39
40
|
export type PutOperationParams<IB extends ZodSchema> = z.infer<IB> & Record<string, unknown> & {
|
|
41
|
+
callerId?: string;
|
|
40
42
|
editorId?: string;
|
|
41
43
|
};
|
|
42
44
|
export interface PutOperationOptions<IB extends ZodSchema, OB extends ZodSchema, TTable, TSession = DefaultSession> {
|
|
@@ -65,7 +67,7 @@ export declare function createPutOperationFactory<TTable>({ createAction, getTab
|
|
|
65
67
|
getSession: SessionGetter<TSession>;
|
|
66
68
|
}) => <IB extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>, OB extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>({ schemas, access, openApiOperation, table, setBody, handler, catch: catchHandler, }: PutOperationOptions<IB, OB, TTable, TSession>) => import("next-rest-framework").RouteOperationDefinition<"PUT">;
|
|
67
69
|
export type DeleteOperationParams<B extends ZodSchema> = z.infer<B> & Record<string, unknown> & {
|
|
68
|
-
|
|
70
|
+
callerId?: string;
|
|
69
71
|
id: string;
|
|
70
72
|
};
|
|
71
73
|
export interface DeleteOperationOptions<TTable, B extends ZodSchema, TSession = DefaultSession> {
|
|
@@ -85,7 +85,12 @@ function createPostOperationFactory({
|
|
|
85
85
|
})(params))[0] : params;
|
|
86
86
|
const data = handler ? await handler({
|
|
87
87
|
data: raw,
|
|
88
|
-
|
|
88
|
+
// callerId 只加在**交给 handler 的那一份**上,不进上面 createAction 收到的
|
|
89
|
+
// params —— 那一份会被原样 insert,而 callerId 不是任何表的列。
|
|
90
|
+
params: {
|
|
91
|
+
...params,
|
|
92
|
+
callerId: (0, import_operation_common.callerIdOf)(session)
|
|
93
|
+
},
|
|
89
94
|
req
|
|
90
95
|
}) : raw;
|
|
91
96
|
return import_next_rest_framework.TypedNextResponse.json(data, { status: 200 });
|
|
@@ -146,7 +151,12 @@ function createPutOperationFactory({
|
|
|
146
151
|
})(params, { scope }) : params;
|
|
147
152
|
const data = handler ? await handler({
|
|
148
153
|
data: raw,
|
|
149
|
-
|
|
154
|
+
// 同 POST:callerId 不进 createAction 那一份(它会被 .set() 原样写进 UPDATE,
|
|
155
|
+
// 而 callerId 不是列)。editorId 是列,所以它留在 params 里。
|
|
156
|
+
params: {
|
|
157
|
+
...params,
|
|
158
|
+
callerId: editorId
|
|
159
|
+
},
|
|
150
160
|
req
|
|
151
161
|
}) : raw;
|
|
152
162
|
return import_next_rest_framework.TypedNextResponse.json(data, { status: 200 });
|
|
@@ -187,13 +197,23 @@ function createDeleteOperationFactory({
|
|
|
187
197
|
const body = bodySchema.parse(
|
|
188
198
|
await req.json()
|
|
189
199
|
);
|
|
200
|
+
const loadSession = (0, import_operation_common.sessionLoader)(getSession, req);
|
|
190
201
|
const { scope } = await (0, import_operation_common.resolveAccess)({
|
|
191
202
|
access,
|
|
192
|
-
loadSession
|
|
203
|
+
loadSession,
|
|
193
204
|
method: "DELETE"
|
|
194
205
|
});
|
|
195
206
|
const data = table && db ? await createAction({ db, table })({ id: body.id }, { scope }) : body;
|
|
196
|
-
|
|
207
|
+
if (handler) {
|
|
208
|
+
await handler({
|
|
209
|
+
data,
|
|
210
|
+
params: {
|
|
211
|
+
...body,
|
|
212
|
+
callerId: (0, import_operation_common.callerIdOf)(await loadSession())
|
|
213
|
+
},
|
|
214
|
+
req
|
|
215
|
+
});
|
|
216
|
+
}
|
|
197
217
|
return import_next_rest_framework.TypedNextResponse.json(data, { status: 200 });
|
|
198
218
|
} catch (error) {
|
|
199
219
|
return (0, import_operation_common.handleOperationError)(error, catchHandler);
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/**
|
|
3
|
+
* 业务侧 insert schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
4
|
+
* 这些字段由后端在 createPostAction 中自动写入。
|
|
5
|
+
*
|
|
6
|
+
* 使用方式(在业务 schema 中):
|
|
7
|
+
* const insertFooSchema = createInsertSchema(foos)
|
|
8
|
+
* .omit(BASIC_INSERT_OMIT)
|
|
9
|
+
* .extend({ ... });
|
|
10
|
+
*
|
|
11
|
+
* 说明:上游导出为 const 而不是 helper function,是为了避免经过泛型
|
|
12
|
+
* wrapper 后 TS 把 drizzle-zod 推导出的深层条件 Shape 归约成 Omit,
|
|
13
|
+
* 从而在下游 `.extend(...)` 时丢失原有字段类型。
|
|
14
|
+
*/
|
|
15
|
+
export declare const BASIC_INSERT_OMIT: {
|
|
16
|
+
readonly id: true;
|
|
17
|
+
readonly creatorId: true;
|
|
18
|
+
readonly editorId: true;
|
|
19
|
+
readonly accessedAt: true;
|
|
20
|
+
readonly createdAt: true;
|
|
21
|
+
readonly updatedAt: true;
|
|
22
|
+
};
|
|
23
|
+
/**
|
|
24
|
+
* 业务侧 update schema 需要从 drizzle-zod 生成的 schema 中排除的基础字段。
|
|
25
|
+
* update 保留 id(随后用 `.required({ id: true })` 变成必填),其余由
|
|
26
|
+
* 后端 createPutAction 自动维护。
|
|
27
|
+
*/
|
|
28
|
+
export declare const BASIC_UPDATE_OMIT: {
|
|
29
|
+
readonly creatorId: true;
|
|
30
|
+
readonly editorId: true;
|
|
31
|
+
readonly accessedAt: true;
|
|
32
|
+
readonly createdAt: true;
|
|
33
|
+
readonly updatedAt: true;
|
|
34
|
+
};
|
|
35
|
+
export declare const queryListSchema: <Incoming extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: Incoming) => z.ZodObject<(("orderBy" | "orderDir" | "pageSize" | "creatorId" | "current" | "createdAtFrom" | "createdAtTo") & keyof Incoming["shape"] extends never ? {
|
|
36
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
37
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
38
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
39
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
40
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
41
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
42
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
43
|
+
asc: "asc";
|
|
44
|
+
desc: "desc";
|
|
45
|
+
}>>;
|
|
46
|
+
} & Incoming["shape"] : ({
|
|
47
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
48
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
49
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
50
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
51
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
52
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
53
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
54
|
+
asc: "asc";
|
|
55
|
+
desc: "desc";
|
|
56
|
+
}>>;
|
|
57
|
+
} extends infer T_1 extends z.core.util.SomeObject ? { [K in keyof T_1 as K extends keyof Incoming["shape"] ? never : K]: {
|
|
58
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
59
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
60
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
61
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
62
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
63
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
64
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
65
|
+
asc: "asc";
|
|
66
|
+
desc: "desc";
|
|
67
|
+
}>>;
|
|
68
|
+
}[K]; } : never) & (Incoming["shape"] extends infer T_2 extends z.core.util.SomeObject ? { [K_1 in keyof T_2]: Incoming["shape"][K_1]; } : never)) extends infer T ? { [k in keyof T]: (("orderBy" | "orderDir" | "pageSize" | "creatorId" | "current" | "createdAtFrom" | "createdAtTo") & keyof Incoming["shape"] extends never ? {
|
|
69
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
70
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
71
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
72
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
73
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
74
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
75
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
76
|
+
asc: "asc";
|
|
77
|
+
desc: "desc";
|
|
78
|
+
}>>;
|
|
79
|
+
} & Incoming["shape"] : ({
|
|
80
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
81
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
82
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
83
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
84
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
85
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
86
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
87
|
+
asc: "asc";
|
|
88
|
+
desc: "desc";
|
|
89
|
+
}>>;
|
|
90
|
+
} extends infer T_1 extends z.core.util.SomeObject ? { [K in keyof T_1 as K extends keyof Incoming["shape"] ? never : K]: {
|
|
91
|
+
current: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
92
|
+
pageSize: z.ZodDefault<z.ZodOptional<z.ZodString>>;
|
|
93
|
+
createdAtFrom: z.ZodOptional<z.ZodString>;
|
|
94
|
+
createdAtTo: z.ZodOptional<z.ZodString>;
|
|
95
|
+
orderBy: z.ZodOptional<z.ZodString>;
|
|
96
|
+
creatorId: z.ZodOptional<z.ZodString>;
|
|
97
|
+
orderDir: z.ZodOptional<z.ZodEnum<{
|
|
98
|
+
asc: "asc";
|
|
99
|
+
desc: "desc";
|
|
100
|
+
}>>;
|
|
101
|
+
}[K]; } : never) & (Incoming["shape"] extends infer T_2 extends z.core.util.SomeObject ? { [K_1 in keyof T_2]: Incoming["shape"][K_1]; } : never))[k]; } : never, Incoming["_zod"]["config"]>;
|
|
102
|
+
export declare const listBodySchema: <T extends z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>(schema: T) => z.ZodObject<{
|
|
103
|
+
total: z.ZodNumber;
|
|
104
|
+
data: z.ZodArray<T>;
|
|
105
|
+
}, z.core.$strip>;
|
|
106
|
+
/**
|
|
107
|
+
* 给 schema 上已有的字段挂说明。字段不在这个 schema 里就跳过 —— 同一张表的
|
|
108
|
+
* insert / update / select 三份含的列不一样(basicFields 只在 select 里),一份说明表要能同时
|
|
109
|
+
* 喂给三者,就不能对「字段一定存在」有要求。**不给不存在的字段凭空造一个。**
|
|
110
|
+
*/
|
|
111
|
+
export declare const describeFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T, notes: Record<string, string>) => T;
|
|
112
|
+
export declare const describeBasicFields: <T extends z.ZodObject<z.core.$ZodLooseShape, z.core.$strip>>(schema: T) => T;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
var __defProp = Object.defineProperty;
|
|
2
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
3
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
5
|
+
var __export = (target, all) => {
|
|
6
|
+
for (var name in all)
|
|
7
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
8
|
+
};
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
18
|
+
|
|
19
|
+
// src/backend/schemas/common.ts
|
|
20
|
+
var common_exports = {};
|
|
21
|
+
__export(common_exports, {
|
|
22
|
+
BASIC_INSERT_OMIT: () => BASIC_INSERT_OMIT,
|
|
23
|
+
BASIC_UPDATE_OMIT: () => BASIC_UPDATE_OMIT,
|
|
24
|
+
describeBasicFields: () => describeBasicFields,
|
|
25
|
+
describeFields: () => describeFields,
|
|
26
|
+
listBodySchema: () => listBodySchema,
|
|
27
|
+
queryListSchema: () => queryListSchema
|
|
28
|
+
});
|
|
29
|
+
module.exports = __toCommonJS(common_exports);
|
|
30
|
+
var import_zod = require("zod");
|
|
31
|
+
var BASIC_INSERT_OMIT = {
|
|
32
|
+
id: true,
|
|
33
|
+
creatorId: true,
|
|
34
|
+
editorId: true,
|
|
35
|
+
accessedAt: true,
|
|
36
|
+
createdAt: true,
|
|
37
|
+
updatedAt: true
|
|
38
|
+
};
|
|
39
|
+
var BASIC_UPDATE_OMIT = {
|
|
40
|
+
creatorId: true,
|
|
41
|
+
editorId: true,
|
|
42
|
+
accessedAt: true,
|
|
43
|
+
createdAt: true,
|
|
44
|
+
updatedAt: true
|
|
45
|
+
};
|
|
46
|
+
var queryListSchema = (schema) => import_zod.z.object({
|
|
47
|
+
current: import_zod.z.string().optional().default("1").describe("Page number, starting at 1."),
|
|
48
|
+
pageSize: import_zod.z.string().optional().default("10").describe(
|
|
49
|
+
"Rows per page. Omitting it yields only 10 — pass a larger value to get the whole set."
|
|
50
|
+
),
|
|
51
|
+
createdAtFrom: import_zod.z.string().optional().describe(
|
|
52
|
+
"Keep only rows created at or after this instant (ISO 8601, e.g. 2026-08-01T00:00:00Z)."
|
|
53
|
+
),
|
|
54
|
+
createdAtTo: import_zod.z.string().optional().describe("Keep only rows created at or before this instant."),
|
|
55
|
+
orderBy: import_zod.z.string().optional().describe(
|
|
56
|
+
"Column to sort by — a column name of this resource (createdAt / updatedAt and the like)."
|
|
57
|
+
),
|
|
58
|
+
creatorId: import_zod.z.string().optional().describe(
|
|
59
|
+
"Keep only rows created by this user. Useful in shared spaces to filter down to one member."
|
|
60
|
+
),
|
|
61
|
+
orderDir: import_zod.z.enum(["asc", "desc"]).optional().describe("Sort ascending or descending.")
|
|
62
|
+
}).merge(schema);
|
|
63
|
+
var listBodySchema = (schema) => import_zod.z.object({
|
|
64
|
+
total: import_zod.z.number().describe(
|
|
65
|
+
"Total number of rows matching the filters — not the length of `data`. Use it to decide whether another page exists."
|
|
66
|
+
),
|
|
67
|
+
data: import_zod.z.array(schema).describe("This page's rows.")
|
|
68
|
+
});
|
|
69
|
+
var BASIC_FIELD_NOTES = {
|
|
70
|
+
creatorId: "User id of the creator. Stamped by the server; clients cannot send it.",
|
|
71
|
+
editorId: "User id of whoever changed this row last. Stamped by the server; clients cannot send it.",
|
|
72
|
+
id: "Primary key of this row (uuid)."
|
|
73
|
+
};
|
|
74
|
+
var BASIC_TIMESTAMP_NOTES = {
|
|
75
|
+
accessedAt: "When this row was last accessed.",
|
|
76
|
+
createdAt: "When this row was created.",
|
|
77
|
+
updatedAt: "When this row was last modified."
|
|
78
|
+
};
|
|
79
|
+
var describeFields = (schema, notes) => {
|
|
80
|
+
const overlay = {};
|
|
81
|
+
for (const [field, note] of Object.entries(notes)) {
|
|
82
|
+
const existing = schema.shape[field];
|
|
83
|
+
if (existing) {
|
|
84
|
+
overlay[field] = existing.describe(note);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return Object.keys(overlay).length ? schema.extend(overlay) : schema;
|
|
88
|
+
};
|
|
89
|
+
var describeBasicFields = (schema) => {
|
|
90
|
+
const withNotes = describeFields(schema, BASIC_FIELD_NOTES);
|
|
91
|
+
const overlay = {};
|
|
92
|
+
for (const [field, note] of Object.entries(BASIC_TIMESTAMP_NOTES)) {
|
|
93
|
+
const existing = withNotes.shape[field];
|
|
94
|
+
if (existing) {
|
|
95
|
+
overlay[field] = existing.meta({
|
|
96
|
+
description: note,
|
|
97
|
+
format: "date-time",
|
|
98
|
+
type: "string"
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return Object.keys(overlay).length ? withNotes.extend(overlay) : withNotes;
|
|
103
|
+
};
|
|
104
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
105
|
+
0 && (module.exports = {
|
|
106
|
+
BASIC_INSERT_OMIT,
|
|
107
|
+
BASIC_UPDATE_OMIT,
|
|
108
|
+
describeBasicFields,
|
|
109
|
+
describeFields,
|
|
110
|
+
listBodySchema,
|
|
111
|
+
queryListSchema
|
|
112
|
+
});
|