create-windy 0.2.5 → 0.2.6

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.
@@ -1,56 +1,19 @@
1
1
  import { describe, expect, test } from "vitest";
2
2
  import { createMemoryHistory } from "vue-router";
3
- import { systemFoundationModule } from "@windy/modules";
4
- import type { WebAccessSnapshot } from "@/layout/navigation";
5
- import type { PlatformLicenseSnapshot } from "@/services/license-api";
6
3
  import { ApiError } from "@/services/http";
7
- import { safeReturnTo } from "./auth-navigation";
8
- import { createAppRouter, routes } from "./index";
9
-
10
- describe("Manifest 菜单与 Router 契约", () => {
11
- test("路由默认要求登录,只有显式 public 的页面允许匿名访问", () => {
12
- const publicRoutes = routes.filter((route) => route.meta?.public === true);
13
-
14
- expect(publicRoutes.map((route) => route.path)).toEqual(["/login"]);
15
- expect(routes.find((route) => route.path === "/")?.meta?.public).not.toBe(
16
- true,
17
- );
18
- expect(
19
- routes.find((route) => route.path === "/admin")?.meta?.public,
20
- ).not.toBe(true);
21
- });
22
-
23
- test("每个 Admin Route 都由真实页面实现且 Guard 来自 Manifest", () => {
24
- const manifests = [systemFoundationModule()];
25
- const definitions = manifests.flatMap((manifest) => manifest.adminRoutes);
26
- const implementationByKey = new Map(
27
- routes.flatMap((route) => {
28
- const key = route.meta?.manifestRouteKey;
29
- return typeof key === "string" ? [[key, route] as const] : [];
30
- }),
31
- );
32
-
33
- expect([...implementationByKey.keys()].sort()).toEqual(
34
- definitions.map(({ key }) => key).sort(),
35
- );
36
- for (const definition of definitions) {
37
- const route = implementationByKey.get(definition.key);
38
- expect(route?.path).toBe(definition.path);
39
- expect(route?.meta?.title).toBe(definition.title);
40
- if (definition.directAccess !== "authenticated") {
41
- expect(route?.meta?.permissionKey).toBe(definition.permissionKey);
42
- expect(route?.meta?.featureKey).toBe(definition.featureKey);
43
- }
44
- }
45
- expect(definitions.some(({ path }) => path === "/admin/profile")).toBe(
46
- true,
47
- );
48
- expect(definitions.some(({ path }) => path === "/work-orders")).toBe(false);
49
- expect(routes.some(({ path }) => path === "/work-orders")).toBe(false);
50
- });
51
- });
4
+ import { createAppRouter } from "./index";
5
+ import {
6
+ accessSnapshot,
7
+ authProvider,
8
+ // @windy-module system.license begin
9
+ licenseProvider,
10
+ licenseSnapshot,
11
+ // @windy-module system.license end
12
+ provider,
13
+ } from "./router-test-fixtures";
52
14
 
