@oj-module/cli 0.2.53 → 0.2.54
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/devkit/SKILL.md +2 -2
- package/devkit/manual.md +32 -25
- package/package.json +2 -2
- package/shell-dist/assets/runtime.js +7 -4
- package/shell-dist/index.html +3 -3
- package/shell-dist/versions.json +3 -3
- package/src/__tests__/templates-tenant-scope.test.ts +3 -2
- package/src/config.ts +9 -0
- package/src/contract/__tests__/client-path.test.ts +30 -0
- package/src/contract/__tests__/emit-client.test.ts +18 -0
- package/src/contract/__tests__/handler-scan.test.ts +23 -0
- package/src/contract/check.ts +5 -5
- package/src/contract/client-path.ts +30 -0
- package/src/contract/emit-client.ts +18 -4
- package/src/contract/handler-scan.ts +40 -3
- package/src/contract/run.ts +5 -3
- package/src/init.ts +19 -1
- package/templates/README.md +40 -44
- package/templates/init.include.yaml +12 -8
- package/templates/openapi.json +1393 -34
- package/templates/src/_platform/api/contacts/api.ts +29 -1
- package/templates/src/_platform/api/contacts/detail/api.ts +25 -1
- package/templates/src/_platform/api/solutions/api.ts +60 -1
- package/templates/src/_platform/web/__client/api.schemas.ts +99 -35
- package/templates/src/_platform/web/__client/api.ts +103 -59
- package/templates/src/app/web/entry.ts +77 -0
- package/templates/src/{home/web → app/web/home}/__client/api.ts +5 -2
- package/templates/src/app/web/home/routes.ts +41 -0
- package/templates/src/app/web/login/routes.ts +71 -0
- package/templates/src/{notification/web → app/web/notification}/__client/api.schemas.ts +14 -0
- package/templates/src/{notification/web → app/web/notification}/__client/api.ts +67 -20
- package/templates/src/app/web/notification/routes.ts +70 -0
- package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.ts +5 -2
- package/templates/src/app/web/personal-center/routes.ts +49 -0
- package/templates/src/{system/web → app/web/system}/__client/api.schemas.ts +28 -22
- package/templates/src/{system/web → app/web/system}/__client/api.ts +103 -58
- package/templates/src/{system/web → app/web/system}/pages/users/index.tsx +3 -3
- package/templates/src/{system/web → app/web/system}/pages/wecom/index.tsx +12 -3
- package/templates/src/{system/web/entry.ts → app/web/system/routes.ts} +24 -35
- package/templates/src/auth/api/api.ts +59 -0
- package/templates/src/auth/api/ldap-login/api.ts +37 -1
- package/templates/src/auth/api/wecom/api.ts +23 -0
- package/templates/src/auth/web/__client/api.schemas.ts +133 -13
- package/templates/src/auth/web/__client/api.ts +129 -31
- package/templates/src/notification/api/api.ts +17 -0
- package/templates/src/notification/api/del/api.ts +2 -0
- package/templates/src/system/api/roles/api.ts +3 -0
- package/templates/src/system/api/roles/bindings/api.ts +3 -0
- package/templates/src/system/api/settings/api.ts +3 -0
- package/templates/src/system/api/users/forbidden/api.ts +3 -0
- package/templates/src/system/api/users/password/api.ts +3 -0
- package/templates/src/system/api/users/roles/api.ts +3 -0
- package/templates/src/web/api/get-async-routes/api.ts +16 -1
- package/templates/src/web/api/hello/api.ts +13 -1
- package/templates/src/web/api/user-info/api.ts +21 -1
- package/templates/src/web/web/__client/api.schemas.ts +28 -0
- package/templates/src/web/web/__client/api.ts +33 -9
- package/templates/web.config.ts +21 -15
- package/vendor/host-versions.json +3 -3
- package/templates/src/home/web/entry.ts +0 -49
- package/templates/src/login/web/entry.ts +0 -79
- package/templates/src/notification/web/entry.ts +0 -84
- package/templates/src/personal-center/web/entry.ts +0 -60
- /package/templates/src/{home/web → app/web/home}/__client/api.schemas.ts +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/en-US.json +0 -0
- /package/templates/src/{home/web → app/web/home}/locales/zh-CN.json +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/bar-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/card-list.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/line-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/components/pie-chart.tsx +0 -0
- /package/templates/src/{home/web → app/web/home}/pages/index.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/auth-provider.ts +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/en-US.json +0 -0
- /package/templates/src/{login/web → app/web/login}/locales/zh-CN.json +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/change-password.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/pages/login.tsx +0 -0
- /package/templates/src/{login/web → app/web/login}/wecom.ts +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/en-US.json +0 -0
- /package/templates/src/{notification/web → app/web/notification}/locales/zh-CN.json +0 -0
- /package/templates/src/{notification/web → app/web/notification}/pages/center/index.tsx +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/__client/api.schemas.ts +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/en-US.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/locales/zh-CN.json +0 -0
- /package/templates/src/{personal-center/web → app/web/personal-center}/pages/my-profile/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/locales/en-US.json +0 -0
- /package/templates/src/{system/web → app/web/system}/locales/zh-CN.json +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/sync-bar.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/departments/tree.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/errors.ts +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/forbidden.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/login-methods/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/oplog/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/roles/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/settings/index.tsx +0 -0
- /package/templates/src/{system/web → app/web/system}/pages/users/roles.tsx +0 -0
|
@@ -320,6 +320,29 @@ ticket.post.schema = {
|
|
|
320
320
|
},
|
|
321
321
|
},
|
|
322
322
|
};
|
|
323
|
+
// 出参:TicketBundle(issueExternal 签发,同 TokenBundle)
|
|
324
|
+
ticket.post.response = {
|
|
325
|
+
type: "object",
|
|
326
|
+
required: ["access_token", "refresh_token", "expires_in", "restricted", "user"],
|
|
327
|
+
properties: {
|
|
328
|
+
access_token: { type: "string" },
|
|
329
|
+
refresh_token: { type: "string" },
|
|
330
|
+
expires_in: { type: "number" },
|
|
331
|
+
restricted: { type: "boolean" },
|
|
332
|
+
user: {
|
|
333
|
+
type: "object",
|
|
334
|
+
required: ["id", "username", "display_name", "avatar_base64", "must_change", "roles"],
|
|
335
|
+
properties: {
|
|
336
|
+
id: { type: "number" },
|
|
337
|
+
username: { type: "string" },
|
|
338
|
+
display_name: { type: ["string", "null"] },
|
|
339
|
+
avatar_base64: { type: ["string", "null"] },
|
|
340
|
+
must_change: { type: "boolean" },
|
|
341
|
+
roles: { type: "array", items: { type: "string" } },
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
},
|
|
345
|
+
};
|
|
323
346
|
|
|
324
347
|
const event = {
|
|
325
348
|
// 企微后台「保存回调 URL」时会 GET 一次:解出 echostr 后**原样明文回**(不套信封、不加引号)。
|
|
@@ -4,10 +4,89 @@
|
|
|
4
4
|
import { z } from "@oj-module/runtime";
|
|
5
5
|
|
|
6
6
|
export const schemas = {
|
|
7
|
+
"authLoginPost": {
|
|
8
|
+
body: z.object({
|
|
9
|
+
"username": z.string().min(1),
|
|
10
|
+
"password": z.string().min(1)
|
|
11
|
+
}),
|
|
12
|
+
data: z.object({
|
|
13
|
+
"access_token": z.string(),
|
|
14
|
+
"refresh_token": z.string(),
|
|
15
|
+
"expires_in": z.number(),
|
|
16
|
+
"restricted": z.boolean(),
|
|
17
|
+
"user": z.object({
|
|
18
|
+
"id": z.number(),
|
|
19
|
+
"username": z.string(),
|
|
20
|
+
"display_name": z.string().nullable(),
|
|
21
|
+
"avatar_base64": z.string().nullable(),
|
|
22
|
+
"must_change": z.boolean(),
|
|
23
|
+
"roles": z.array(z.string())
|
|
24
|
+
})
|
|
25
|
+
}),
|
|
26
|
+
},
|
|
27
|
+
"authLogoutPost": {
|
|
28
|
+
body: z.object({
|
|
29
|
+
"refresh_token": z.string().min(1)
|
|
30
|
+
}),
|
|
31
|
+
data: z.null(),
|
|
32
|
+
},
|
|
33
|
+
"authRefreshPost": {
|
|
34
|
+
body: z.object({
|
|
35
|
+
"refresh_token": z.string().min(1)
|
|
36
|
+
}),
|
|
37
|
+
data: z.object({
|
|
38
|
+
"access_token": z.string(),
|
|
39
|
+
"refresh_token": z.string(),
|
|
40
|
+
"expires_in": z.number(),
|
|
41
|
+
"restricted": z.boolean(),
|
|
42
|
+
"user": z.object({
|
|
43
|
+
"id": z.number(),
|
|
44
|
+
"username": z.string(),
|
|
45
|
+
"display_name": z.string().nullable(),
|
|
46
|
+
"avatar_base64": z.string().nullable(),
|
|
47
|
+
"must_change": z.boolean(),
|
|
48
|
+
"roles": z.array(z.string())
|
|
49
|
+
})
|
|
50
|
+
}),
|
|
51
|
+
},
|
|
7
52
|
"authPasswordPut": {
|
|
8
53
|
body: z.object({
|
|
9
54
|
"old_password": z.string().min(1),
|
|
10
55
|
"new_password": z.string().min(6)
|
|
56
|
+
}),
|
|
57
|
+
data: z.object({
|
|
58
|
+
"access_token": z.string(),
|
|
59
|
+
"refresh_token": z.string(),
|
|
60
|
+
"expires_in": z.number(),
|
|
61
|
+
"restricted": z.boolean(),
|
|
62
|
+
"user": z.object({
|
|
63
|
+
"id": z.number(),
|
|
64
|
+
"username": z.string(),
|
|
65
|
+
"display_name": z.string().nullable(),
|
|
66
|
+
"avatar_base64": z.string().nullable(),
|
|
67
|
+
"must_change": z.boolean(),
|
|
68
|
+
"roles": z.array(z.string())
|
|
69
|
+
})
|
|
70
|
+
}),
|
|
71
|
+
},
|
|
72
|
+
"authLdapLoginPost": {
|
|
73
|
+
body: z.object({
|
|
74
|
+
"username": z.string().min(1),
|
|
75
|
+
"password": z.string().min(1)
|
|
76
|
+
}),
|
|
77
|
+
data: z.object({
|
|
78
|
+
"access_token": z.string(),
|
|
79
|
+
"refresh_token": z.string(),
|
|
80
|
+
"expires_in": z.number(),
|
|
81
|
+
"restricted": z.boolean(),
|
|
82
|
+
"user": z.object({
|
|
83
|
+
"id": z.number(),
|
|
84
|
+
"username": z.string(),
|
|
85
|
+
"display_name": z.string().nullable(),
|
|
86
|
+
"avatar_base64": z.string().nullable(),
|
|
87
|
+
"must_change": z.boolean(),
|
|
88
|
+
"roles": z.array(z.string())
|
|
89
|
+
})
|
|
11
90
|
}),
|
|
12
91
|
},
|
|
13
92
|
"authWecomAdminAppsGet": {
|
|
@@ -24,6 +103,16 @@ export const schemas = {
|
|
|
24
103
|
"installed_at": z.number(),
|
|
25
104
|
"update_time": z.number()
|
|
26
105
|
})),
|
|
106
|
+
},
|
|
107
|
+
"authWecomAdminOverviewGet": {
|
|
108
|
+
data: z.object({
|
|
109
|
+
"kinds": z.array(z.enum(["suite", "corp"])),
|
|
110
|
+
"configError": z.string().nullable(),
|
|
111
|
+
"callbackUrl": z.string().nullable(),
|
|
112
|
+
"corpId": z.string().nullable(),
|
|
113
|
+
"agentId": z.string().nullable(),
|
|
114
|
+
"corpCallbackConfigured": z.boolean()
|
|
115
|
+
}),
|
|
27
116
|
},
|
|
28
117
|
"authWecomAdminStatusPost": {
|
|
29
118
|
body: z.object({
|
|
@@ -41,16 +130,6 @@ export const schemas = {
|
|
|
41
130
|
"installUrl": z.string(),
|
|
42
131
|
"callbackUrl": z.string(),
|
|
43
132
|
"expiresIn": z.number()
|
|
44
|
-
}),
|
|
45
|
-
},
|
|
46
|
-
"authWecomAdminOverviewGet": {
|
|
47
|
-
data: z.object({
|
|
48
|
-
"kinds": z.array(z.enum(["suite", "corp"])),
|
|
49
|
-
"configError": z.string().nullable(),
|
|
50
|
-
"callbackUrl": z.string().nullable(),
|
|
51
|
-
"corpId": z.string().nullable(),
|
|
52
|
-
"agentId": z.string().nullable(),
|
|
53
|
-
"corpCallbackConfigured": z.boolean()
|
|
54
133
|
}),
|
|
55
134
|
},
|
|
56
135
|
"authWecomAdminConnectPost": {
|
|
@@ -59,11 +138,48 @@ export const schemas = {
|
|
|
59
138
|
"agentId": z.string(),
|
|
60
139
|
"departments": z.number(),
|
|
61
140
|
"members": z.number()
|
|
141
|
+
}),
|
|
142
|
+
},
|
|
143
|
+
"authWecomAdminTenantAdminGet": {
|
|
144
|
+
query: z.object({
|
|
145
|
+
"corpId": z.string().min(1)
|
|
146
|
+
}),
|
|
147
|
+
data: z.object({
|
|
148
|
+
"corpId": z.string(),
|
|
149
|
+
"username": z.string(),
|
|
150
|
+
"exists": z.boolean(),
|
|
151
|
+
"mustChange": z.boolean()
|
|
152
|
+
}),
|
|
153
|
+
},
|
|
154
|
+
"authWecomAdminTenantAdminPasswordPost": {
|
|
155
|
+
body: z.object({
|
|
156
|
+
"corpId": z.string().min(1),
|
|
157
|
+
"password": z.string().min(6)
|
|
158
|
+
}),
|
|
159
|
+
data: z.object({
|
|
160
|
+
"corpId": z.string(),
|
|
161
|
+
"username": z.string(),
|
|
162
|
+
"exists": z.boolean(),
|
|
163
|
+
"mustChange": z.boolean()
|
|
62
164
|
}),
|
|
63
165
|
},
|
|
64
166
|
"authWecomTicketPost": {
|
|
65
167
|
body: z.object({
|
|
66
168
|
"ticket": z.string().min(32).max(128)
|
|
169
|
+
}),
|
|
170
|
+
data: z.object({
|
|
171
|
+
"access_token": z.string(),
|
|
172
|
+
"refresh_token": z.string(),
|
|
173
|
+
"expires_in": z.number(),
|
|
174
|
+
"restricted": z.boolean(),
|
|
175
|
+
"user": z.object({
|
|
176
|
+
"id": z.number(),
|
|
177
|
+
"username": z.string(),
|
|
178
|
+
"display_name": z.string().nullable(),
|
|
179
|
+
"avatar_base64": z.string().nullable(),
|
|
180
|
+
"must_change": z.boolean(),
|
|
181
|
+
"roles": z.array(z.string())
|
|
182
|
+
})
|
|
67
183
|
}),
|
|
68
184
|
},
|
|
69
185
|
"authWecomSyncStartPost": {
|
|
@@ -74,6 +190,8 @@ export const schemas = {
|
|
|
74
190
|
"connected": z.boolean(),
|
|
75
191
|
"authMode": z.number(),
|
|
76
192
|
"profileStatus": z.string().nullable(),
|
|
193
|
+
"agentId": z.string().nullable(),
|
|
194
|
+
"credentialKind": z.union([z.literal("suite"), z.literal("corp"), z.literal(null)]).nullable(),
|
|
77
195
|
"status": z.enum(["idle", "running", "done", "failed", "cancelled"]),
|
|
78
196
|
"phase": z.enum(["departments", "members", "reconcile"]),
|
|
79
197
|
"stats": z.object({
|
|
@@ -106,6 +224,8 @@ export const schemas = {
|
|
|
106
224
|
"connected": z.boolean(),
|
|
107
225
|
"authMode": z.number(),
|
|
108
226
|
"profileStatus": z.string().nullable(),
|
|
227
|
+
"agentId": z.string().nullable(),
|
|
228
|
+
"credentialKind": z.union([z.literal("suite"), z.literal("corp"), z.literal(null)]).nullable(),
|
|
109
229
|
"status": z.enum(["idle", "running", "done", "failed", "cancelled"]),
|
|
110
230
|
"phase": z.enum(["departments", "members", "reconcile"]),
|
|
111
231
|
"stats": z.object({
|
|
@@ -127,9 +247,7 @@ export const schemas = {
|
|
|
127
247
|
}).nullable(),
|
|
128
248
|
"startedAt": z.number(),
|
|
129
249
|
"finishedAt": z.number(),
|
|
130
|
-
"updateTime": z.number()
|
|
131
|
-
"agentId": z.string().nullable(),
|
|
132
|
-
"credentialKind": z.union([z.literal("suite"), z.literal("corp"), z.literal(null)]).nullable()
|
|
250
|
+
"updateTime": z.number()
|
|
133
251
|
}),
|
|
134
252
|
},
|
|
135
253
|
"authWecomSyncCancelPost": {
|
|
@@ -140,6 +258,8 @@ export const schemas = {
|
|
|
140
258
|
"connected": z.boolean(),
|
|
141
259
|
"authMode": z.number(),
|
|
142
260
|
"profileStatus": z.string().nullable(),
|
|
261
|
+
"agentId": z.string().nullable(),
|
|
262
|
+
"credentialKind": z.union([z.literal("suite"), z.literal("corp"), z.literal(null)]).nullable(),
|
|
143
263
|
"status": z.enum(["idle", "running", "done", "failed", "cancelled"]),
|
|
144
264
|
"phase": z.enum(["departments", "members", "reconcile"]),
|
|
145
265
|
"stats": z.object({
|
|
@@ -33,7 +33,7 @@ export function createAuthClient(ctx: ModuleContext) {
|
|
|
33
33
|
}
|
|
34
34
|
ctx.register.apiPrefix(API_PREFIX);
|
|
35
35
|
bindRequest(ctx.utils.request);
|
|
36
|
-
return { authLoginPost, authLogoutPost, authRefreshPost, authPasswordPut, authLdapLoginPost, authWecomAdminAppsGet, authWecomAdminStatusPost, authWecomAdminInstallGet,
|
|
36
|
+
return { authLoginPost, authLogoutPost, authRefreshPost, authPasswordPut, authLdapLoginPost, authWecomAdminAppsGet, authWecomAdminOverviewGet, authWecomAdminStatusPost, authWecomAdminInstallGet, authWecomAdminConnectPost, authWecomAdminTenantAdminGet, authWecomAdminTenantAdminPasswordPost, authWecomLoginGet, authWecomCallbackGet, authWecomTicketPost, authWecomEventGet, authWecomEventPost, authWecomSyncStartPost, authWecomSyncStatusGet, authWecomSyncCancelPost };
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
/**
|
|
@@ -64,14 +64,23 @@ async function toApiError(e: unknown): Promise<unknown> {
|
|
|
64
64
|
return e;
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
-
export
|
|
67
|
+
export type AuthLoginPostBody = z.input<(typeof schemas)["authLoginPost"]["body"]>;
|
|
68
|
+
export type AuthLoginPostData = z.infer<(typeof schemas)["authLoginPost"]["data"]>;
|
|
69
|
+
|
|
70
|
+
export async function authLoginPost(body: AuthLoginPostBody): Promise<AuthLoginPostData> {
|
|
68
71
|
const client = ensureReq();
|
|
69
72
|
try {
|
|
70
|
-
const env = await client.post(`auth/login
|
|
73
|
+
const env = await client.post(`auth/login`, { json: body }).json<OjEnvelope<AuthLoginPostData>>();
|
|
71
74
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
72
75
|
if (typeof env.code === "number" && env.code !== 0)
|
|
73
76
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
74
|
-
const data = env.data as
|
|
77
|
+
const data = env.data as AuthLoginPostData;
|
|
78
|
+
if (import.meta.env.DEV) {
|
|
79
|
+
const { schemas } = await import("./api.schemas");
|
|
80
|
+
const r = schemas["authLoginPost"].data.safeParse(data);
|
|
81
|
+
if (!r.success)
|
|
82
|
+
throw new ContractApiError(-1, `[契约违例] authLoginPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
83
|
+
}
|
|
75
84
|
return data;
|
|
76
85
|
}
|
|
77
86
|
catch (e) {
|
|
@@ -79,14 +88,23 @@ export async function authLoginPost(): Promise<unknown> {
|
|
|
79
88
|
}
|
|
80
89
|
}
|
|
81
90
|
|
|
82
|
-
export
|
|
91
|
+
export type AuthLogoutPostBody = z.input<(typeof schemas)["authLogoutPost"]["body"]>;
|
|
92
|
+
export type AuthLogoutPostData = z.infer<(typeof schemas)["authLogoutPost"]["data"]>;
|
|
93
|
+
|
|
94
|
+
export async function authLogoutPost(body: AuthLogoutPostBody): Promise<AuthLogoutPostData> {
|
|
83
95
|
const client = ensureReq();
|
|
84
96
|
try {
|
|
85
|
-
const env = await client.post(`auth/logout
|
|
97
|
+
const env = await client.post(`auth/logout`, { json: body }).json<OjEnvelope<AuthLogoutPostData>>();
|
|
86
98
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
87
99
|
if (typeof env.code === "number" && env.code !== 0)
|
|
88
100
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
89
|
-
const data = env.data as
|
|
101
|
+
const data = env.data as AuthLogoutPostData;
|
|
102
|
+
if (import.meta.env.DEV) {
|
|
103
|
+
const { schemas } = await import("./api.schemas");
|
|
104
|
+
const r = schemas["authLogoutPost"].data.safeParse(data);
|
|
105
|
+
if (!r.success)
|
|
106
|
+
throw new ContractApiError(-1, `[契约违例] authLogoutPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
107
|
+
}
|
|
90
108
|
return data;
|
|
91
109
|
}
|
|
92
110
|
catch (e) {
|
|
@@ -94,14 +112,23 @@ export async function authLogoutPost(): Promise<unknown> {
|
|
|
94
112
|
}
|
|
95
113
|
}
|
|
96
114
|
|
|
97
|
-
export
|
|
115
|
+
export type AuthRefreshPostBody = z.input<(typeof schemas)["authRefreshPost"]["body"]>;
|
|
116
|
+
export type AuthRefreshPostData = z.infer<(typeof schemas)["authRefreshPost"]["data"]>;
|
|
117
|
+
|
|
118
|
+
export async function authRefreshPost(body: AuthRefreshPostBody): Promise<AuthRefreshPostData> {
|
|
98
119
|
const client = ensureReq();
|
|
99
120
|
try {
|
|
100
|
-
const env = await client.post(`auth/refresh
|
|
121
|
+
const env = await client.post(`auth/refresh`, { json: body }).json<OjEnvelope<AuthRefreshPostData>>();
|
|
101
122
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
102
123
|
if (typeof env.code === "number" && env.code !== 0)
|
|
103
124
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
104
|
-
const data = env.data as
|
|
125
|
+
const data = env.data as AuthRefreshPostData;
|
|
126
|
+
if (import.meta.env.DEV) {
|
|
127
|
+
const { schemas } = await import("./api.schemas");
|
|
128
|
+
const r = schemas["authRefreshPost"].data.safeParse(data);
|
|
129
|
+
if (!r.success)
|
|
130
|
+
throw new ContractApiError(-1, `[契约违例] authRefreshPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
131
|
+
}
|
|
105
132
|
return data;
|
|
106
133
|
}
|
|
107
134
|
catch (e) {
|
|
@@ -110,15 +137,22 @@ export async function authRefreshPost(): Promise<unknown> {
|
|
|
110
137
|
}
|
|
111
138
|
|
|
112
139
|
export type AuthPasswordPutBody = z.input<(typeof schemas)["authPasswordPut"]["body"]>;
|
|
140
|
+
export type AuthPasswordPutData = z.infer<(typeof schemas)["authPasswordPut"]["data"]>;
|
|
113
141
|
|
|
114
|
-
export async function authPasswordPut(body: AuthPasswordPutBody): Promise<
|
|
142
|
+
export async function authPasswordPut(body: AuthPasswordPutBody): Promise<AuthPasswordPutData> {
|
|
115
143
|
const client = ensureReq();
|
|
116
144
|
try {
|
|
117
|
-
const env = await client.put(`auth/password`, { json: body }).json<OjEnvelope<
|
|
145
|
+
const env = await client.put(`auth/password`, { json: body }).json<OjEnvelope<AuthPasswordPutData>>();
|
|
118
146
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
119
147
|
if (typeof env.code === "number" && env.code !== 0)
|
|
120
148
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
121
|
-
const data = env.data as
|
|
149
|
+
const data = env.data as AuthPasswordPutData;
|
|
150
|
+
if (import.meta.env.DEV) {
|
|
151
|
+
const { schemas } = await import("./api.schemas");
|
|
152
|
+
const r = schemas["authPasswordPut"].data.safeParse(data);
|
|
153
|
+
if (!r.success)
|
|
154
|
+
throw new ContractApiError(-1, `[契约违例] authPasswordPut 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
155
|
+
}
|
|
122
156
|
return data;
|
|
123
157
|
}
|
|
124
158
|
catch (e) {
|
|
@@ -126,14 +160,23 @@ export async function authPasswordPut(body: AuthPasswordPutBody): Promise<unknow
|
|
|
126
160
|
}
|
|
127
161
|
}
|
|
128
162
|
|
|
129
|
-
export
|
|
163
|
+
export type AuthLdapLoginPostBody = z.input<(typeof schemas)["authLdapLoginPost"]["body"]>;
|
|
164
|
+
export type AuthLdapLoginPostData = z.infer<(typeof schemas)["authLdapLoginPost"]["data"]>;
|
|
165
|
+
|
|
166
|
+
export async function authLdapLoginPost(body: AuthLdapLoginPostBody): Promise<AuthLdapLoginPostData> {
|
|
130
167
|
const client = ensureReq();
|
|
131
168
|
try {
|
|
132
|
-
const env = await client.post(`auth/ldap-login
|
|
169
|
+
const env = await client.post(`auth/ldap-login`, { json: body }).json<OjEnvelope<AuthLdapLoginPostData>>();
|
|
133
170
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
134
171
|
if (typeof env.code === "number" && env.code !== 0)
|
|
135
172
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
136
|
-
const data = env.data as
|
|
173
|
+
const data = env.data as AuthLdapLoginPostData;
|
|
174
|
+
if (import.meta.env.DEV) {
|
|
175
|
+
const { schemas } = await import("./api.schemas");
|
|
176
|
+
const r = schemas["authLdapLoginPost"].data.safeParse(data);
|
|
177
|
+
if (!r.success)
|
|
178
|
+
throw new ContractApiError(-1, `[契约违例] authLdapLoginPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
179
|
+
}
|
|
137
180
|
return data;
|
|
138
181
|
}
|
|
139
182
|
catch (e) {
|
|
@@ -164,6 +207,29 @@ export async function authWecomAdminAppsGet(): Promise<AuthWecomAdminAppsGetData
|
|
|
164
207
|
}
|
|
165
208
|
}
|
|
166
209
|
|
|
210
|
+
export type AuthWecomAdminOverviewGetData = z.infer<(typeof schemas)["authWecomAdminOverviewGet"]["data"]>;
|
|
211
|
+
|
|
212
|
+
export async function authWecomAdminOverviewGet(): Promise<AuthWecomAdminOverviewGetData> {
|
|
213
|
+
const client = ensureReq();
|
|
214
|
+
try {
|
|
215
|
+
const env = await client.get(`auth/wecom/admin/overview`).json<OjEnvelope<AuthWecomAdminOverviewGetData>>();
|
|
216
|
+
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
217
|
+
if (typeof env.code === "number" && env.code !== 0)
|
|
218
|
+
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
219
|
+
const data = env.data as AuthWecomAdminOverviewGetData;
|
|
220
|
+
if (import.meta.env.DEV) {
|
|
221
|
+
const { schemas } = await import("./api.schemas");
|
|
222
|
+
const r = schemas["authWecomAdminOverviewGet"].data.safeParse(data);
|
|
223
|
+
if (!r.success)
|
|
224
|
+
throw new ContractApiError(-1, `[契约违例] authWecomAdminOverviewGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
225
|
+
}
|
|
226
|
+
return data;
|
|
227
|
+
}
|
|
228
|
+
catch (e) {
|
|
229
|
+
throw await toApiError(e);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
167
233
|
export type AuthWecomAdminStatusPostBody = z.input<(typeof schemas)["authWecomAdminStatusPost"]["body"]>;
|
|
168
234
|
export type AuthWecomAdminStatusPostData = z.infer<(typeof schemas)["authWecomAdminStatusPost"]["data"]>;
|
|
169
235
|
|
|
@@ -211,21 +277,21 @@ export async function authWecomAdminInstallGet(): Promise<AuthWecomAdminInstallG
|
|
|
211
277
|
}
|
|
212
278
|
}
|
|
213
279
|
|
|
214
|
-
export type
|
|
280
|
+
export type AuthWecomAdminConnectPostData = z.infer<(typeof schemas)["authWecomAdminConnectPost"]["data"]>;
|
|
215
281
|
|
|
216
|
-
export async function
|
|
282
|
+
export async function authWecomAdminConnectPost(): Promise<AuthWecomAdminConnectPostData> {
|
|
217
283
|
const client = ensureReq();
|
|
218
284
|
try {
|
|
219
|
-
const env = await client.
|
|
285
|
+
const env = await client.post(`auth/wecom/admin/connect`).json<OjEnvelope<AuthWecomAdminConnectPostData>>();
|
|
220
286
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
221
287
|
if (typeof env.code === "number" && env.code !== 0)
|
|
222
288
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
223
|
-
const data = env.data as
|
|
289
|
+
const data = env.data as AuthWecomAdminConnectPostData;
|
|
224
290
|
if (import.meta.env.DEV) {
|
|
225
291
|
const { schemas } = await import("./api.schemas");
|
|
226
|
-
const r = schemas["
|
|
292
|
+
const r = schemas["authWecomAdminConnectPost"].data.safeParse(data);
|
|
227
293
|
if (!r.success)
|
|
228
|
-
throw new ContractApiError(-1, `[契约违例]
|
|
294
|
+
throw new ContractApiError(-1, `[契约违例] authWecomAdminConnectPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
229
295
|
}
|
|
230
296
|
return data;
|
|
231
297
|
}
|
|
@@ -234,21 +300,46 @@ export async function authWecomAdminOverviewGet(): Promise<AuthWecomAdminOvervie
|
|
|
234
300
|
}
|
|
235
301
|
}
|
|
236
302
|
|
|
237
|
-
export type
|
|
303
|
+
export type AuthWecomAdminTenantAdminGetQuery = z.input<(typeof schemas)["authWecomAdminTenantAdminGet"]["query"]>;
|
|
304
|
+
export type AuthWecomAdminTenantAdminGetData = z.infer<(typeof schemas)["authWecomAdminTenantAdminGet"]["data"]>;
|
|
238
305
|
|
|
239
|
-
export async function
|
|
306
|
+
export async function authWecomAdminTenantAdminGet(query: AuthWecomAdminTenantAdminGetQuery): Promise<AuthWecomAdminTenantAdminGetData> {
|
|
240
307
|
const client = ensureReq();
|
|
241
308
|
try {
|
|
242
|
-
const env = await client.
|
|
309
|
+
const env = await client.get(`auth/wecom/admin/tenant-admin`, { searchParams: query as Record<string, string | number | boolean> }).json<OjEnvelope<AuthWecomAdminTenantAdminGetData>>();
|
|
243
310
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
244
311
|
if (typeof env.code === "number" && env.code !== 0)
|
|
245
312
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
246
|
-
const data = env.data as
|
|
313
|
+
const data = env.data as AuthWecomAdminTenantAdminGetData;
|
|
247
314
|
if (import.meta.env.DEV) {
|
|
248
315
|
const { schemas } = await import("./api.schemas");
|
|
249
|
-
const r = schemas["
|
|
316
|
+
const r = schemas["authWecomAdminTenantAdminGet"].data.safeParse(data);
|
|
250
317
|
if (!r.success)
|
|
251
|
-
throw new ContractApiError(-1, `[契约违例]
|
|
318
|
+
throw new ContractApiError(-1, `[契约违例] authWecomAdminTenantAdminGet 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
319
|
+
}
|
|
320
|
+
return data;
|
|
321
|
+
}
|
|
322
|
+
catch (e) {
|
|
323
|
+
throw await toApiError(e);
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export type AuthWecomAdminTenantAdminPasswordPostBody = z.input<(typeof schemas)["authWecomAdminTenantAdminPasswordPost"]["body"]>;
|
|
328
|
+
export type AuthWecomAdminTenantAdminPasswordPostData = z.infer<(typeof schemas)["authWecomAdminTenantAdminPasswordPost"]["data"]>;
|
|
329
|
+
|
|
330
|
+
export async function authWecomAdminTenantAdminPasswordPost(body: AuthWecomAdminTenantAdminPasswordPostBody): Promise<AuthWecomAdminTenantAdminPasswordPostData> {
|
|
331
|
+
const client = ensureReq();
|
|
332
|
+
try {
|
|
333
|
+
const env = await client.post(`auth/wecom/admin/tenant-admin/password`, { json: body }).json<OjEnvelope<AuthWecomAdminTenantAdminPasswordPostData>>();
|
|
334
|
+
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
335
|
+
if (typeof env.code === "number" && env.code !== 0)
|
|
336
|
+
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
337
|
+
const data = env.data as AuthWecomAdminTenantAdminPasswordPostData;
|
|
338
|
+
if (import.meta.env.DEV) {
|
|
339
|
+
const { schemas } = await import("./api.schemas");
|
|
340
|
+
const r = schemas["authWecomAdminTenantAdminPasswordPost"].data.safeParse(data);
|
|
341
|
+
if (!r.success)
|
|
342
|
+
throw new ContractApiError(-1, `[契约违例] authWecomAdminTenantAdminPasswordPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
252
343
|
}
|
|
253
344
|
return data;
|
|
254
345
|
}
|
|
@@ -288,15 +379,22 @@ export async function authWecomCallbackGet(): Promise<unknown> {
|
|
|
288
379
|
}
|
|
289
380
|
|
|
290
381
|
export type AuthWecomTicketPostBody = z.input<(typeof schemas)["authWecomTicketPost"]["body"]>;
|
|
382
|
+
export type AuthWecomTicketPostData = z.infer<(typeof schemas)["authWecomTicketPost"]["data"]>;
|
|
291
383
|
|
|
292
|
-
export async function authWecomTicketPost(body: AuthWecomTicketPostBody): Promise<
|
|
384
|
+
export async function authWecomTicketPost(body: AuthWecomTicketPostBody): Promise<AuthWecomTicketPostData> {
|
|
293
385
|
const client = ensureReq();
|
|
294
386
|
try {
|
|
295
|
-
const env = await client.post(`auth/wecom/ticket`, { json: body }).json<OjEnvelope<
|
|
387
|
+
const env = await client.post(`auth/wecom/ticket`, { json: body }).json<OjEnvelope<AuthWecomTicketPostData>>();
|
|
296
388
|
// 2xx + code!==0 也是业务错误(§6.2 通道 a):oj 不会这么发,但契约机制的价值恰是防漂移
|
|
297
389
|
if (typeof env.code === "number" && env.code !== 0)
|
|
298
390
|
throw new ContractApiError(env.code, env.msg ?? "业务错误(信封 code 非 0)");
|
|
299
|
-
const data = env.data as
|
|
391
|
+
const data = env.data as AuthWecomTicketPostData;
|
|
392
|
+
if (import.meta.env.DEV) {
|
|
393
|
+
const { schemas } = await import("./api.schemas");
|
|
394
|
+
const r = schemas["authWecomTicketPost"].data.safeParse(data);
|
|
395
|
+
if (!r.success)
|
|
396
|
+
throw new ContractApiError(-1, `[契约违例] authWecomTicketPost 响应与契约不符:${r.error.issues.map(i => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ")}`);
|
|
397
|
+
}
|
|
300
398
|
return data;
|
|
301
399
|
}
|
|
302
400
|
catch (e) {
|
|
@@ -137,6 +137,11 @@ notifications.read.post.schema = {
|
|
|
137
137
|
properties: { id: { type: "string" } },
|
|
138
138
|
},
|
|
139
139
|
};
|
|
140
|
+
notifications.read.post.response = { type: "null" };
|
|
141
|
+
|
|
142
|
+
// read-all / clear:无入参、无返回体(json.ok(null))——只声明出参,入参留空。
|
|
143
|
+
notifications["read-all"].post.response = { type: "null" };
|
|
144
|
+
notifications.clear.post.response = { type: "null" };
|
|
140
145
|
|
|
141
146
|
// POST /api/notification/push —— 给指定用户推一条按人通知(发射原语 HTTP 入口)。
|
|
142
147
|
// 真正逻辑在 _shared/notify.pushNotice(任一后端模块也可直接 import 调用,无需走 HTTP)。
|
|
@@ -239,6 +244,12 @@ const broadcast = {
|
|
|
239
244
|
},
|
|
240
245
|
};
|
|
241
246
|
|
|
247
|
+
push.post.response = {
|
|
248
|
+
type: "object",
|
|
249
|
+
required: ["id"],
|
|
250
|
+
properties: { id: { type: "string" } },
|
|
251
|
+
};
|
|
252
|
+
|
|
242
253
|
broadcast.post.schema = {
|
|
243
254
|
body: {
|
|
244
255
|
type: "object",
|
|
@@ -253,4 +264,10 @@ broadcast.post.schema = {
|
|
|
253
264
|
},
|
|
254
265
|
};
|
|
255
266
|
|
|
267
|
+
broadcast.post.response = {
|
|
268
|
+
type: "object",
|
|
269
|
+
required: ["id"],
|
|
270
|
+
properties: { id: { type: "string" } },
|
|
271
|
+
};
|
|
272
|
+
|
|
256
273
|
export default { notifications, push, "push-broadcast": broadcast };
|
|
@@ -1,8 +1,23 @@
|
|
|
1
1
|
// GET /api/web/get-async-routes —— 后端动态路由(Bearer 守卫保护)。
|
|
2
2
|
// demo 返回空数组:模块路由由 web/src 静态注入;需要后端路由时按
|
|
3
3
|
// 宿主 RouteObject 形状返回。
|
|
4
|
-
|
|
4
|
+
const handler = {
|
|
5
5
|
get() {
|
|
6
6
|
json.ok([]);
|
|
7
7
|
},
|
|
8
8
|
};
|
|
9
|
+
|
|
10
|
+
// 出参:宿主 RouteObject 形状的数组(demo 恒空数组;页面路由由前端静态注入)
|
|
11
|
+
handler.get.response = {
|
|
12
|
+
type: "array",
|
|
13
|
+
items: {
|
|
14
|
+
type: "object",
|
|
15
|
+
properties: {
|
|
16
|
+
path: { type: "string" },
|
|
17
|
+
component: { type: "string" },
|
|
18
|
+
children: { type: "array", items: { type: "object" } },
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export default handler;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// GET /api/web/hello —— 演示业务端点(Bearer 守卫保护)。
|
|
2
2
|
// 改完保存即生效(oj dev/ts 热更);新增/删除模块目录需重启 ojm dev。
|
|
3
|
-
|
|
3
|
+
const handler = {
|
|
4
4
|
get() {
|
|
5
5
|
json.ok({
|
|
6
6
|
message: "hello from oj",
|
|
@@ -8,3 +8,15 @@ export default {
|
|
|
8
8
|
});
|
|
9
9
|
},
|
|
10
10
|
};
|
|
11
|
+
|
|
12
|
+
// 出参:演示端点(hello 文案 + 当前用户 id)
|
|
13
|
+
handler.get.response = {
|
|
14
|
+
type: "object",
|
|
15
|
+
required: ["message", "user"],
|
|
16
|
+
properties: {
|
|
17
|
+
message: { type: "string" },
|
|
18
|
+
user: { type: ["number", "null"] },
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export default handler;
|