@takaro/db 0.0.1 → 0.0.4

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.
Files changed (73) hide show
  1. package/dist/config.d.ts +14 -0
  2. package/dist/config.js +12 -0
  3. package/dist/config.js.map +1 -1
  4. package/dist/knex.d.ts +3 -0
  5. package/dist/knex.js +1 -0
  6. package/dist/knex.js.map +1 -1
  7. package/dist/migrations/sql/20240613175009-lastseen-users-pog.d.ts +3 -0
  8. package/dist/migrations/sql/20240613175009-lastseen-users-pog.js +17 -0
  9. package/dist/migrations/sql/20240613175009-lastseen-users-pog.js.map +1 -0
  10. package/dist/migrations/sql/20240614143517-shop.d.ts +3 -0
  11. package/dist/migrations/sql/20240614143517-shop.js +29 -0
  12. package/dist/migrations/sql/20240614143517-shop.js.map +1 -0
  13. package/dist/migrations/sql/20240615145045-user-player-link.d.ts +3 -0
  14. package/dist/migrations/sql/20240615145045-user-player-link.js +17 -0
  15. package/dist/migrations/sql/20240615145045-user-player-link.js.map +1 -0
  16. package/dist/migrations/sql/20240622132552-fix-ip-history-fk.d.ts +3 -0
  17. package/dist/migrations/sql/20240622132552-fix-ip-history-fk.js +11 -0
  18. package/dist/migrations/sql/20240622132552-fix-ip-history-fk.js.map +1 -0
  19. package/dist/migrations/sql/20240626200803-player-inventory-idx.d.ts +3 -0
  20. package/dist/migrations/sql/20240626200803-player-inventory-idx.js +11 -0
  21. package/dist/migrations/sql/20240626200803-player-inventory-idx.js.map +1 -0
  22. package/dist/migrations/sql/20240628174914-shop-orders.d.ts +3 -0
  23. package/dist/migrations/sql/20240628174914-shop-orders.js +16 -0
  24. package/dist/migrations/sql/20240628174914-shop-orders.js.map +1 -0
  25. package/dist/migrations/sql/20240705140358-teleports-public-refactor.d.ts +3 -0
  26. package/dist/migrations/sql/20240705140358-teleports-public-refactor.js +26 -0
  27. package/dist/migrations/sql/20240705140358-teleports-public-refactor.js.map +1 -0
  28. package/dist/migrations/sql/20240710181429-events-name-idx.d.ts +3 -0
  29. package/dist/migrations/sql/20240710181429-events-name-idx.js +11 -0
  30. package/dist/migrations/sql/20240710181429-events-name-idx.js.map +1 -0
  31. package/dist/migrations/sql/20240711181423-shop-orders-multi-listing.d.ts +3 -0
  32. package/dist/migrations/sql/20240711181423-shop-orders-multi-listing.js +26 -0
  33. package/dist/migrations/sql/20240711181423-shop-orders-multi-listing.js.map +1 -0
  34. package/dist/migrations/sql/20240718150807-listing-deleted-and-draft-status.d.ts +3 -0
  35. package/dist/migrations/sql/20240718150807-listing-deleted-and-draft-status.js +13 -0
  36. package/dist/migrations/sql/20240718150807-listing-deleted-and-draft-status.js.map +1 -0
  37. package/dist/migrations/sql/20240720135010-acting-user-and-module.d.ts +3 -0
  38. package/dist/migrations/sql/20240720135010-acting-user-and-module.js +19 -0
  39. package/dist/migrations/sql/20240720135010-acting-user-and-module.js.map +1 -0
  40. package/dist/migrations/sql/20240811061243-domain-rate-limit-setting.d.ts +3 -0
  41. package/dist/migrations/sql/20240811061243-domain-rate-limit-setting.js +14 -0
  42. package/dist/migrations/sql/20240811061243-domain-rate-limit-setting.js.map +1 -0
  43. package/dist/queryBuilder.d.ts +8 -2
  44. package/dist/queryBuilder.js +38 -19
  45. package/dist/queryBuilder.js.map +1 -1
  46. package/package.json +2 -3
  47. package/src/__tests__/queryBuilder.integration.test.ts +7 -4
  48. package/src/config.ts +14 -0
  49. package/src/knex.ts +1 -0
  50. package/src/migrations/sql/20221019173729_settings.ts +2 -2
  51. package/src/migrations/sql/20221102190532_commands.ts +2 -2
  52. package/src/migrations/sql/20230308183400-persistent-variables.ts +2 -2
  53. package/src/migrations/sql/20230604130951-fix-hook-eventtypes.ts +1 -1
  54. package/src/migrations/sql/20230622192402-discord-hooks.ts +2 -2
  55. package/src/migrations/sql/20230712061220-roles-permissions-extension.ts +1 -1
  56. package/src/migrations/sql/20230921123717-count-for-permissions.ts +1 -1
  57. package/src/migrations/sql/20240105130846-remove-some-module-perms.ts +4 -4
  58. package/src/migrations/sql/20240119152426-remove-hook-check.ts +1 -1
  59. package/src/migrations/sql/20240121142329-move-ip-history.ts +1 -1
  60. package/src/migrations/sql/20240613175009-lastseen-users-pog.ts +21 -0
  61. package/src/migrations/sql/20240614143517-shop.ts +39 -0
  62. package/src/migrations/sql/20240615145045-user-player-link.ts +20 -0
  63. package/src/migrations/sql/20240622132552-fix-ip-history-fk.ts +13 -0
  64. package/src/migrations/sql/20240626200803-player-inventory-idx.ts +13 -0
  65. package/src/migrations/sql/20240628174914-shop-orders.ts +20 -0
  66. package/src/migrations/sql/20240705140358-teleports-public-refactor.ts +31 -0
  67. package/src/migrations/sql/20240710181429-events-name-idx.ts +13 -0
  68. package/src/migrations/sql/20240711181423-shop-orders-multi-listing.ts +34 -0
  69. package/src/migrations/sql/20240718150807-listing-deleted-and-draft-status.ts +15 -0
  70. package/src/migrations/sql/20240720135010-acting-user-and-module.ts +24 -0
  71. package/src/migrations/sql/20240811061243-domain-rate-limit-setting.ts +16 -0
  72. package/src/migrations/util/alterEnum.ts +1 -1
  73. package/src/queryBuilder.ts +60 -20
