@takaro/db 0.0.28 → 0.0.30

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.
@@ -0,0 +1,4 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
4
+ //# sourceMappingURL=20250524054924-tracking.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250524054924-tracking.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20250524054924-tracking.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuElD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAGpD"}
@@ -0,0 +1,75 @@
1
+ export async function up(knex) {
2
+ // Create partitioned table using raw SQL since Knex doesn't have native partition support
3
+ await knex.raw(`
4
+ CREATE TABLE "playerLocation" (
5
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
6
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
7
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
8
+ domain VARCHAR(255) NOT NULL,
9
+ "playerId" UUID NOT NULL,
10
+ x NUMERIC(10,3) NOT NULL,
11
+ y NUMERIC(10,3) NOT NULL,
12
+ z NUMERIC(10,3) NOT NULL,
13
+ CONSTRAINT "playerLocation_pkey" PRIMARY KEY ("createdAt", "playerId"),
14
+ CONSTRAINT "playerLocation_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
15
+ CONSTRAINT "playerLocation_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE
16
+ ) PARTITION BY RANGE ("createdAt")
17
+ `);
18
+ // Create function to ensure partition exists for given date
19
+ await knex.raw(`
20
+ CREATE OR REPLACE FUNCTION ensure_player_location_partition(date_param VARCHAR DEFAULT NULL)
21
+ RETURNS VOID AS $$
22
+ DECLARE
23
+ current_day_start DATE;
24
+ next_day_start DATE;
25
+ partition_name TEXT;
26
+ partition_exists BOOLEAN;
27
+ target_date DATE;
28
+ BEGIN
29
+ -- Parse date parameter or use current date
30
+ IF date_param IS NOT NULL THEN
31
+ target_date := DATE(date_param::TIMESTAMP);
32
+ ELSE
33
+ target_date := CURRENT_DATE;
34
+ END IF;
35
+
36
+ -- Calculate day boundaries for target date
37
+ current_day_start := DATE_TRUNC('day', target_date);
38
+ next_day_start := current_day_start + INTERVAL '1 day';
39
+
40
+ -- Generate partition name (format: playerLocation_YYYY_MM_DD)
41
+ partition_name := 'playerLocation_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
42
+
43
+ -- Check if partition exists
44
+ SELECT EXISTS (
45
+ SELECT 1 FROM pg_class c
46
+ JOIN pg_namespace n ON n.oid = c.relnamespace
47
+ WHERE c.relname = partition_name
48
+ AND n.nspname = 'public'
49
+ ) INTO partition_exists;
50
+
51
+ -- Create partition if it doesn't exist
52
+ IF NOT partition_exists THEN
53
+ EXECUTE format(
54
+ 'CREATE TABLE %I PARTITION OF "playerLocation"
55
+ FOR VALUES FROM (%L) TO (%L)',
56
+ partition_name,
57
+ current_day_start,
58
+ next_day_start
59
+ );
60
+
61
+ RAISE NOTICE 'Created partition: % for date: %',
62
+ partition_name, current_day_start;
63
+ END IF;
64
+ END;
65
+ $$ LANGUAGE plpgsql;
66
+ `);
67
+ // Create indexes
68
+ await knex.raw(`CREATE INDEX "playerLocation_domain_createdAt_idx" ON "playerLocation" (domain, "createdAt")`);
69
+ await knex.raw(`CREATE INDEX "playerLocation_playerId_idx" ON "playerLocation" ("playerId")`);
70
+ }
71
+ export async function down(knex) {
72
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_location_partition(VARCHAR)');
73
+ await knex.raw('DROP TABLE IF EXISTS "playerLocation" CASCADE');
74
+ }
75
+ //# sourceMappingURL=20250524054924-tracking.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250524054924-tracking.js","sourceRoot":"","sources":["../../../src/migrations/sql/20250524054924-tracking.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,0FAA0F;IAC1F,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;GAcd,CAAC,CAAC;IAEH,4DAA4D;IAC5D,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+Cd,CAAC,CAAC;IAEH,iBAAiB;IACjB,MAAM,IAAI,CAAC,GAAG,CAAC,8FAA8F,CAAC,CAAC;IAC/G,MAAM,IAAI,CAAC,GAAG,CAAC,6EAA6E,CAAC,CAAC;AAChG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,MAAM,IAAI,CAAC,GAAG,CAAC,mEAAmE,CAAC,CAAC;IACpF,MAAM,IAAI,CAAC,GAAG,CAAC,+CAA+C,CAAC,CAAC;AAClE,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
4
+ //# sourceMappingURL=20250525135204-ban-reason-length.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250525135204-ban-reason-length.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20250525135204-ban-reason-length.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAuBlD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAOpD"}
@@ -0,0 +1,30 @@
1
+ export async function up(knex) {
2
+ await knex.schema.alterTable('bans', (table) => {
3
+ table.text('reason').alter();
4
+ });
5
+ await knex.raw(`
6
+ CREATE OR REPLACE FUNCTION truncate_reason()
7
+ RETURNS TRIGGER AS $$
8
+ BEGIN
9
+ IF char_length(NEW.reason) > 10000 THEN
10
+ NEW.reason = left(NEW.reason, 10000);
11
+ END IF;
12
+ RETURN NEW;
13
+ END;
14
+ $$ LANGUAGE plpgsql;
15
+ `);
16
+ await knex.raw(`
17
+ CREATE TRIGGER truncate_reason_trigger
18
+ BEFORE INSERT OR UPDATE ON "bans"
19
+ FOR EACH ROW
20
+ EXECUTE FUNCTION truncate_reason();
21
+ `);
22
+ }
23
+ export async function down(knex) {
24
+ await knex.raw('DROP TRIGGER IF EXISTS truncate_reason_trigger ON "bans"');
25
+ await knex.raw('DROP FUNCTION IF EXISTS truncate_reason()');
26
+ await knex.schema.alterTable('bans', (table) => {
27
+ table.string('reason', 255).alter();
28
+ });
29
+ }
30
+ //# sourceMappingURL=20250525135204-ban-reason-length.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250525135204-ban-reason-length.js","sourceRoot":"","sources":["../../../src/migrations/sql/20250525135204-ban-reason-length.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QAC7C,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,KAAK,EAAE,CAAC;IAC/B,CAAC,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;;;;;;GAUd,CAAC,CAAC;IAEH,MAAM,IAAI,CAAC,GAAG,CAAC;;;;;GAKd,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,MAAM,IAAI,CAAC,GAAG,CAAC,0DAA0D,CAAC,CAAC;IAC3E,MAAM,IAAI,CAAC,GAAG,CAAC,2CAA2C,CAAC,CAAC;IAE5D,MAAM,IAAI,CAAC,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE;QAC7C,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,CAAC;IACtC,CAAC,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,4 @@
1
+ import { Knex } from 'knex';
2
+ export declare function up(knex: Knex): Promise<void>;
3
+ export declare function down(knex: Knex): Promise<void>;
4
+ //# sourceMappingURL=20250605201140-shop-permissions-missing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250605201140-shop-permissions-missing.d.ts","sourceRoot":"","sources":["../../../src/migrations/sql/20250605201140-shop-permissions-missing.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,wBAAsB,EAAE,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAalD;AAED,wBAAsB,IAAI,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAEpD"}
@@ -0,0 +1,18 @@
1
+ export async function up(knex) {
2
+ await knex('permission').insert([
3
+ {
4
+ permission: 'MANAGE_SHOP_LISTINGS',
5
+ description: 'Manage shop listings',
6
+ friendlyName: 'Manage Shop Listings',
7
+ },
8
+ {
9
+ permission: 'MANAGE_SHOP_ORDERS',
10
+ description: 'Manage shop orders, allowing administrative actions on orders not belonging to the user',
11
+ friendlyName: 'Manage Shop Orders',
12
+ },
13
+ ]);
14
+ }
15
+ export async function down(knex) {
16
+ await knex('permission').whereIn('permission', ['MANAGE_SHOP_LISTINGS', 'MANAGE_SHOP_ORDERS']).del();
17
+ }
18
+ //# sourceMappingURL=20250605201140-shop-permissions-missing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"20250605201140-shop-permissions-missing.js","sourceRoot":"","sources":["../../../src/migrations/sql/20250605201140-shop-permissions-missing.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,KAAK,UAAU,EAAE,CAAC,IAAU;IACjC,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,MAAM,CAAC;QAC9B;YACE,UAAU,EAAE,sBAAsB;YAClC,WAAW,EAAE,sBAAsB;YACnC,YAAY,EAAE,sBAAsB;SACrC;QACD;YACE,UAAU,EAAE,oBAAoB;YAChC,WAAW,EAAE,yFAAyF;YACtG,YAAY,EAAE,oBAAoB;SACnC;KACF,CAAC,CAAC;AACL,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,IAAI,CAAC,IAAU;IACnC,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,sBAAsB,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAAG,EAAE,CAAC;AACvG,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@takaro/db",
3
- "version": "0.0.28",
3
+ "version": "0.0.30",
4
4
  "description": "An opinionated data layer",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",
