@southwind-ai/database 0.1.2 → 0.2.0
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/index.ts +1 -0
- package/package.json +5 -2
- package/src/postgres-module-migrations.ts +69 -0
- package/src/runner.ts +17 -2
- package/src/schema/identity.ts +7 -0
- package/src/schema/index.ts +0 -3
- package/src/schema/migration-history.ts +3 -0
- package/src/schema/notifications.ts +135 -0
- package/src/schema/platform-settings.ts +13 -0
- package/src/sql-store.ts +13 -3
- package/src/store.ts +2 -0
- package/src/schema/work-orders.ts +0 -23
package/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@southwind-ai/database",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"license": "UNLICENSED",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"typecheck": "tsc --noEmit --project tsconfig.json"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@southwind-ai/shared": "0.
|
|
18
|
+
"@southwind-ai/shared": "0.2.0",
|
|
19
19
|
"drizzle-orm": "^0.45.2"
|
|
20
20
|
},
|
|
21
21
|
"exports": {
|
|
@@ -23,5 +23,8 @@
|
|
|
23
23
|
},
|
|
24
24
|
"publishConfig": {
|
|
25
25
|
"access": "public"
|
|
26
|
+
},
|
|
27
|
+
"devDependencies": {
|
|
28
|
+
"pg": "^8.22.0"
|
|
26
29
|
}
|
|
27
30
|
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import type { MigrationBundle } from "@southwind-ai/shared";
|
|
2
|
+
import { PlatformMigrationRunner, type MigrationRunReport } from "./runner.js";
|
|
3
|
+
import {
|
|
4
|
+
createPostgresStatementExecutor,
|
|
5
|
+
PostgresMigrationHistoryStore,
|
|
6
|
+
type QueryableClient,
|
|
7
|
+
} from "./sql-store.js";
|
|
8
|
+
|
|
9
|
+
const MODULE_MIGRATION_LOCK = "windy.module-migrations.v1";
|
|
10
|
+
|
|
11
|
+
export interface PostgresTransactionClient extends QueryableClient {
|
|
12
|
+
release(): void;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface PostgresTransactionPool {
|
|
16
|
+
connect(): Promise<PostgresTransactionClient>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PostgresMigrationBundleOptions {
|
|
20
|
+
pool: PostgresTransactionPool;
|
|
21
|
+
bundles: readonly MigrationBundle[];
|
|
22
|
+
now?: () => Date;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function runPostgresMigrationBundles(
|
|
26
|
+
options: PostgresMigrationBundleOptions,
|
|
27
|
+
): Promise<MigrationRunReport> {
|
|
28
|
+
const client = await options.pool.connect();
|
|
29
|
+
let transactionOpen = false;
|
|
30
|
+
try {
|
|
31
|
+
await client.query("begin");
|
|
32
|
+
transactionOpen = true;
|
|
33
|
+
await client.query("select pg_advisory_xact_lock(hashtext($1))", [
|
|
34
|
+
MODULE_MIGRATION_LOCK,
|
|
35
|
+
]);
|
|
36
|
+
const runner = new PlatformMigrationRunner({
|
|
37
|
+
dialect: "postgresql",
|
|
38
|
+
store: new PostgresMigrationHistoryStore(client),
|
|
39
|
+
execute: createPostgresStatementExecutor(client),
|
|
40
|
+
now: options.now,
|
|
41
|
+
recordFailures: false,
|
|
42
|
+
});
|
|
43
|
+
const report = await runner.runBundles(options.bundles);
|
|
44
|
+
const failure = report.failed[0];
|
|
45
|
+
if (failure) {
|
|
46
|
+
throw new Error(
|
|
47
|
+
`Module Migration ${failure.id} 执行失败:${failure.error}`,
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
await client.query("commit");
|
|
51
|
+
transactionOpen = false;
|
|
52
|
+
return report;
|
|
53
|
+
} catch (error) {
|
|
54
|
+
if (transactionOpen) await rollbackWithoutMasking(client);
|
|
55
|
+
throw error;
|
|
56
|
+
} finally {
|
|
57
|
+
client.release();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function rollbackWithoutMasking(
|
|
62
|
+
client: PostgresTransactionClient,
|
|
63
|
+
): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
await client.query("rollback");
|
|
66
|
+
} catch {
|
|
67
|
+
// 原始 migration 错误决定启动失败原因,连接释放后由 Pool 负责处置。
|
|
68
|
+
}
|
|
69
|
+
}
|
package/src/runner.ts
CHANGED
|
@@ -17,11 +17,14 @@ export interface MigrationRunnerOptions {
|
|
|
17
17
|
store: MigrationHistoryStore;
|
|
18
18
|
execute?: (statement: MigrationStatement) => Promise<void>;
|
|
19
19
|
now?: () => Date;
|
|
20
|
+
recordFailures?: boolean;
|
|
20
21
|
}
|
|
21
22
|
|
|
22
23
|
export interface MigrationPlanItem {
|
|
23
24
|
id: string;
|
|
24
25
|
module: string;
|
|
26
|
+
moduleVersion: string;
|
|
27
|
+
migrationOrder: number;
|
|
25
28
|
description: string;
|
|
26
29
|
direction: "up" | "down";
|
|
27
30
|
checksum: string;
|
|
@@ -62,9 +65,15 @@ export class PlatformMigrationRunner {
|
|
|
62
65
|
): Promise<MigrationPlanItem[]> {
|
|
63
66
|
const ordered = validateAndOrderBundles(bundles);
|
|
64
67
|
const applied = appliedById(await this.options.store.listApplied());
|
|
68
|
+
let migrationOrder = 0;
|
|
65
69
|
return ordered.flatMap((bundle) =>
|
|
66
70
|
orderBundleMigrations(bundle).map((migration) =>
|
|
67
|
-
planItem(
|
|
71
|
+
planItem(
|
|
72
|
+
migration,
|
|
73
|
+
bundle.version,
|
|
74
|
+
migrationOrder++,
|
|
75
|
+
applied.get(migration.id),
|
|
76
|
+
),
|
|
68
77
|
),
|
|
69
78
|
);
|
|
70
79
|
}
|
|
@@ -119,7 +128,7 @@ export class PlatformMigrationRunner {
|
|
|
119
128
|
} catch (error) {
|
|
120
129
|
const message = error instanceof Error ? error.message : String(error);
|
|
121
130
|
failed.push({ id: item.id, error: message });
|
|
122
|
-
if (!dryRun) {
|
|
131
|
+
if (!dryRun && this.options.recordFailures !== false) {
|
|
123
132
|
await this.recordResult(item, stepStartedAt, stepStatements, message);
|
|
124
133
|
}
|
|
125
134
|
break;
|
|
@@ -147,6 +156,8 @@ export class PlatformMigrationRunner {
|
|
|
147
156
|
await this.options.store.record({
|
|
148
157
|
id: item.id,
|
|
149
158
|
module: item.module,
|
|
159
|
+
moduleVersion: item.moduleVersion,
|
|
160
|
+
migrationOrder: item.migrationOrder,
|
|
150
161
|
direction: item.direction,
|
|
151
162
|
status: error ? "failed" : "applied",
|
|
152
163
|
checksum: item.checksum,
|
|
@@ -164,6 +175,8 @@ function appliedById(entries: readonly MigrationHistoryEntry[]) {
|
|
|
164
175
|
|
|
165
176
|
function planItem(
|
|
166
177
|
migration: BundleMigrationDefinition,
|
|
178
|
+
moduleVersion: string,
|
|
179
|
+
migrationOrder: number,
|
|
167
180
|
applied: MigrationHistoryEntry | undefined,
|
|
168
181
|
): MigrationPlanItem {
|
|
169
182
|
if (applied?.module !== undefined && applied.module !== migration.module) {
|
|
@@ -182,6 +195,8 @@ function planItem(
|
|
|
182
195
|
return {
|
|
183
196
|
id: migration.id,
|
|
184
197
|
module: migration.module,
|
|
198
|
+
moduleVersion,
|
|
199
|
+
migrationOrder,
|
|
185
200
|
description: migration.description,
|
|
186
201
|
direction: migration.direction,
|
|
187
202
|
checksum: migration.checksum,
|
package/src/schema/identity.ts
CHANGED
|
@@ -27,7 +27,14 @@ export const departments = pgTable(
|
|
|
27
27
|
},
|
|
28
28
|
(table) => [
|
|
29
29
|
uniqueIndex("uk_departments_code").on(table.code),
|
|
30
|
+
uniqueIndex("uk_departments_single_root")
|
|
31
|
+
.on(sql`((1))`)
|
|
32
|
+
.where(sql`${table.parentId} is null and ${table.deletedAt} is null`),
|
|
30
33
|
index("idx_departments_parent").on(table.parentId),
|
|
34
|
+
check(
|
|
35
|
+
"departments_parent_not_self",
|
|
36
|
+
sql`${table.parentId} is null or ${table.parentId} <> ${table.id}`,
|
|
37
|
+
),
|
|
31
38
|
],
|
|
32
39
|
);
|
|
33
40
|
|
package/src/schema/index.ts
CHANGED
|
@@ -25,9 +25,6 @@ export * from "./platform-settings.js";
|
|
|
25
25
|
// @windy-module system.scheduler begin
|
|
26
26
|
export * from "./scheduler.js";
|
|
27
27
|
// @windy-module system.scheduler end
|
|
28
|
-
// @windy-module work-order begin
|
|
29
|
-
export * from "./work-orders.js";
|
|
30
|
-
// @windy-module work-order end
|
|
31
28
|
// @windy-module system.workflow begin
|
|
32
29
|
export * from "./workflow.js";
|
|
33
30
|
// @windy-module system.workflow end
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
index,
|
|
3
|
+
integer,
|
|
3
4
|
jsonb,
|
|
4
5
|
pgTable,
|
|
5
6
|
text,
|
|
@@ -12,6 +13,8 @@ export const windyMigrationHistory = pgTable(
|
|
|
12
13
|
{
|
|
13
14
|
id: varchar("id", { length: 160 }).primaryKey(),
|
|
14
15
|
module: varchar("module", { length: 96 }).notNull(),
|
|
16
|
+
moduleVersion: varchar("module_version", { length: 64 }),
|
|
17
|
+
migrationOrder: integer("migration_order"),
|
|
15
18
|
direction: varchar("direction", { length: 16 }).notNull(),
|
|
16
19
|
status: varchar("status", { length: 16 }).notNull(),
|
|
17
20
|
checksum: varchar("checksum", { length: 128 }),
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
boolean,
|
|
2
3
|
index,
|
|
4
|
+
integer,
|
|
3
5
|
pgTable,
|
|
4
6
|
primaryKey,
|
|
5
7
|
text,
|
|
@@ -9,6 +11,10 @@ import {
|
|
|
9
11
|
varchar,
|
|
10
12
|
} from "drizzle-orm/pg-core";
|
|
11
13
|
import type { TargetRef } from "@southwind-ai/shared";
|
|
14
|
+
import type {
|
|
15
|
+
ExternalNotificationChannel,
|
|
16
|
+
NotificationDeliveryStatus,
|
|
17
|
+
} from "@southwind-ai/shared";
|
|
12
18
|
import { auditColumns, idColumn, statusColumn } from "./common.js";
|
|
13
19
|
import { users } from "./identity.js";
|
|
14
20
|
|
|
@@ -22,15 +28,32 @@ export const platformNotifications = pgTable(
|
|
|
22
28
|
category: varchar("category", { length: 40 }).notNull(),
|
|
23
29
|
status: statusColumn().notNull(),
|
|
24
30
|
publishedAt: timestamp("published_at", { withTimezone: true }).notNull(),
|
|
31
|
+
scheduledFor: timestamp("scheduled_for", { withTimezone: true }),
|
|
25
32
|
archivedAt: timestamp("archived_at", { withTimezone: true }),
|
|
26
33
|
archivedBy: varchar("archived_by", { length: 64 }),
|
|
27
34
|
target: jsonb("target").$type<TargetRef>(),
|
|
28
35
|
recipientUserId: varchar("recipient_user_id", { length: 64 }),
|
|
36
|
+
recipientUserIds: jsonb("recipient_user_ids")
|
|
37
|
+
.$type<string[]>()
|
|
38
|
+
.notNull()
|
|
39
|
+
.default([]),
|
|
29
40
|
recipientRoleCodes: jsonb("recipient_role_codes")
|
|
30
41
|
.$type<string[]>()
|
|
31
42
|
.notNull()
|
|
32
43
|
.default([]),
|
|
44
|
+
recipientDepartmentIds: jsonb("recipient_department_ids")
|
|
45
|
+
.$type<string[]>()
|
|
46
|
+
.notNull()
|
|
47
|
+
.default([]),
|
|
48
|
+
includeChildDepartments: boolean("include_child_departments")
|
|
49
|
+
.notNull()
|
|
50
|
+
.default(false),
|
|
51
|
+
deliveryChannels: jsonb("delivery_channels")
|
|
52
|
+
.$type<ExternalNotificationChannel[]>()
|
|
53
|
+
.notNull()
|
|
54
|
+
.default([]),
|
|
33
55
|
sourceKey: varchar("source_key", { length: 180 }),
|
|
56
|
+
revision: integer("revision").notNull().default(1),
|
|
34
57
|
...auditColumns,
|
|
35
58
|
},
|
|
36
59
|
(table) => [
|
|
@@ -42,6 +65,96 @@ export const platformNotifications = pgTable(
|
|
|
42
65
|
],
|
|
43
66
|
);
|
|
44
67
|
|
|
68
|
+
export const notificationPreferences = pgTable("notification_preferences", {
|
|
69
|
+
userId: varchar("user_id", { length: 64 })
|
|
70
|
+
.primaryKey()
|
|
71
|
+
.references(() => users.id, { onDelete: "restrict" }),
|
|
72
|
+
enabledChannels: jsonb("enabled_channels")
|
|
73
|
+
.$type<ExternalNotificationChannel[]>()
|
|
74
|
+
.notNull()
|
|
75
|
+
.default([]),
|
|
76
|
+
disabledCategories: jsonb("disabled_categories")
|
|
77
|
+
.$type<string[]>()
|
|
78
|
+
.notNull()
|
|
79
|
+
.default([]),
|
|
80
|
+
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
81
|
+
.notNull()
|
|
82
|
+
.defaultNow(),
|
|
83
|
+
updatedBy: varchar("updated_by", { length: 64 }).notNull(),
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
export const notificationDeliveryOutbox = pgTable(
|
|
87
|
+
"notification_delivery_outbox",
|
|
88
|
+
{
|
|
89
|
+
id: idColumn().primaryKey(),
|
|
90
|
+
notificationId: varchar("notification_id", { length: 64 })
|
|
91
|
+
.notNull()
|
|
92
|
+
.references(() => platformNotifications.id, { onDelete: "restrict" }),
|
|
93
|
+
userId: varchar("user_id", { length: 64 })
|
|
94
|
+
.notNull()
|
|
95
|
+
.references(() => users.id, { onDelete: "restrict" }),
|
|
96
|
+
channel: varchar("channel", { length: 16 })
|
|
97
|
+
.$type<ExternalNotificationChannel>()
|
|
98
|
+
.notNull(),
|
|
99
|
+
status: varchar("status", { length: 16 })
|
|
100
|
+
.$type<NotificationDeliveryStatus>()
|
|
101
|
+
.notNull(),
|
|
102
|
+
subject: varchar("subject", { length: 120 }).notNull(),
|
|
103
|
+
content: text("content").notNull(),
|
|
104
|
+
attemptCount: integer("attempt_count").notNull().default(0),
|
|
105
|
+
nextAttemptAt: timestamp("next_attempt_at", { withTimezone: true })
|
|
106
|
+
.notNull()
|
|
107
|
+
.defaultNow(),
|
|
108
|
+
leaseToken: varchar("lease_token", { length: 64 }),
|
|
109
|
+
leaseExpiresAt: timestamp("lease_expires_at", { withTimezone: true }),
|
|
110
|
+
deliveredAt: timestamp("delivered_at", { withTimezone: true }),
|
|
111
|
+
providerMessageId: varchar("provider_message_id", { length: 180 }),
|
|
112
|
+
lastErrorCode: varchar("last_error_code", { length: 64 }),
|
|
113
|
+
createdAt: timestamp("created_at", { withTimezone: true })
|
|
114
|
+
.notNull()
|
|
115
|
+
.defaultNow(),
|
|
116
|
+
updatedAt: timestamp("updated_at", { withTimezone: true })
|
|
117
|
+
.notNull()
|
|
118
|
+
.defaultNow(),
|
|
119
|
+
},
|
|
120
|
+
(table) => [
|
|
121
|
+
uniqueIndex("uk_notification_delivery_target").on(
|
|
122
|
+
table.notificationId,
|
|
123
|
+
table.userId,
|
|
124
|
+
table.channel,
|
|
125
|
+
),
|
|
126
|
+
index("idx_notification_delivery_claim").on(
|
|
127
|
+
table.status,
|
|
128
|
+
table.nextAttemptAt,
|
|
129
|
+
table.leaseExpiresAt,
|
|
130
|
+
),
|
|
131
|
+
index("idx_notification_delivery_notification").on(table.notificationId),
|
|
132
|
+
],
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
export const notificationRevisions = pgTable(
|
|
136
|
+
"notification_revisions",
|
|
137
|
+
{
|
|
138
|
+
notificationId: varchar("notification_id", { length: 64 })
|
|
139
|
+
.notNull()
|
|
140
|
+
.references(() => platformNotifications.id, { onDelete: "restrict" }),
|
|
141
|
+
revision: integer("revision").notNull(),
|
|
142
|
+
title: varchar("title", { length: 120 }).notNull(),
|
|
143
|
+
summary: varchar("summary", { length: 240 }).notNull(),
|
|
144
|
+
content: text("content").notNull(),
|
|
145
|
+
category: varchar("category", { length: 40 }).notNull(),
|
|
146
|
+
editedAt: timestamp("edited_at", { withTimezone: true }).notNull(),
|
|
147
|
+
editedBy: varchar("edited_by", { length: 64 }).notNull(),
|
|
148
|
+
},
|
|
149
|
+
(table) => [
|
|
150
|
+
primaryKey({ columns: [table.notificationId, table.revision] }),
|
|
151
|
+
index("idx_notification_revisions_time").on(
|
|
152
|
+
table.notificationId,
|
|
153
|
+
table.editedAt,
|
|
154
|
+
),
|
|
155
|
+
],
|
|
156
|
+
);
|
|
157
|
+
|
|
45
158
|
export const notificationReads = pgTable(
|
|
46
159
|
"notification_reads",
|
|
47
160
|
{
|
|
@@ -58,3 +171,25 @@ export const notificationReads = pgTable(
|
|
|
58
171
|
index("idx_notification_reads_user").on(table.userId, table.readAt),
|
|
59
172
|
],
|
|
60
173
|
);
|
|
174
|
+
|
|
175
|
+
export const notificationFavorites = pgTable(
|
|
176
|
+
"notification_favorites",
|
|
177
|
+
{
|
|
178
|
+
notificationId: varchar("notification_id", { length: 64 })
|
|
179
|
+
.notNull()
|
|
180
|
+
.references(() => platformNotifications.id, { onDelete: "restrict" }),
|
|
181
|
+
userId: varchar("user_id", { length: 64 })
|
|
182
|
+
.notNull()
|
|
183
|
+
.references(() => users.id, { onDelete: "restrict" }),
|
|
184
|
+
favoritedAt: timestamp("favorited_at", { withTimezone: true })
|
|
185
|
+
.notNull()
|
|
186
|
+
.defaultNow(),
|
|
187
|
+
},
|
|
188
|
+
(table) => [
|
|
189
|
+
primaryKey({ columns: [table.notificationId, table.userId] }),
|
|
190
|
+
index("idx_notification_favorites_user").on(
|
|
191
|
+
table.userId,
|
|
192
|
+
table.favoritedAt,
|
|
193
|
+
),
|
|
194
|
+
],
|
|
195
|
+
);
|
|
@@ -19,11 +19,16 @@ import {
|
|
|
19
19
|
// @windy-module system.settings begin
|
|
20
20
|
import {
|
|
21
21
|
boolean,
|
|
22
|
+
jsonb as settingsJsonb,
|
|
22
23
|
pgTable as settingsTable,
|
|
23
24
|
text,
|
|
24
25
|
varchar as settingsVarchar,
|
|
25
26
|
} from "drizzle-orm/pg-core";
|
|
26
27
|
import { auditColumns } from "./common.js";
|
|
28
|
+
import type {
|
|
29
|
+
WatermarkContentField,
|
|
30
|
+
WatermarkDensity,
|
|
31
|
+
} from "@southwind-ai/shared";
|
|
27
32
|
// @windy-module system.settings end
|
|
28
33
|
|
|
29
34
|
// @windy-module system.settings begin
|
|
@@ -44,6 +49,14 @@ export const platformWatermarkPolicies = settingsTable(
|
|
|
44
49
|
id: settingsVarchar("id", { length: 64 }).primaryKey(),
|
|
45
50
|
enabled: boolean("enabled").notNull(),
|
|
46
51
|
businessPagesEnabled: boolean("business_pages_enabled").notNull(),
|
|
52
|
+
density: settingsVarchar("density", { length: 16 })
|
|
53
|
+
.$type<WatermarkDensity>()
|
|
54
|
+
.default("medium")
|
|
55
|
+
.notNull(),
|
|
56
|
+
contentFields: settingsJsonb("content_fields")
|
|
57
|
+
.$type<WatermarkContentField[]>()
|
|
58
|
+
.default(["username", "displayName"])
|
|
59
|
+
.notNull(),
|
|
47
60
|
customContent: settingsVarchar("custom_content", { length: 80 }).notNull(),
|
|
48
61
|
...auditColumns,
|
|
49
62
|
},
|
package/src/sql-store.ts
CHANGED
|
@@ -15,6 +15,8 @@ export interface QueryableClient {
|
|
|
15
15
|
interface MigrationHistoryRow {
|
|
16
16
|
id: string;
|
|
17
17
|
module: string;
|
|
18
|
+
module_version: string | null;
|
|
19
|
+
migration_order: number | null;
|
|
18
20
|
direction: "up" | "down";
|
|
19
21
|
status: "applied" | "failed";
|
|
20
22
|
checksum: string | null;
|
|
@@ -29,7 +31,8 @@ export class PostgresMigrationHistoryStore implements MigrationHistoryStore {
|
|
|
29
31
|
|
|
30
32
|
async listApplied(): Promise<MigrationHistoryEntry[]> {
|
|
31
33
|
const result = await this.client.query<MigrationHistoryRow>(
|
|
32
|
-
`select id, module,
|
|
34
|
+
`select id, module, module_version, migration_order, direction, status,
|
|
35
|
+
checksum, statements, error, started_at, finished_at
|
|
33
36
|
from windy_migration_history
|
|
34
37
|
where status = $1
|
|
35
38
|
order by finished_at asc`,
|
|
@@ -42,10 +45,13 @@ export class PostgresMigrationHistoryStore implements MigrationHistoryStore {
|
|
|
42
45
|
async record(entry: MigrationHistoryEntry): Promise<void> {
|
|
43
46
|
await this.client.query(
|
|
44
47
|
`insert into windy_migration_history
|
|
45
|
-
(id, module,
|
|
46
|
-
|
|
48
|
+
(id, module, module_version, migration_order, direction, status,
|
|
49
|
+
checksum, statements, error, started_at, finished_at)
|
|
50
|
+
values ($1, $2, $3, $4, $5, $6, $7, $8::jsonb, $9, $10, $11)
|
|
47
51
|
on conflict (id) do update set
|
|
48
52
|
module = excluded.module,
|
|
53
|
+
module_version = excluded.module_version,
|
|
54
|
+
migration_order = excluded.migration_order,
|
|
49
55
|
direction = excluded.direction,
|
|
50
56
|
status = excluded.status,
|
|
51
57
|
checksum = excluded.checksum,
|
|
@@ -56,6 +62,8 @@ export class PostgresMigrationHistoryStore implements MigrationHistoryStore {
|
|
|
56
62
|
[
|
|
57
63
|
entry.id,
|
|
58
64
|
entry.module,
|
|
65
|
+
entry.moduleVersion || null,
|
|
66
|
+
entry.migrationOrder ?? null,
|
|
59
67
|
entry.direction,
|
|
60
68
|
entry.status,
|
|
61
69
|
entry.checksum || null,
|
|
@@ -86,6 +94,8 @@ function mapHistoryRow(row: MigrationHistoryRow): MigrationHistoryEntry {
|
|
|
86
94
|
return {
|
|
87
95
|
id: row.id,
|
|
88
96
|
module: row.module,
|
|
97
|
+
moduleVersion: row.module_version || undefined,
|
|
98
|
+
migrationOrder: row.migration_order ?? undefined,
|
|
89
99
|
direction: row.direction,
|
|
90
100
|
status: row.status,
|
|
91
101
|
startedAt: toIsoString(row.started_at),
|
package/src/store.ts
CHANGED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { index, pgTable, text, varchar } from "drizzle-orm/pg-core";
|
|
2
|
-
import { auditColumns, idColumn, statusColumn } from "./common.js";
|
|
3
|
-
|
|
4
|
-
export const workOrders = pgTable(
|
|
5
|
-
"work_orders",
|
|
6
|
-
{
|
|
7
|
-
id: idColumn().primaryKey(),
|
|
8
|
-
title: varchar("title", { length: 120 }).notNull(),
|
|
9
|
-
description: text("description").notNull(),
|
|
10
|
-
priority: varchar("priority", { length: 24 }).notNull(),
|
|
11
|
-
workflowStatus: varchar("workflow_status", { length: 32 }).notNull(),
|
|
12
|
-
ownerId: varchar("owner_id", { length: 64 }).notNull(),
|
|
13
|
-
departmentId: varchar("department_id", { length: 64 }),
|
|
14
|
-
status: statusColumn().notNull(),
|
|
15
|
-
...auditColumns,
|
|
16
|
-
},
|
|
17
|
-
(table) => [
|
|
18
|
-
index("idx_work_orders_owner").on(table.ownerId),
|
|
19
|
-
index("idx_work_orders_department").on(table.departmentId),
|
|
20
|
-
index("idx_work_orders_status").on(table.status),
|
|
21
|
-
index("idx_work_orders_workflow").on(table.workflowStatus),
|
|
22
|
-
],
|
|
23
|
-
);
|