@@ -183,18 +183,21 @@ describe('QueryBuilder', () => {
183
183
  const start = new Date();
184
184
  await TestUserModel.query().insert({ name: 'test1' });
185
185
  await TestUserModel.query().insert({ name: 'test2' });
186
-
187
186
  const end = new Date();
188
187
 
189
188
  // Quick hack to make the test pass
190
189
  // Relying on times like this with an external DB is troublesome...
191
190
  await sleep(250);
192
191
 
193
- await TestUserModel.query().insert({ name: 'test3', createdAt: end.toISOString() });
192
+ await TestUserModel.query().insert({ name: 'test3' });
194
193
 
195
194
  const res = await new QueryBuilder<TestUserModel, TestUserModel>({
196
- startDate: start.toISOString(),
197
- endDate: end.toISOString(),
195
+ greaterThan: {
196
+ createdAt: start.toISOString(),
197
+ },
198
+ lessThan: {
199
+ createdAt: end.toISOString(),
200
+ },
198
201
  }).build(TestUserModel.query());
199
202
 
200
203
  expect(res.results).to.have.lengthOf(2);
package/src/config.ts CHANGED
@@ -7,6 +7,8 @@ export interface IDbConfig extends IBaseConfig {
7
7
  user: string;
8
8
  password: string;
9
9
  database: string;
10
+ ssl: boolean;
11
+ ca: string;
10
12
  };
11
13
  redis: {
12
14
  host: string;
@@ -51,6 +53,18 @@ export const configSchema = {
51
53
  default: 'postgres',
52
54
  env: 'POSTGRES_DB',
53
55
  },
56
+ ssl: {
57
+ doc: 'Whether to use SSL for the connection',
58
+ format: Boolean,
59
+ default: false,
60
+ env: 'POSTGRES_SSL',
61
+ },
62
+ ca: {
63
+ doc: 'The CA certificate to use for SSL connections',
64
+ format: String,
65
+ default: '',
66
+ env: 'POSTGRES_CA',
67
+ },
54
68
  },
55
69
  redis: {
56
70
  host: {
package/src/knex.ts CHANGED
@@ -18,6 +18,7 @@ export function getKnexOptions(extra: Record<string, unknown> = {}) {
18
18
  user: config.get('postgres.user'),
19
19
  password: config.get('postgres.password'),
20
20
  database: config.get('postgres.database'),
21
+ ssl: config.get('postgres.ssl') ? { ca: config.get('postgres.ca') } : false,
21
22
  },
22
23
  ...extra,
23
24
  };
@@ -38,7 +38,7 @@ export async function up(knex: Knex): Promise<void> {
38
38
  'MANAGE_PLAYERS',
39
39
  'MANAGE_SETTINGS',
40
40
  'READ_SETTINGS',
41
- ])
41
+ ]),
42
42
  );