@@ -0,0 +1,79 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Create partitioned table using raw SQL since Knex doesn't have native partition support
5
+ await knex.raw(`
6
+ CREATE TABLE "playerLocation" (
7
+ id UUID DEFAULT gen_random_uuid() NOT NULL,
8
+ "createdAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
9
+ "updatedAt" TIMESTAMP WITH TIME ZONE DEFAULT CURRENT_TIMESTAMP NOT NULL,
10
+ domain VARCHAR(255) NOT NULL,
11
+ "playerId" UUID NOT NULL,
12
+ x NUMERIC(10,3) NOT NULL,
13
+ y NUMERIC(10,3) NOT NULL,
14
+ z NUMERIC(10,3) NOT NULL,
15
+ CONSTRAINT "playerLocation_pkey" PRIMARY KEY ("createdAt", "playerId"),
16
+ CONSTRAINT "playerLocation_domain_foreign" FOREIGN KEY (domain) REFERENCES domains(id) ON DELETE CASCADE,
17
+ CONSTRAINT "playerLocation_playerId_foreign" FOREIGN KEY ("playerId") REFERENCES "playerOnGameServer"(id) ON DELETE CASCADE
18
+ ) PARTITION BY RANGE ("createdAt")
19
+ `);
20
+
21
+ // Create function to ensure partition exists for given date
22
+ await knex.raw(`
23
+ CREATE OR REPLACE FUNCTION ensure_player_location_partition(date_param VARCHAR DEFAULT NULL)
24
+ RETURNS VOID AS $$
25
+ DECLARE
26
+ current_day_start DATE;
27
+ next_day_start DATE;
28
+ partition_name TEXT;
29
+ partition_exists BOOLEAN;
30
+ target_date DATE;
31
+ BEGIN
32
+ -- Parse date parameter or use current date
33
+ IF date_param IS NOT NULL THEN
34
+ target_date := DATE(date_param::TIMESTAMP);
35
+ ELSE
36
+ target_date := CURRENT_DATE;
37
+ END IF;
38
+
39
+ -- Calculate day boundaries for target date
40
+ current_day_start := DATE_TRUNC('day', target_date);
41
+ next_day_start := current_day_start + INTERVAL '1 day';
42
+
43
+ -- Generate partition name (format: playerLocation_YYYY_MM_DD)
44
+ partition_name := 'playerLocation_' || TO_CHAR(current_day_start, 'YYYY_MM_DD');
45
+
46
+ -- Check if partition exists
47
+ SELECT EXISTS (
48
+ SELECT 1 FROM pg_class c
49
+ JOIN pg_namespace n ON n.oid = c.relnamespace
50
+ WHERE c.relname = partition_name
51
+ AND n.nspname = 'public'
52
+ ) INTO partition_exists;
53
+
54
+ -- Create partition if it doesn't exist
55
+ IF NOT partition_exists THEN
56
+ EXECUTE format(
57
+ 'CREATE TABLE %I PARTITION OF "playerLocation"
58
+ FOR VALUES FROM (%L) TO (%L)',
59
+ partition_name,
60
+ current_day_start,
61
+ next_day_start
62
+ );
63
+
64
+ RAISE NOTICE 'Created partition: % for date: %',
65
+ partition_name, current_day_start;
66
+ END IF;
67
+ END;
68
+ $$ LANGUAGE plpgsql;
69
+ `);
70
+
71
+ // Create indexes
72
+ await knex.raw(`CREATE INDEX "playerLocation_domain_createdAt_idx" ON "playerLocation" (domain, "createdAt")`);
73
+ await knex.raw(`CREATE INDEX "playerLocation_playerId_idx" ON "playerLocation" ("playerId")`);
74
+ }
75
+
76
+ export async function down(knex: Knex): Promise<void> {
77
+ await knex.raw('DROP FUNCTION IF EXISTS ensure_player_location_partition(VARCHAR)');
78
+ await knex.raw('DROP TABLE IF EXISTS "playerLocation" CASCADE');
79
+ }
@@ -0,0 +1,35 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('bans', (table) => {
5
+ table.text('reason').alter();
6
+ });
7
+
8
+ await knex.raw(`
9
+ CREATE OR REPLACE FUNCTION truncate_reason()
10
+ RETURNS TRIGGER AS $$
11
+ BEGIN
12
+ IF char_length(NEW.reason) > 10000 THEN
13
+ NEW.reason = left(NEW.reason, 10000);
14
+ END IF;
15
+ RETURN NEW;
16
+ END;
17
+ $$ LANGUAGE plpgsql;
18
+ `);
19
+
20
+ await knex.raw(`
21
+ CREATE TRIGGER truncate_reason_trigger
22
+ BEFORE INSERT OR UPDATE ON "bans"
23
+ FOR EACH ROW
24
+ EXECUTE FUNCTION truncate_reason();
25
+ `);
26
+ }
27
+
28
+ export async function down(knex: Knex): Promise<void> {
29
+ await knex.raw('DROP TRIGGER IF EXISTS truncate_reason_trigger ON "bans"');
30
+ await knex.raw('DROP FUNCTION IF EXISTS truncate_reason()');
31
+
32
+ await knex.schema.alterTable('bans', (table) => {
33
+ table.string('reason', 255).alter();
34
+ });
35
+ }
@@ -0,0 +1,20 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex('permission').insert([
5
+ {
6
+ permission: 'MANAGE_SHOP_LISTINGS',
7
+ description: 'Manage shop listings',
8
+ friendlyName: 'Manage Shop Listings',
9
+ },
10
+ {
11
+ permission: 'MANAGE_SHOP_ORDERS',
12
+ description: 'Manage shop orders, allowing administrative actions on orders not belonging to the user',
13
+ friendlyName: 'Manage Shop Orders',
14
+ },
15
+ ]);
16
+ }
17
+
18
+ export async function down(knex: Knex): Promise<void> {
19
+ await knex('permission').whereIn('permission', ['MANAGE_SHOP_LISTINGS', 'MANAGE_SHOP_ORDERS']).del();
20
+ }