53
15
  describe("Web 路由强制访问", () => {
16
+ // @windy-module system.license begin
54
17
  test("普通业务账号可进入首页且不会加载管理面或 License 快照", async () => {
55
18
  let accessRefreshes = 0;
56
19
  let licenseRefreshes = 0;
@@ -78,12 +41,15 @@ describe("Web 路由强制访问", () => {
78
41
  expect(accessRefreshes).toBe(0);
79
42
  expect(licenseRefreshes).toBe(0);
80
43
  });
44
+ // @windy-module system.license end
81
45
 
82
46
  test("拥有管理面准入权限的管理员可进入管理后台", async () => {
83
47
  const router = createAppRouter(
84
48
  createMemoryHistory(),
85
49
  provider(accessSnapshot()),
50
+ // @windy-module system.license begin
86
51
  licenseProvider(),
52
+ // @windy-module system.license end
87
53
  authProvider(),
88
54
  );
89
55
 
@@ -100,7 +66,9 @@ describe("Web 路由强制访问", () => {
100
66
  refresh: async () =>
101
67
  Promise.reject(new ApiError("admin access denied", 403)),
102
68
  },
69
+ // @windy-module system.license begin
103
70
  licenseProvider(),
71
+ // @windy-module system.license end
104
72
  authProvider(),
105
73
  );
106
74
 
@@ -119,7 +87,9 @@ describe("Web 路由强制访问", () => {
119
87
  const router = createAppRouter(
120
88
  createMemoryHistory(),
121
89
  provider(snapshot),
90
+ // @windy-module system.license begin
122
91
  licenseProvider(),
92
+ // @windy-module system.license end
123
93
  authProvider(),
124
94
  );
125
95
 
@@ -138,7 +108,9 @@ describe("Web 路由强制访问", () => {
138
108
  const router = createAppRouter(
139
109
  createMemoryHistory(),
140
110
  provider(snapshot),
111
+ // @windy-module system.license begin
141
112
  licenseProvider(),
113
+ // @windy-module system.license end
142
114
  authProvider(),
143
115
  );
144
116
  snapshot.features[0]!.allowed = false;
@@ -161,7 +133,9 @@ describe("Web 路由强制访问", () => {
161
133
  const router = createAppRouter(
162
134
  createMemoryHistory(),
163
135
  provider(snapshot),
136
+ // @windy-module system.license begin
164
137
  licenseProvider(),
138
+ // @windy-module system.license end
165
139
  authProvider(),
166
140
  );
167
141
 
@@ -183,7 +157,9 @@ describe("Web 路由强制访问", () => {
183
157
  const router = createAppRouter(
184
158
  createMemoryHistory(),
185
159
  provider(snapshot),
160
+ // @windy-module system.license begin
186
161
  licenseProvider(),
162
+ // @windy-module system.license end
187
163
  authProvider(),
188
164
  );
189
165
 
@@ -192,6 +168,7 @@ describe("Web 路由强制访问", () => {
192
168
  expect(router.currentRoute.value.path).toBe("/admin/profile");
193
169
  });
194
170
 
171
+ // @windy-module system.license begin
195
172
  test("到期后任意业务页面都进入 License 更新页且不会重定向循环", async () => {
196
173
  const snapshot = accessSnapshot();
197
174
  let accessRefreshes = 0;
@@ -232,165 +209,5 @@ describe("Web 路由强制访问", () => {
232
209
 
233
210
  expect(router.currentRoute.value.name).toBe("runtime-license");
234
211
  });
235
-
236
- test("未认证直访受保护页面会保存安全 returnTo 并进入登录页", async () => {
237
- const router = createAppRouter(
238
- createMemoryHistory(),
239
- provider(accessSnapshot()),
240
- licenseProvider(),
241
- authProvider("anonymous"),
242
- );
243
-
244
- await router.push("/admin/system/users?page=2");
245
-
246
- expect(router.currentRoute.value.name).toBe("login");
247
- expect(router.currentRoute.value.query.returnTo).toBe(
248
- "/admin/system/users?page=2",
249
- );
250
- });
251
-
252
- test("已认证用户访问登录页只接受站内绝对 returnTo", async () => {
253
- const router = createAppRouter(
254
- createMemoryHistory(),
255
- provider(accessSnapshot()),
256
- licenseProvider(),
257
- authProvider(),
258
- );
259
-
260
- await router.push({
261
- name: "login",
262
- query: { returnTo: "https://evil.test" },
263
- });
264
-
265
- expect(router.currentRoute.value.path).toBe("/");
266
- });
267
-
268
- test("管理员重置后的账号只能进入强制改密页且不会重定向循环", async () => {
269
- const session = authProvider();
270
- session.actor.credentialState = "must-change-password";
271
- const router = createAppRouter(
272
- createMemoryHistory(),
273
- provider(accessSnapshot()),
274
- licenseProvider(),
275
- session,
276
- );
277
-
278
- await router.push("/admin/system/users");
279
- expect(router.currentRoute.value.name).toBe("change-password");
280
-
281
- await router.push("/change-password");
282
- expect(router.currentRoute.value.name).toBe("change-password");
283
- });
284
-
285
- test("会话服务异常不会被误判成未登录", async () => {
286
- const router = createAppRouter(
287
- createMemoryHistory(),
288
- provider(accessSnapshot()),
289
- licenseProvider(),
290
- {
291
- ensureInitialized: async () => Promise.reject(new Error("offline")),
292
- clear() {},
293
- },
294
- );
295
-
296
- await router.push("/admin/system/users");
297
-
298
- expect(router.currentRoute.value.name).toBe("access-denied");
299
- expect(router.currentRoute.value.query.reason).toBe("session-unavailable");
300
- });
301
-
302
- test("会话过期引发的 401 才会清退到登录页", async () => {
303
- const router = createAppRouter(
304
- createMemoryHistory(),
305
- provider(accessSnapshot()),
306
- { refresh: async () => Promise.reject(new ApiError("expired", 401)) },
307
- authProvider(),
308
- );
309
-
310
- await router.push("/admin/system/license");
311
-
312
- expect(router.currentRoute.value.name).toBe("login");
313
- expect(router.currentRoute.value.query.returnTo).toBe(
314
- "/admin/system/license",
315
- );
316
- });
317
- });
318
-
319
- describe("安全 returnTo", () => {
320
- test("只接受应用内绝对路径并排除登录页自身", () => {
321
- expect(safeReturnTo("/admin/system/users?tab=active#top")).toBe(
322
- "/admin/system/users?tab=active#top",
323
- );
324
- expect(safeReturnTo("https://evil.test/admin/system/users")).toBe("/");
325
- expect(safeReturnTo("//evil.test/admin/system/users")).toBe("/");
326
- expect(safeReturnTo("/\\evil.test/admin/system/users")).toBe("/");
327
- expect(safeReturnTo("/login?returnTo=/admin/system/users")).toBe("/");
328
- });
212
+ // @windy-module system.license end
329
213
  });
330
-
331
- function provider(snapshot: WebAccessSnapshot) {
332
- return { refresh: async () => snapshot };
333
- }
334
-
335
- function licenseProvider(restricted = false) {
336
- const snapshot = licenseSnapshot(restricted);
337
- return { refresh: async () => snapshot };
338
- }
339
-
340
- function licenseSnapshot(restricted = false): PlatformLicenseSnapshot {
341
- return {
342
- status: restricted ? "expired" : "valid",
343
- restricted,
344
- reminder: { level: "none" },
345
- canViewDetails: false,
346
- };
347
- }
348
-
349
- function authProvider(phase: "authenticated" | "anonymous" = "authenticated") {
350
- let currentPhase = phase;
351
- const actor = accessSnapshot().actor;
352
- return {
353
- actor,
354
- ensureInitialized: async () => ({
355
- phase: currentPhase,
356
- actor: currentPhase === "authenticated" ? actor : undefined,
357
- }),
358
- clear() {
359
- currentPhase = "anonymous";
360
- },
361
- };
362
- }
363
-
364
- function accessSnapshot(): WebAccessSnapshot {
365
- return {
366
- actor: {
367
- id: "user_1",
368
- type: "user",
369
- name: "审计员",
370
- roleCodes: ["auditor"],
371
- permissionKeys: [
372
- "system.admin.access",
373
- "system.audit.read",
374
- "system.feature.read",
375
- ],
376
- },
377
- menus: [],
378
- permissions: [],
379
- features: [
380
- {
381
- key: "system.audit",
382
- enabled: true,
383
- visible: "visible",
384
- allowed: true,
385
- reason: "allowed",
386
- },
387
- {
388
- key: "system.rbac",
389
- enabled: true,
390
- visible: "visible",
391
- allowed: true,
392
- reason: "allowed",
393
- },
394
- ],
395
- };
396
- }
@@ -0,0 +1,49 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import { systemFoundationModule } from "@windy/modules";
3
+ import { routes } from "./index";
4
+
5
+ describe("Manifest 菜单与 Router 契约", () => {
6
+ test("路由默认要求登录,只有显式 public 的页面允许匿名访问", () => {
7
+ const publicRoutes = routes.filter((route) => route.meta?.public === true);
8
+
9
+ expect(publicRoutes.map((route) => route.path)).toEqual([
10
+ "/login",
11
+ "/service-unavailable",
12
+ ]);
13
+ expect(routes.find((route) => route.path === "/")?.meta?.public).not.toBe(
14
+ true,
15
+ );
16
+ expect(
17
+ routes.find((route) => route.path === "/admin")?.meta?.public,
18
+ ).not.toBe(true);
19
+ });
20
+
21
+ test("每个 Admin Route 都由真实页面实现且 Guard 来自 Manifest", () => {
22
+ const manifests = [systemFoundationModule()];
23
+ const definitions = manifests.flatMap((manifest) => manifest.adminRoutes);
24
+ const implementationByKey = new Map(
25
+ routes.flatMap((route) => {
26
+ const key = route.meta?.manifestRouteKey;
27
+ return typeof key === "string" ? [[key, route] as const] : [];
28
+ }),
29
+ );
30
+
31
+ expect([...implementationByKey.keys()].sort()).toEqual(
32
+ definitions.map(({ key }) => key).sort(),
33
+ );
34
+ for (const definition of definitions) {
35
+ const route = implementationByKey.get(definition.key);
36
+ expect(route?.path).toBe(definition.path);
37
+ expect(route?.meta?.title).toBe(definition.title);
38
+ if (definition.directAccess !== "authenticated") {
39
+ expect(route?.meta?.permissionKey).toBe(definition.permissionKey);
40
+ expect(route?.meta?.featureKey).toBe(definition.featureKey);
41
+ }
42
+ }
43
+ expect(definitions.some(({ path }) => path === "/admin/profile")).toBe(
44
+ true,
45
+ );
46
+ expect(definitions.some(({ path }) => path === "/work-orders")).toBe(false);
47
+ expect(routes.some(({ path }) => path === "/work-orders")).toBe(false);
48
+ });
49
+ });
@@ -0,0 +1,75 @@
1
+ import type { WebAccessSnapshot } from "@/layout/navigation";
2
+ // @windy-module system.license begin
3
+ import type { PlatformLicenseSnapshot } from "@/services/license-api";
4
+ // @windy-module system.license end
5
+
6
+ export function provider(snapshot: WebAccessSnapshot) {
7
+ return { refresh: async () => snapshot };
8
+ }
9
+
10
+ // @windy-module system.license begin
11
+ export function licenseProvider(restricted = false) {
12
+ const snapshot = licenseSnapshot(restricted);
13
+ return { refresh: async () => snapshot };
14
+ }
15
+
16
+ export function licenseSnapshot(restricted = false): PlatformLicenseSnapshot {
17
+ return {
18
+ status: restricted ? "expired" : "valid",
19
+ restricted,
20
+ reminder: { level: "none" },
21
+ canViewDetails: false,
22
+ };
23
+ }
24
+ // @windy-module system.license end
25
+
26
+ export function authProvider(
27
+ phase: "authenticated" | "anonymous" = "authenticated",
28
+ ) {
29
+ let currentPhase = phase;
30
+ const actor = accessSnapshot().actor;
31
+ return {
32
+ actor,
33
+ ensureInitialized: async () => ({
34
+ phase: currentPhase,
35
+ actor: currentPhase === "authenticated" ? actor : undefined,
36
+ }),
37
+ clear() {
38
+ currentPhase = "anonymous";
39
+ },
40
+ };
41
+ }
42
+
43
+ export function accessSnapshot(): WebAccessSnapshot {
44
+ return {
45
+ actor: {
46
+ id: "user_1",
47
+ type: "user",
48
+ name: "审计员",
49
+ roleCodes: ["auditor"],
50
+ permissionKeys: [
51
+ "system.admin.access",
52
+ "system.audit.read",
53
+ "system.feature.read",
54
+ ],
55
+ },
56
+ menus: [],
57
+ permissions: [],
58
+ features: [
59
+ {
60
+ key: "system.audit",
61
+ enabled: true,
62
+ visible: "visible",
63
+ allowed: true,
64
+ reason: "allowed",
65
+ },
66
+ {
67
+ key: "system.rbac",
68
+ enabled: true,
69
+ visible: "visible",
70
+ allowed: true,
71
+ reason: "allowed",
72
+ },
73
+ ],
74
+ };
75
+ }
@@ -10,7 +10,7 @@
10
10
  ],
11
11
  "scripts": {
12
12
  "prepare": "husky",
13
- "dev": "bun run --cwd apps/web dev",
13
+ "dev": "bun run scripts/dev.ts",
14
14
  "dev:web": "bun run --cwd apps/web dev",
15
15
  "dev:server": "bun run --cwd apps/server dev",
16
16
  "dev:license": "bun run --cwd apps/license dev",
@@ -1,19 +1,24 @@
1
1
  // @windy-module system.license begin
2
2
  import { sql } from "drizzle-orm";
3
+ import { timestamp as licenseTimestamp } from "drizzle-orm/pg-core";
4
+ import { idColumn as licenseIdColumn } from "./common.js";
3
5
  // @windy-module system.license end
6
+ // @windy-module system.audit begin
7
+ import { timestamp as auditTimestamp } from "drizzle-orm/pg-core";
8
+ import { idColumn as auditIdColumn } from "./common.js";
9
+ // @windy-module system.audit end
4
10
  import {
5
11
  boolean,
6
12
  index,
7
13
  jsonb,
8
14
  pgTable,
9
15
  text,
10
- timestamp,
11
16
  // @windy-module system.license begin
12
17
  uniqueIndex,
13
18
  // @windy-module system.license end
14
19
  varchar,
15
20
  } from "drizzle-orm/pg-core";
16
- import { auditColumns, idColumn, statusColumn } from "./common.js";
21
+ import { auditColumns, statusColumn } from "./common.js";
17
22
 
18
23
  export const featureFlags = pgTable(
19
24
  "feature_flags",
@@ -43,7 +48,7 @@ export const featureFlags = pgTable(
43
48
  export const licenseOperators = pgTable(
44
49
  "license_operators",
45
50
  {
46
- id: idColumn().primaryKey(),
51
+ id: licenseIdColumn().primaryKey(),
47
52
  username: varchar("username", { length: 80 }).notNull(),
48
53
  displayName: varchar("display_name", { length: 120 }).notNull(),
49
54
  tokenHash: varchar("token_hash", { length: 128 }),
@@ -51,13 +56,13 @@ export const licenseOperators = pgTable(
51
56
  role: varchar("role", { length: 40 }).notNull().default("readonly"),
52
57
  permissions: jsonb("permissions").$type<string[]>().notNull(),
53
58
  status: statusColumn().notNull(),
54
- lastUsedAt: timestamp("last_used_at", { withTimezone: true }),
55
- createdAt: timestamp("created_at", { withTimezone: true })
59
+ lastUsedAt: licenseTimestamp("last_used_at", { withTimezone: true }),
60
+ createdAt: licenseTimestamp("created_at", { withTimezone: true })
56
61
  .notNull()
57
62
  .defaultNow(),
58
- updatedAt: timestamp("updated_at", { withTimezone: true }),
63
+ updatedAt: licenseTimestamp("updated_at", { withTimezone: true }),
59
64
  updatedBy: varchar("updated_by", { length: 64 }),
60
- disabledAt: timestamp("disabled_at", { withTimezone: true }),
65
+ disabledAt: licenseTimestamp("disabled_at", { withTimezone: true }),
61
66
  disabledBy: varchar("disabled_by", { length: 64 }),
62
67
  },
63
68
  (table) => [
@@ -69,18 +74,18 @@ export const licenseOperators = pgTable(
69
74
  export const licenseOperatorSessions = pgTable(
70
75
  "license_operator_sessions",
71
76
  {
72
- id: idColumn().primaryKey(),
77
+ id: licenseIdColumn().primaryKey(),
73
78
  operatorId: varchar("operator_id", { length: 64 })
74
79
  .notNull()
75
80
  .references(() => licenseOperators.id, { onDelete: "restrict" }),
76
81
  tokenHash: varchar("token_hash", { length: 128 }).notNull(),
77
82
  status: statusColumn().notNull().default("active"),
78
- issuedAt: timestamp("issued_at", { withTimezone: true })
83
+ issuedAt: licenseTimestamp("issued_at", { withTimezone: true })
79
84
  .notNull()
80
85
  .defaultNow(),
81
- expiresAt: timestamp("expires_at", { withTimezone: true }).notNull(),
82
- revokedAt: timestamp("revoked_at", { withTimezone: true }),
83
- lastSeenAt: timestamp("last_seen_at", { withTimezone: true }),
86
+ expiresAt: licenseTimestamp("expires_at", { withTimezone: true }).notNull(),
87
+ revokedAt: licenseTimestamp("revoked_at", { withTimezone: true }),
88
+ lastSeenAt: licenseTimestamp("last_seen_at", { withTimezone: true }),
84
89
  ip: varchar("ip", { length: 80 }),
85
90
  userAgent: text("user_agent"),
86
91
  },
@@ -95,7 +100,7 @@ export const licenseOperatorSessions = pgTable(
95
100
  export const licenseKeys = pgTable(
96
101
  "license_keys",
97
102
  {
98
- id: idColumn().primaryKey(),
103
+ id: licenseIdColumn().primaryKey(),
99
104
  keyId: varchar("key_id", { length: 160 }).notNull(),
100
105
  publicKeyPem: text("public_key_pem").notNull(),
101
106
  algorithm: varchar("algorithm", { length: 40 })
@@ -103,11 +108,11 @@ export const licenseKeys = pgTable(
103
108
  .default("ED25519"),
104
109
  status: statusColumn().notNull(),
105
110
  rotatedFromKeyId: varchar("rotated_from_key_id", { length: 160 }),
106
- createdAt: timestamp("created_at", { withTimezone: true }).notNull(),
111
+ createdAt: licenseTimestamp("created_at", { withTimezone: true }).notNull(),
107
112
  createdBy: varchar("created_by", { length: 64 }).notNull(),
108
- retiredAt: timestamp("retired_at", { withTimezone: true }),
113
+ retiredAt: licenseTimestamp("retired_at", { withTimezone: true }),
109
114
  retiredBy: varchar("retired_by", { length: 64 }),
110
- retainUntil: timestamp("retain_until", { withTimezone: true }),
115
+ retainUntil: licenseTimestamp("retain_until", { withTimezone: true }),
111
116
  },
112
117
  (table) => [
113
118
  uniqueIndex("uk_license_keys_key_id").on(table.keyId),
@@ -121,13 +126,13 @@ export const licenseKeys = pgTable(
121
126
  export const licenseKeyCompatibilities = pgTable(
122
127
  "license_key_compatibilities",
123
128
  {
124
- id: idColumn().primaryKey(),
129
+ id: licenseIdColumn().primaryKey(),
125
130
  productCode: varchar("product_code", { length: 120 }).notNull(),
126
131
  softwareVersion: varchar("software_version", { length: 80 }).notNull(),
127
132
  publicKeyId: varchar("public_key_id", { length: 160 })
128
133
  .notNull()
129
134
  .references(() => licenseKeys.keyId, { onDelete: "restrict" }),
130
- declaredAt: timestamp("declared_at", { withTimezone: true })
135
+ declaredAt: licenseTimestamp("declared_at", { withTimezone: true })
131
136
  .notNull()
132
137
  .defaultNow(),
133
138
  declaredBy: varchar("declared_by", { length: 64 }).notNull(),
@@ -149,12 +154,12 @@ export const licenseKeyCompatibilities = pgTable(
149
154
  export const licenseGovernanceEvents = pgTable(
150
155
  "license_governance_events",
151
156
  {
152
- id: idColumn().primaryKey(),
157
+ id: licenseIdColumn().primaryKey(),
153
158
  type: varchar("type", { length: 100 }).notNull(),
154
159
  resourceType: varchar("resource_type", { length: 80 }).notNull(),
155
160
  resourceId: varchar("resource_id", { length: 160 }).notNull(),
156
161
  actorId: varchar("actor_id", { length: 64 }).notNull(),
157
- occurredAt: timestamp("occurred_at", { withTimezone: true })
162
+ occurredAt: licenseTimestamp("occurred_at", { withTimezone: true })
158
163
  .notNull()
159
164
  .defaultNow(),
160
165
  payload: jsonb("payload").$type<Record<string, unknown>>().notNull(),
@@ -178,7 +183,7 @@ export const licenseGovernanceEvents = pgTable(
178
183
  export const auditLogs = pgTable(
179
184
  "audit_logs",
180
185
  {
181
- id: idColumn().primaryKey(),
186
+ id: auditIdColumn().primaryKey(),
182
187
  actorId: varchar("actor_id", { length: 64 }),
183
188
  action: varchar("action", { length: 120 }).notNull(),
184
189
  resource: varchar("resource", { length: 160 }).notNull(),
@@ -186,7 +191,7 @@ export const auditLogs = pgTable(
186
191
  ip: varchar("ip", { length: 64 }),
187
192
  userAgent: text("user_agent"),
188
193
  metadata: jsonb("metadata").$type<Record<string, unknown>>(),
189
- createdAt: timestamp("created_at", { withTimezone: true })
194
+ createdAt: auditTimestamp("created_at", { withTimezone: true })
190
195
  .notNull()
191
196
  .defaultNow(),
192
197
  },
@@ -1,43 +1,57 @@
1
+ // 即使两个可选模块都被裁剪,也要保持该文件可由 schema barrel 导出。
2
+ export {};
3
+
4
+ // @windy-module system.configuration begin
1
5
  import type { ModuleConfigDiffEntry, ModuleConfigValues } from "@windy/shared";
2
6
  import {
3
7
  index,
4
8
  integer,
5
9
  jsonb,
6
- pgTable,
7
- text,
10
+ pgTable as configTable,
8
11
  timestamp,
9
12
  uniqueIndex,
10
- varchar,
13
+ varchar as configVarchar,
14
+ } from "drizzle-orm/pg-core";
15
+ // @windy-module system.configuration end
16
+ // @windy-module system.settings begin
17
+ import {
18
+ pgTable as settingsTable,
19
+ text,
20
+ varchar as settingsVarchar,
11
21
  } from "drizzle-orm/pg-core";
12
22
  import { auditColumns } from "./common.js";
23
+ // @windy-module system.settings end
13
24
 
14
25
  // @windy-module system.settings begin
15
- export const platformBrandingSettings = pgTable("platform_branding_settings", {
16
- id: varchar("id", { length: 64 }).primaryKey(),
17
- softwareName: varchar("software_name", { length: 120 }).notNull(),
18
- logoUrl: text("logo_url"),
19
- description: varchar("description", { length: 500 }),
20
- ...auditColumns,
21
- });
26
+ export const platformBrandingSettings = settingsTable(
27
+ "platform_branding_settings",
28
+ {
29
+ id: settingsVarchar("id", { length: 64 }).primaryKey(),
30
+ softwareName: settingsVarchar("software_name", { length: 120 }).notNull(),
31
+ logoUrl: text("logo_url"),
32
+ description: settingsVarchar("description", { length: 500 }),
33
+ ...auditColumns,
34
+ },
35
+ );
22
36
  // @windy-module system.settings end
23
37
 
24
38
  // @windy-module system.configuration begin
25
- export const moduleConfigVersions = pgTable(
39
+ export const moduleConfigVersions = configTable(
26
40
  "module_config_versions",
27
41
  {
28
- id: varchar("id", { length: 64 }).primaryKey(),
29
- moduleKey: varchar("module_key", { length: 80 }).notNull(),
42
+ id: configVarchar("id", { length: 64 }).primaryKey(),
43
+ moduleKey: configVarchar("module_key", { length: 80 }).notNull(),
30
44
  version: integer("version").notNull(),
31
45
  values: jsonb("values").$type<ModuleConfigValues>().notNull(),
32
46
  diff: jsonb("diff").$type<ModuleConfigDiffEntry[]>().notNull(),
33
- applyStatus: varchar("apply_status", { length: 24 }).notNull(),
34
- applyError: varchar("apply_error", { length: 500 }),
47
+ applyStatus: configVarchar("apply_status", { length: 24 }).notNull(),
48
+ applyError: configVarchar("apply_error", { length: 500 }),
35
49
  rollbackOfVersion: integer("rollback_of_version"),
36
- reason: varchar("reason", { length: 500 }),
50
+ reason: configVarchar("reason", { length: 500 }),
37
51
  createdAt: timestamp("created_at", { withTimezone: true })
38
52
  .notNull()
39
53
  .defaultNow(),
40
- createdBy: varchar("created_by", { length: 64 }).notNull(),
54
+ createdBy: configVarchar("created_by", { length: 64 }).notNull(),
41
55
  appliedAt: timestamp("applied_at", { withTimezone: true }),
42
56
  },
43
57
  (table) => [
@@ -33,6 +33,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
33
33
  "system.department.read",
34
34
  "system.rbac",
35
35
  ),
36
+ // @windy-module system.settings begin
36
37
  {
37
38
  ...route(
38
39
  "system.settings",
@@ -44,6 +45,7 @@ export const systemAdminRoutes: ModuleManifest["adminRoutes"] = [
44
45
  ),
45
46
  directAccess: "admin-admission",
46
47
  },
48
+ // @windy-module system.settings end
47
49
  route(
48
50
  "system.menu",
49
51
  "/admin/system/menus",