43
43
  }
44
44
 
@@ -65,6 +65,6 @@ export async function down(knex: Knex): Promise<void> {
65
65
  'MANAGE_MODULES',
66
66
  'READ_PLAYERS',
67
67
  'MANAGE_PLAYERS',
68
- ])
68
+ ]),
69
69
  );
70
70
  }
@@ -25,7 +25,7 @@ export async function up(knex: Knex): Promise<void> {
25
25
  'READ_SETTINGS',
26
26
  'READ_COMMANDS',
27
27
  'MANAGE_COMMANDS',
28
- ])
28
+ ]),
29
29
  );
30
30
 
31
31
  await knex.schema.alterTable('commands', (table) => {
@@ -61,6 +61,6 @@ export async function down(knex: Knex): Promise<void> {
61
61
  'MANAGE_PLAYERS',
62
62
  'MANAGE_SETTINGS',
63
63
  'READ_SETTINGS',
64
- ])
64
+ ]),
65
65
  );
66
66
  }
@@ -27,7 +27,7 @@ export async function up(knex: Knex): Promise<void> {
27
27
  'MANAGE_COMMANDS',
28
28
  'READ_VARIABLES',
29
29
  'MANAGE_VARIABLES',
30
- ])
30
+ ]),
31
31
  );
32
32
 
33
33
  await knex.schema.createTable('variables', (table) => {
@@ -69,6 +69,6 @@ export async function down(knex: Knex): Promise<void> {
69
69
  'READ_SETTINGS',
70
70
  'READ_COMMANDS',
71
71
  'MANAGE_COMMANDS',
72
- ])
72
+ ]),
73
73
  );
74
74
  }
@@ -3,7 +3,7 @@ import { formatAlterTableEnumSql } from '../util/alterEnum.js';
3
3
 
4
4
  export async function up(knex: Knex): Promise<void> {
5
5
  await knex.raw(
6
- formatAlterTableEnumSql('hooks', 'eventType', ['log', 'player-connected', 'player-disconnected', 'chat-message'])
6
+ formatAlterTableEnumSql('hooks', 'eventType', ['log', 'player-connected', 'player-disconnected', 'chat-message']),
7
7
  );
8
8
  }
9
9
 
@@ -9,12 +9,12 @@ export async function up(knex: Knex): Promise<void> {
9
9
  'player-disconnected',
10
10
  'chat-message',
11
11
  'discord-message',
12
- ])
12
+ ]),
13
13
  );
14
14
  }
15
15
 
16
16
  export async function down(knex: Knex): Promise<void> {
17
17
  await knex.raw(
18
- formatAlterTableEnumSql('hooks', 'eventType', ['log', 'player-connected', 'player-disconnected', 'chat-message'])
18
+ formatAlterTableEnumSql('hooks', 'eventType', ['log', 'player-connected', 'player-disconnected', 'chat-message']),
19
19
  );
20
20
  }
@@ -42,7 +42,7 @@ export async function down(knex: Knex): Promise<void> {
42
42
  'MANAGE_PLAYERS',
43
43
  'MANAGE_SETTINGS',
44
44
  'READ_SETTINGS',
45
- ])
45
+ ]),
46
46
  );
47
47
 
48
48
  await knex.schema.dropTable('roleOnPlayer');
@@ -36,7 +36,7 @@ export async function up(knex: Knex): Promise<void> {
36
36
  perms.map((perm) => ({
37
37
  permission: perm,
38
38
  canHaveCount: false,
39
- }))
39
+ })),
40
40
  );
41
41
 
