@takaro/db 0.4.3 → 0.4.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.
- package/dist/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.d.ts +4 -0
- package/dist/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.d.ts.map +1 -0
- package/dist/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.js +70 -0
- package/dist/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.js.map +1 -0
- package/dist/migrations/sql/20251101000000-add-deleted-domain-state.d.ts +4 -0
- package/dist/migrations/sql/20251101000000-add-deleted-domain-state.d.ts.map +1 -0
- package/dist/migrations/sql/20251101000000-add-deleted-domain-state.js +33 -0
- package/dist/migrations/sql/20251101000000-add-deleted-domain-state.js.map +1 -0
- package/package.json +1 -1
- package/src/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.ts +75 -0
- package/src/migrations/sql/20251101000000-add-deleted-domain-state.ts +43 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20251014120000-shop-order-auto-cancel-on-listing-delete.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CA0DlD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAYpD"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
// Step 1: Create trigger function that auto-cancels orders when listing is deleted
|
|
3
|
+
await knex.raw(`
|
|
4
|
+
CREATE OR REPLACE FUNCTION cancel_orders_on_listing_delete()
|
|
5
|
+
RETURNS TRIGGER AS $$
|
|
6
|
+
BEGIN
|
|
7
|
+
-- Only proceed if deletedAt changed from NULL to a value (soft-delete)
|
|
8
|
+
IF OLD."deletedAt" IS NULL AND NEW."deletedAt" IS NOT NULL THEN
|
|
9
|
+
-- Cancel all PAID orders for this listing and refund currency
|
|
10
|
+
-- Aggregate refunds by player to handle multiple orders correctly
|
|
11
|
+
WITH orders_to_cancel AS (
|
|
12
|
+
SELECT
|
|
13
|
+
so.id AS order_id,
|
|
14
|
+
so."playerId",
|
|
15
|
+
NEW."gameServerId",
|
|
16
|
+
NEW.domain AS domain,
|
|
17
|
+
NEW.price * so.amount AS refund_amount
|
|
18
|
+
FROM "shopOrder" so
|
|
19
|
+
WHERE so."listingId" = NEW.id
|
|
20
|
+
AND so.status = 'PAID'
|
|
21
|
+
),
|
|
22
|
+
aggregated_refunds AS (
|
|
23
|
+
SELECT
|
|
24
|
+
"playerId",
|
|
25
|
+
"gameServerId",
|
|
26
|
+
domain,
|
|
27
|
+
SUM(refund_amount) AS total_refund
|
|
28
|
+
FROM orders_to_cancel
|
|
29
|
+
GROUP BY "playerId", "gameServerId", domain
|
|
30
|
+
)
|
|
31
|
+
-- Update playerOnGameServer currency with aggregated total
|
|
32
|
+
UPDATE "playerOnGameServer" pogs
|
|
33
|
+
SET currency = pogs.currency + ar.total_refund
|
|
34
|
+
FROM aggregated_refunds ar
|
|
35
|
+
WHERE pogs."playerId" = ar."playerId"
|
|
36
|
+
AND pogs."gameServerId" = ar."gameServerId"
|
|
37
|
+
AND pogs.domain = ar.domain;
|
|
38
|
+
|
|
39
|
+
-- Update order status to CANCELED
|
|
40
|
+
UPDATE "shopOrder"
|
|
41
|
+
SET status = 'CANCELED'
|
|
42
|
+
WHERE "listingId" = NEW.id
|
|
43
|
+
AND status = 'PAID';
|
|
44
|
+
END IF;
|
|
45
|
+
|
|
46
|
+
RETURN NEW;
|
|
47
|
+
END;
|
|
48
|
+
$$ LANGUAGE plpgsql;
|
|
49
|
+
`);
|
|
50
|
+
// Step 2: Create trigger on shopListing table
|
|
51
|
+
await knex.raw(`
|
|
52
|
+
CREATE TRIGGER auto_cancel_orders_on_listing_delete
|
|
53
|
+
AFTER UPDATE ON "shopListing"
|
|
54
|
+
FOR EACH ROW
|
|
55
|
+
WHEN (OLD."deletedAt" IS NULL AND NEW."deletedAt" IS NOT NULL)
|
|
56
|
+
EXECUTE FUNCTION cancel_orders_on_listing_delete();
|
|
57
|
+
`);
|
|
58
|
+
}
|
|
59
|
+
export async function down(knex) {
|
|
60
|
+
// Drop trigger
|
|
61
|
+
await knex.raw(`
|
|
62
|
+
DROP TRIGGER IF EXISTS auto_cancel_orders_on_listing_delete ON "shopListing";
|
|
63
|
+
`);
|
|
64
|
+
// Drop function
|
|
65
|
+
await knex.raw(`
|
|
66
|
+
DROP FUNCTION IF EXISTS cancel_orders_on_listing_delete();
|
|
67
|
+
`);
|
|
68
|
+
// Note: We don't reverse the data cleanup since those were genuinely bad orders
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=20251014120000-shop-order-auto-cancel-on-listing-delete.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20251014120000-shop-order-auto-cancel-on-listing-delete.js","sourceRoot":"","sources":["../../../src/migrations/sql/20251014120000-shop-order-auto-cancel-on-listing-delete.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,mFAAmF;IACnF,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8Cd,CAAC,CAAC;IAEH,8CAA8C;IAC9C,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;GAMd,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,eAAe;IACf,MAAM,IAAI,CAAC,GAAG,CAAC;;GAEd,CAAC,CAAC;IAEH,gBAAgB;IAChB,MAAM,IAAI,CAAC,GAAG,CAAC;;GAEd,CAAC,CAAC;IAEH,gFAAgF;AAClF,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20251101000000-add-deleted-domain-state.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20251101000000-add-deleted-domain-state.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAoBlD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAkBpD"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
export async function up(knex) {
|
|
2
|
+
// PostgreSQL requires a multi-step process to update enum constraints
|
|
3
|
+
// 1. Add a temporary column with the new enum values
|
|
4
|
+
// 2. Copy data from old column to new column
|
|
5
|
+
// 3. Drop old column
|
|
6
|
+
// 4. Rename new column to old column name
|
|
7
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
8
|
+
table.enum('state_new', ['ACTIVE', 'DISABLED', 'MAINTENANCE', 'DELETED']).notNullable().defaultTo('ACTIVE');
|
|
9
|
+
});
|
|
10
|
+
await knex.raw(`UPDATE domains SET state_new = state::text::varchar`);
|
|
11
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
12
|
+
table.dropColumn('state');
|
|
13
|
+
});
|
|
14
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
15
|
+
table.renameColumn('state_new', 'state');
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function down(knex) {
|
|
19
|
+
// Revert to the original enum values (removing DELETED)
|
|
20
|
+
// First, ensure no domains are in DELETED state (or this will fail)
|
|
21
|
+
await knex.raw(`DELETE FROM domains WHERE state = 'DELETED'`);
|
|
22
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
23
|
+
table.enum('state_new', ['ACTIVE', 'DISABLED', 'MAINTENANCE']).notNullable().defaultTo('ACTIVE');
|
|
24
|
+
});
|
|
25
|
+
await knex.raw(`UPDATE domains SET state_new = state::text::varchar`);
|
|
26
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
27
|
+
table.dropColumn('state');
|
|
28
|
+
});
|
|
29
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
30
|
+
table.renameColumn('state_new', 'state');
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=20251101000000-add-deleted-domain-state.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"20251101000000-add-deleted-domain-state.js","sourceRoot":"","sources":["../../../src/migrations/sql/20251101000000-add-deleted-domain-state.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,sEAAsE;IACtE,qDAAqD;IACrD,6CAA6C;IAC7C,qBAAqB;IACrB,0CAA0C;IAE1C,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,SAAS,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC9G,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IAEtE,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,wDAAwD;IACxD,oEAAoE;IACpE,MAAM,IAAI,CAAC,GAAG,CAAC,6CAA6C,CAAC,CAAC;IAE9D,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,QAAQ,EAAE,UAAU,EAAE,aAAa,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;IACnG,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;IAEtE,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAC5B,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;QAChD,KAAK,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
|
|
3
|
+
export async function up(knex: Knex): Promise<void> {
|
|
4
|
+
// Step 1: Create trigger function that auto-cancels orders when listing is deleted
|
|
5
|
+
await knex.raw(`
|
|
6
|
+
CREATE OR REPLACE FUNCTION cancel_orders_on_listing_delete()
|
|
7
|
+
RETURNS TRIGGER AS $$
|
|
8
|
+
BEGIN
|
|
9
|
+
-- Only proceed if deletedAt changed from NULL to a value (soft-delete)
|
|
10
|
+
IF OLD."deletedAt" IS NULL AND NEW."deletedAt" IS NOT NULL THEN
|
|
11
|
+
-- Cancel all PAID orders for this listing and refund currency
|
|
12
|
+
-- Aggregate refunds by player to handle multiple orders correctly
|
|
13
|
+
WITH orders_to_cancel AS (
|
|
14
|
+
SELECT
|
|
15
|
+
so.id AS order_id,
|
|
16
|
+
so."playerId",
|
|
17
|
+
NEW."gameServerId",
|
|
18
|
+
NEW.domain AS domain,
|
|
19
|
+
NEW.price * so.amount AS refund_amount
|
|
20
|
+
FROM "shopOrder" so
|
|
21
|
+
WHERE so."listingId" = NEW.id
|
|
22
|
+
AND so.status = 'PAID'
|
|
23
|
+
),
|
|
24
|
+
aggregated_refunds AS (
|
|
25
|
+
SELECT
|
|
26
|
+
"playerId",
|
|
27
|
+
"gameServerId",
|
|
28
|
+
domain,
|
|
29
|
+
SUM(refund_amount) AS total_refund
|
|
30
|
+
FROM orders_to_cancel
|
|
31
|
+
GROUP BY "playerId", "gameServerId", domain
|
|
32
|
+
)
|
|
33
|
+
-- Update playerOnGameServer currency with aggregated total
|
|
34
|
+
UPDATE "playerOnGameServer" pogs
|
|
35
|
+
SET currency = pogs.currency + ar.total_refund
|
|
36
|
+
FROM aggregated_refunds ar
|
|
37
|
+
WHERE pogs."playerId" = ar."playerId"
|
|
38
|
+
AND pogs."gameServerId" = ar."gameServerId"
|
|
39
|
+
AND pogs.domain = ar.domain;
|
|
40
|
+
|
|
41
|
+
-- Update order status to CANCELED
|
|
42
|
+
UPDATE "shopOrder"
|
|
43
|
+
SET status = 'CANCELED'
|
|
44
|
+
WHERE "listingId" = NEW.id
|
|
45
|
+
AND status = 'PAID';
|
|
46
|
+
END IF;
|
|
47
|
+
|
|
48
|
+
RETURN NEW;
|
|
49
|
+
END;
|
|
50
|
+
$$ LANGUAGE plpgsql;
|
|
51
|
+
`);
|
|
52
|
+
|
|
53
|
+
// Step 2: Create trigger on shopListing table
|
|
54
|
+
await knex.raw(`
|
|
55
|
+
CREATE TRIGGER auto_cancel_orders_on_listing_delete
|
|
56
|
+
AFTER UPDATE ON "shopListing"
|
|
57
|
+
FOR EACH ROW
|
|
58
|
+
WHEN (OLD."deletedAt" IS NULL AND NEW."deletedAt" IS NOT NULL)
|
|
59
|
+
EXECUTE FUNCTION cancel_orders_on_listing_delete();
|
|
60
|
+
`);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function down(knex: Knex): Promise<void> {
|
|
64
|
+
// Drop trigger
|
|
65
|
+
await knex.raw(`
|
|
66
|
+
DROP TRIGGER IF EXISTS auto_cancel_orders_on_listing_delete ON "shopListing";
|
|
67
|
+
`);
|
|
68
|
+
|
|
69
|
+
// Drop function
|
|
70
|
+
await knex.raw(`
|
|
71
|
+
DROP FUNCTION IF EXISTS cancel_orders_on_listing_delete();
|
|
72
|
+
`);
|
|
73
|
+
|
|
74
|
+
// Note: We don't reverse the data cleanup since those were genuinely bad orders
|
|
75
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { Knex } from 'knex';
|
|
2
|
+
|
|
3
|
+
export async function up(knex: Knex): Promise<void> {
|
|
4
|
+
// PostgreSQL requires a multi-step process to update enum constraints
|
|
5
|
+
// 1. Add a temporary column with the new enum values
|
|
6
|
+
// 2. Copy data from old column to new column
|
|
7
|
+
// 3. Drop old column
|
|
8
|
+
// 4. Rename new column to old column name
|
|
9
|
+
|
|
10
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
11
|
+
table.enum('state_new', ['ACTIVE', 'DISABLED', 'MAINTENANCE', 'DELETED']).notNullable().defaultTo('ACTIVE');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
await knex.raw(`UPDATE domains SET state_new = state::text::varchar`);
|
|
15
|
+
|
|
16
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
17
|
+
table.dropColumn('state');
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
21
|
+
table.renameColumn('state_new', 'state');
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export async function down(knex: Knex): Promise<void> {
|
|
26
|
+
// Revert to the original enum values (removing DELETED)
|
|
27
|
+
// First, ensure no domains are in DELETED state (or this will fail)
|
|
28
|
+
await knex.raw(`DELETE FROM domains WHERE state = 'DELETED'`);
|
|
29
|
+
|
|
30
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
31
|
+
table.enum('state_new', ['ACTIVE', 'DISABLED', 'MAINTENANCE']).notNullable().defaultTo('ACTIVE');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
await knex.raw(`UPDATE domains SET state_new = state::text::varchar`);
|
|
35
|
+
|
|
36
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
37
|
+
table.dropColumn('state');
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
await knex.schema.alterTable('domains', (table) => {
|
|
41
|
+
table.renameColumn('state_new', 'state');
|
|
42
|
+
});
|
|
43
|
+
}
|