42
42
  await knex.schema.createTable('temp_permissiononrole', (table) => {
@@ -20,14 +20,14 @@ export async function up(knex: Knex): Promise<void> {
20
20
  const readPermissionOnRole = await knex('permissionOnRole')
21
21
  .whereIn(
22
22
  'permissionId',
23
- readPerms.map((p) => p.id)
23
+ readPerms.map((p) => p.id),
24
24
  )
25
25
  .select('id');
26
26
 
27
27
  const managePermissionOnRole = await knex('permissionOnRole')
28
28
  .whereIn(
29
29
  'permissionId',
30
- managePerms.map((p) => p.id)
30
+ managePerms.map((p) => p.id),
31
31
  )
32
32
  .select('id');
33
33
 
@@ -35,7 +35,7 @@ export async function up(knex: Knex): Promise<void> {
35
35
  await knex('permissionOnRole')
36
36
  .whereIn(
37
37
  'id',
38
- readPermissionOnRole.map((p) => p.id)
38
+ readPermissionOnRole.map((p) => p.id),
39
39
  )
40
40
  .update({
41
41
  permissionId: readModulePerm.id,
@@ -44,7 +44,7 @@ export async function up(knex: Knex): Promise<void> {
44
44
  await knex('permissionOnRole')
45
45
  .whereIn(
46
46
  'id',
47
- managePermissionOnRole.map((p) => p.id)
47
+ managePermissionOnRole.map((p) => p.id),
48
48
  )
49
49
  .update({
50
50
  permissionId: manageModulePerm.id,
@@ -13,6 +13,6 @@ export async function down(knex: Knex): Promise<void> {
13
13
  'player-disconnected',
14
14
  'chat-message',
15
15
  'discord-message',
16
- ])
16
+ ]),
17
17
  );
18
18
  }
@@ -27,7 +27,7 @@ export async function up(knex: Knex): Promise<void> {
27
27
  playerId: pog.playerId,
28
28
  gameServerId: pog.gameServerId,
29
29
  };
30
- })
30
+ }),
31
31
  );
32
32
 
33
33
  if (newRecords.length) {
@@ -0,0 +1,21 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('users', (table) => {
5
+ table.timestamp('lastSeen').defaultTo(knex.fn.now()).notNullable();
6
+ });
7
+
8
+ await knex.schema.alterTable('playerOnGameServer', (table) => {
9
+ table.timestamp('lastSeen').defaultTo(knex.fn.now()).notNullable();
10
+ });
11
+ }
12
+
13
+ export async function down(knex: Knex): Promise<void> {
14
+ await knex.schema.alterTable('users', (table) => {
15
+ table.dropColumn('lastSeen');
16
+ });
17
+
18
+ await knex.schema.alterTable('playerOnGameServer', (table) => {
19
+ table.dropColumn('lastSeen');
20
+ });
21
+ }
@@ -0,0 +1,39 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('shopListing', (table) => {
5
+ table.timestamps(true, true, true);
6
+ table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid ()'));
7
+ table.string('domain').references('domains.id').onDelete('CASCADE').notNullable();
8
+ table.index('domain');
9
+
10
+ table.uuid('gameServerId').references('gameservers.id').onDelete('CASCADE').notNullable();
11
+
12
+ table.uuid('itemId').references('items.id').onDelete('CASCADE');
13
+ table.uuid('functionId').references('functions.id').onDelete('CASCADE');
14
+
15
+ // Either an item or a function must be set
16
+ table.check('("itemId" IS NOT NULL) OR ("functionId" IS NOT NULL)');
17
+
18
+ table.integer('price').notNullable();
19
+
20
+ // Price must be positive and non-zero
21
+ table.check('"price" > 0');
22
+
23
+ table.string('name').nullable();
24
+ });
25
+
26
+ // Listings can only be bought by users with the role
27
+ await knex.schema.createTable('shopListingRole', (table) => {
28
+ table.timestamps(true, true, true);
29
+ table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid ()'));
30
+
31
+ table.uuid('listingId').references('shopListing.id').onDelete('CASCADE').notNullable();
32
+ table.uuid('roleId').references('roles.id').onDelete('CASCADE').notNullable();
33
+ });
34
+ }
35
+
36
+ export async function down(knex: Knex): Promise<void> {
37
+ await knex.schema.dropTable('shopListingRole');
38
+ await knex.schema.dropTable('shopListing');
39
+ }
@@ -0,0 +1,20 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Remove users_idp_id_unique constraint
5
+ //idpId must be unique per domain instead
6
+ await knex.schema.alterTable('users', (table) => {
7
+ table.dropUnique(['idpid']);
8
+ table.unique(['idpId', 'domain']);
9
+
10
+ table.uuid('playerId').references('players.id').onDelete('SET NULL').nullable();
11
+ });
12
+ }
13
+
14
+ export async function down(knex: Knex): Promise<void> {
15
+ await knex.schema.alterTable('users', (table) => {
16
+ table.dropUnique(['idpid', 'domain']);
17
+ table.unique(['idpId']);
18
+ table.dropColumn('playerId');
19
+ });
20
+ }
@@ -0,0 +1,13 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('playerIpHistory', (table) => {
5
+ table.uuid('gameServerId').nullable().alter();
6
+ });
7
+ }
8
+
9
+ export async function down(knex: Knex): Promise<void> {
10
+ await knex.schema.alterTable('playerIpHistory', (table) => {
11
+ table.uuid('gameServerId').notNullable().alter();
12
+ });
13
+ }
@@ -0,0 +1,13 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('playerInventory', (table) => {
5
+ table.index(['playerId', 'domain']);
6
+ });
7
+ }
8
+
9
+ export async function down(knex: Knex): Promise<void> {
10
+ await knex.schema.alterTable('playerInventory', (table) => {
11
+ table.dropIndex(['playerId', 'domain']);
12
+ });
13
+ }
@@ -0,0 +1,20 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('shopOrder', (table) => {
5
+ table.timestamps(true, true, true);
6
+ table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid ()'));
7
+ table.string('domain').references('domains.id').onDelete('CASCADE').notNullable();
8
+ table.index('domain');
9
+
10
+ table.uuid('listingId').references('shopListing.id').onDelete('CASCADE').notNullable();
11
+ table.uuid('userId').references('users.id').onDelete('CASCADE').notNullable();
12
+
13
+ table.integer('amount').defaultTo(1).notNullable();
14
+ table.enum('status', ['COMPLETED', 'PAID', 'CANCELED']).notNullable();
15
+ });
16
+ }
17
+
18
+ export async function down(knex: Knex): Promise<void> {
19
+ await knex.schema.dropTable('shopOrder');
20
+ }
@@ -0,0 +1,31 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ // Find all variables where key starts with 'tp_'
5
+ const variables = await knex('variables').select().where('key', 'like', 'tp_%');
6
+
7
+ // Reduce the list to only public teleports
8
+ const publicTeleports = variables.filter((variable) => {
9
+ try {
10
+ const value = JSON.parse(variable.value);
11
+ return value.public;
12
+ } catch (error) {
13
+ // eslint-disable-next-line no-console
14
+ console.warn(`Error parsing JSON for variable ${variable.key}: ${error}. Skipping.`);
15
+ return false;
16
+ }
17
+ });
18
+
19
+ // Change the key for all public teleports from tp_xxx to pubtp_xxx
20
+ await Promise.all(
21
+ publicTeleports.map((teleport) => {
22
+ return knex('variables')
23
+ .update({ key: `pub${teleport.key}` })
24
+ .where('id', teleport.id);
25
+ }),
26
+ );
27
+ }
28
+
29
+ export async function down(_knex: Knex): Promise<void> {
30
+ // ... no down here, this is a one-way migration
31
+ }
@@ -0,0 +1,13 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('events', (table) => {
5
+ table.index(['domain', 'eventName']);
6
+ });
7
+ }
8
+
9
+ export async function down(knex: Knex): Promise<void> {
10
+ await knex.schema.alterTable('events', (table) => {
11
+ table.dropIndex(['domain', 'eventName']);
12
+ });
13
+ }
@@ -0,0 +1,34 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.createTable('itemOnShopListing', (table) => {
5
+ table.timestamps(true, true, true);
6
+ table.uuid('id').primary().defaultTo(knex.raw('gen_random_uuid ()'));
7
+
8
+ table.uuid('listingId').references('shopListing.id').onDelete('CASCADE').notNullable();
9
+ table.uuid('itemId').references('items.id').onDelete('CASCADE');
10
+
11
+ table.integer('amount').defaultTo(1).notNullable();
12
+ // Amount must be positive and non-zero
13
+ table.check('"amount" > 0');
14
+
15
+ table.string('quality').nullable();
16
+ });
17
+
18
+ await knex.schema.alterTable('shopListing', (table) => {
19
+ table.dropChecks('"shopListing_check"');
20
+ table.dropColumn('functionId');
21
+ table.dropColumn('itemId');
22
+ });
23
+ }
24
+
25
+ export async function down(knex: Knex): Promise<void> {
26
+ await knex.schema.alterTable('shopListing', (table) => {
27
+ table.uuid('itemId').references('items.id').onDelete('CASCADE');
28
+ table.uuid('functionId').references('functions.id').onDelete('CASCADE');
29
+
30
+ table.check('("itemId" IS NOT NULL) OR ("functionId" IS NOT NULL)');
31
+ });
32
+
33
+ await knex.schema.dropTable('itemOnShopListing');
34
+ }
@@ -0,0 +1,15 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('shopListing', (table) => {
5
+ table.timestamp('deletedAt').nullable();
6
+ table.boolean('draft').defaultTo(false);
7
+ });
8
+ }
9
+
10
+ export async function down(knex: Knex): Promise<void> {
11
+ await knex.schema.alterTable('shopListing', (table) => {
12
+ table.dropColumn('deletedAt');
13
+ table.dropColumn('draft');
14
+ });
15
+ }
@@ -0,0 +1,24 @@
1
+ import { Knex } from 'knex';
2
+
3
+ export async function up(knex: Knex): Promise<void> {
4
+ await knex.schema.alterTable('events', (table) => {
5
+ table.uuid('actingUserId').nullable();
6
+ table.foreign('actingUserId').references('id').inTable('users');
7
+
8
+ table.uuid('actingModuleId').nullable();
9
+ table.foreign('actingModuleId').references('id').inTable('modules');
10
+
11
+ table.index('actingUserId');
12
+ table.index('actingModuleId');
13
+ });
14
+ }
15
+
16
+ export async function down(knex: Knex): Promise<void> {
17
+ await knex.schema.alterTable('events', (table) => {
18
+ table.dropForeign(['actingUserId']);
19
+ table.dropColumn('actingUserId');
20
+
21
+ table.dropForeign(['actingModuleId']);
22
+ table.dropColumn('actingModuleId');
23
+ });
24
+ }
@@ -0,0 +1,16 @@
1
+ import ms from 'ms';
2
+ import { Knex } from 'knex';
3
+
4
+ export async function up(knex: Knex): Promise<void> {
5
+ await knex.schema.alterTable('domains', (table) => {
6
+ table.integer('rateLimitPoints').notNullable().defaultTo(2500);
7
+ table.integer('rateLimitDuration').notNullable().defaultTo(ms('15min'));
8
+ });
9
+ }
10
+
11
+ export async function down(knex: Knex): Promise<void> {
12
+ await knex.schema.alterTable('domains', (table) => {
13
+ table.dropColumn('rateLimitPoints');
14
+ table.dropColumn('rateLimitDuration');
15
+ });
16
+ }
@@ -4,7 +4,7 @@ export function formatAlterTableEnumSql(tableName: string, columnName: string, e
4
4
  return [
5
5
  `ALTER TABLE "${tableName}" DROP CONSTRAINT IF EXISTS "${constraintName}";`,
6
6
  `ALTER TABLE "${tableName}" ADD CONSTRAINT "${constraintName}" CHECK ("${columnName}" = ANY (ARRAY['${enums.join(
7
- "'::text, '"
7
+ "'::text, '",
8
8
  )}'::text]));`,
9
9
  ].join('\n');
10
10
  }
@@ -1,5 +1,12 @@
1
- import { IsDateString, IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
2
- import { QueryBuilder as ObjectionQueryBuilder, Model as ObjectionModel, Page, AnyQueryBuilder } from 'objection';
1
+ import { IsEnum, IsNumber, IsOptional, IsString } from 'class-validator';
2
+ import {
3
+ QueryBuilder as ObjectionQueryBuilder,
4
+ Model as ObjectionModel,
5
+ Page,
6
+ AnyQueryBuilder,
7
+ Expression,
8
+ PrimitiveValue,
9
+ } from 'objection';
3
10
 
4
11
  export class ITakaroQuery<T> {
5
12
  @IsOptional()
@@ -12,6 +19,16 @@ export class ITakaroQuery<T> {
12
19
  [key in keyof T]?: unknown[] | unknown;
13
20
  };
14
21
 
22
+ @IsOptional()
23
+ greaterThan?: {
24
+ [key in keyof T]?: unknown;
25
+ };
26
+
27
+ @IsOptional()
28
+ lessThan?: {
29
+ [key in keyof T]?: unknown;
30
+ };
31
+
15
32
  @IsOptional()
16
33
  @IsNumber()
17
34
  page?: number;
@@ -29,14 +46,6 @@ export class ITakaroQuery<T> {
29
46
  @IsEnum(['asc', 'desc'])
30
47
  sortDirection?: SortDirection;
31
48
 
32
- @IsOptional()
33
- @IsDateString()
34
- startDate?: string;
35
-
36
- @IsOptional()
37
- @IsDateString()
38
- endDate?: string;
39
-
40
49
  @IsOptional()
41
50
  @IsString({ each: true })
42
51
  extend?: string[];
@@ -58,14 +67,9 @@ export class QueryBuilder<Model extends ObjectionModel, OutputDTO> {
58
67
 
59
68
  let qry = query.page(pagination.page, pagination.limit).orderBy(sorting.sortBy, sorting.sortDirection);
60
69
 
61
- if (this.query.startDate) {
62
- qry = qry.where(`${tableName}.createdAt`, '>=', this.query.startDate);
63
- }
64
- if (this.query.endDate) {
65
- qry = qry.where(`${tableName}.createdAt`, '<=', this.query.endDate);
66
- }
67
-
68
70
  qry = this.filters(tableName, qry);
71
+ qry = this.greaterThan(tableName, qry);
72
+ qry = this.lessThan(tableName, qry);
69
73
 
70
74
  if (this.query.search) {
71
75
  qry.where((builder) => {
@@ -93,15 +97,19 @@ export class QueryBuilder<Model extends ObjectionModel, OutputDTO> {
93
97
 
94
98
  private filters(
95
99
  tableName: string,
96
- query: ObjectionQueryBuilder<Model, Page<Model>>
100
+ query: ObjectionQueryBuilder<Model, Page<Model>>,
97
101
  ): ObjectionQueryBuilder<Model, Page<Model>> {
98
102
  for (const filter in this.query.filters) {
99
103
  if (Object.prototype.hasOwnProperty.call(this.query.filters, filter)) {
100
104
  const searchVal = this.query.filters[filter];
101
105
 
102
106
  if (searchVal && Array.isArray(searchVal)) {
103
- const filtered = searchVal.filter(Boolean);
104
- if (filtered.length) {
107
+ if (searchVal.includes(null) || searchVal.includes('null')) {
108
+ query.whereNull(`${tableName}.${filter}`);
109
+ continue;
110
+ }
111
+
112
+ if (searchVal.length) {
105
113
  query.whereIn(`${tableName}.${filter}`, searchVal.filter(Boolean) as unknown as AnyQueryBuilder);
106
114
  }
107
115
  }
@@ -111,6 +119,38 @@ export class QueryBuilder<Model extends ObjectionModel, OutputDTO> {
111
119
  return query;
112
120
  }
113
121
 
122
+ private greaterThan(
123
+ tableName: string,
124
+ query: ObjectionQueryBuilder<Model, Page<Model>>,
125
+ ): ObjectionQueryBuilder<Model, Page<Model>> {
126
+ for (const filter in this.query.greaterThan) {
127
+ if (Object.prototype.hasOwnProperty.call(this.query.greaterThan, filter)) {
128
+ const searchVal = this.query.greaterThan[filter];
129
+ if (searchVal) {
130
+ query.where(`${tableName}.${filter}`, '>=', searchVal as unknown as Expression<PrimitiveValue>);
131
+ }
132
+ }
133
+ }
134
+
135
+ return query;
136
+ }
137
+
138
+ private lessThan(
139
+ tableName: string,
140
+ query: ObjectionQueryBuilder<Model, Page<Model>>,
141
+ ): ObjectionQueryBuilder<Model, Page<Model>> {
142
+ for (const filter in this.query.lessThan) {
143
+ if (Object.prototype.hasOwnProperty.call(this.query.lessThan, filter)) {
144
+ const searchVal = this.query.lessThan[filter];
145
+ if (searchVal) {
146
+ query.where(`${tableName}.${filter}`, '<=', searchVal as unknown as Expression<PrimitiveValue>);
147
+ }
148
+ }
149
+ }
150
+
151
+ return query;
152
+ }
153
+
114
154
  private sorting(): { sortBy: string; sortDirection: SortDirection } {
115
155
  if (!this.query.sortBy) {
116
156
  return {