@moneypot/hub 1.19.10 → 1.19.12
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/cli/db-migrate.js +12 -0
- package/dist/src/config.d.ts +1 -1
- package/dist/src/config.js +11 -4
- package/dist/src/context.js +1 -1
- package/dist/src/db/transaction.d.ts +10 -3
- package/dist/src/db/transaction.js +62 -31
- package/dist/src/index.js +14 -5
- package/dist/src/migrations.d.ts +2 -1
- package/dist/src/migrations.js +22 -33
- package/dist/src/services/jwt-service.d.ts +2 -2
- package/dist/src/test/index.js +12 -5
- package/package.json +1 -1
package/dist/cli/db-migrate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { resolve } from "path";
|
|
3
|
+
import { Client } from "pg";
|
|
3
4
|
function printUsage() {
|
|
4
5
|
console.error("\nUsage:");
|
|
5
6
|
console.error(" db-migrate [userDatabaseMigrationsPath]");
|
|
@@ -22,13 +23,21 @@ async function main() {
|
|
|
22
23
|
}
|
|
23
24
|
await import("dotenv/config");
|
|
24
25
|
const { runMigrations } = await import("../src/index.js");
|
|
26
|
+
const connectionString = process.env.SUPERUSER_DATABASE_URL;
|
|
27
|
+
if (!connectionString) {
|
|
28
|
+
console.error("❌ SUPERUSER_DATABASE_URL environment variable is not set");
|
|
29
|
+
process.exit(1);
|
|
30
|
+
}
|
|
25
31
|
console.log("Running database migrations...");
|
|
26
32
|
console.log("Hub core migrations: ✓");
|
|
27
33
|
if (userDatabaseMigrationsPath) {
|
|
28
34
|
console.log(`User migrations: ${userDatabaseMigrationsPath}`);
|
|
29
35
|
}
|
|
36
|
+
const pgClient = new Client({ connectionString });
|
|
30
37
|
try {
|
|
38
|
+
await pgClient.connect();
|
|
31
39
|
await runMigrations({
|
|
40
|
+
pgClient,
|
|
32
41
|
userDatabaseMigrationsPath,
|
|
33
42
|
});
|
|
34
43
|
console.log("✅ Database migrations completed successfully");
|
|
@@ -39,5 +48,8 @@ async function main() {
|
|
|
39
48
|
console.error(error);
|
|
40
49
|
process.exit(1);
|
|
41
50
|
}
|
|
51
|
+
finally {
|
|
52
|
+
await pgClient.end();
|
|
53
|
+
}
|
|
42
54
|
}
|
|
43
55
|
main();
|
package/dist/src/config.d.ts
CHANGED
|
@@ -4,7 +4,7 @@ export declare const PORT: number;
|
|
|
4
4
|
export declare const LOG_LEVEL: string;
|
|
5
5
|
export declare const LOG_PRETTY: boolean | undefined;
|
|
6
6
|
export declare const MP_GRAPHQL_URL: string;
|
|
7
|
-
export declare const
|
|
7
|
+
export declare const POSTGRAPHILE_DATABASE_URL: string;
|
|
8
8
|
export declare const SUPERUSER_DATABASE_URL: string;
|
|
9
9
|
export declare const HASHCHAINSERVER_URL: string;
|
|
10
10
|
export declare const HASHCHAINSERVER_MAX_ITERATIONS: number;
|
package/dist/src/config.js
CHANGED
|
@@ -69,16 +69,23 @@ export const MP_GRAPHQL_URL = getEnvVariable("MP_GRAPHQL_URL", (value) => {
|
|
|
69
69
|
}
|
|
70
70
|
return value;
|
|
71
71
|
});
|
|
72
|
-
export const
|
|
72
|
+
export const POSTGRAPHILE_DATABASE_URL = getEnvVariable("POSTGRAPHILE_DATABASE_URL", (value) => {
|
|
73
73
|
if (!value) {
|
|
74
|
-
|
|
74
|
+
const legacyValue = process.env.DATABASE_URL;
|
|
75
|
+
if (legacyValue) {
|
|
76
|
+
console.warn("⚠️ DATABASE_URL is deprecated. Please rename it to POSTGRAPHILE_DATABASE_URL.");
|
|
77
|
+
value = legacyValue;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
throw new Error(`Missing POSTGRAPHILE_DATABASE_URL env var.`);
|
|
81
|
+
}
|
|
75
82
|
}
|
|
76
83
|
if (!URL.parse(value)) {
|
|
77
|
-
console.warn("
|
|
84
|
+
console.warn("POSTGRAPHILE_DATABASE_URL is not a valid URL.");
|
|
78
85
|
}
|
|
79
86
|
const databaseUrlUsername = pgConnectionString.parse(value).user;
|
|
80
87
|
if (databaseUrlUsername !== "app_postgraphile") {
|
|
81
|
-
console.warn(`
|
|
88
|
+
console.warn(`POSTGRAPHILE_DATABASE_URL username is ${databaseUrlUsername}, expected app_postgraphile`);
|
|
82
89
|
}
|
|
83
90
|
return value;
|
|
84
91
|
});
|
package/dist/src/context.js
CHANGED
|
@@ -3,7 +3,7 @@ import * as config from "./config.js";
|
|
|
3
3
|
import { logger } from "./logger.js";
|
|
4
4
|
import { DatabaseNotifier } from "./db/index.js";
|
|
5
5
|
export function createServerContext(overrides) {
|
|
6
|
-
const postgraphileDatabaseUrl = overrides?.postgraphileDatabaseUrl ?? config.
|
|
6
|
+
const postgraphileDatabaseUrl = overrides?.postgraphileDatabaseUrl ?? config.POSTGRAPHILE_DATABASE_URL;
|
|
7
7
|
const superuserDatabaseUrl = overrides?.superuserDatabaseUrl ?? config.SUPERUSER_DATABASE_URL;
|
|
8
8
|
const context = {
|
|
9
9
|
postgraphilePool: new pg.Pool({
|
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
import * as pg from "pg";
|
|
2
2
|
declare const PgClientInTransactionBrand: unique symbol;
|
|
3
|
-
export
|
|
3
|
+
export declare class RollbackFailedError extends Error {
|
|
4
|
+
readonly originalError: unknown;
|
|
5
|
+
readonly rollbackError: unknown;
|
|
6
|
+
constructor(originalError: unknown, rollbackError: unknown);
|
|
7
|
+
}
|
|
8
|
+
export type PgClientInTransaction = pg.ClientBase & {
|
|
4
9
|
readonly [PgClientInTransactionBrand]: true;
|
|
5
10
|
};
|
|
6
|
-
export declare function isInTransaction(pgClient: pg.
|
|
7
|
-
export declare function assertInTransaction(pgClient: pg.
|
|
11
|
+
export declare function isInTransaction(pgClient: pg.ClientBase): pgClient is PgClientInTransaction;
|
|
12
|
+
export declare function assertInTransaction(pgClient: pg.ClientBase): asserts pgClient is PgClientInTransaction;
|
|
8
13
|
export declare function getIsolationLevel(pgClient: PgClientInTransaction): IsolationLevel | null;
|
|
9
14
|
export declare const IsolationLevel: {
|
|
10
15
|
readonly READ_COMMITTED: "READ COMMITTED";
|
|
@@ -12,6 +17,8 @@ export declare const IsolationLevel: {
|
|
|
12
17
|
readonly SERIALIZABLE: "SERIALIZABLE";
|
|
13
18
|
};
|
|
14
19
|
export type IsolationLevel = (typeof IsolationLevel)[keyof typeof IsolationLevel];
|
|
20
|
+
export declare function withPgClientTransaction<T>(pgClient: pg.ClientBase, callback: (pgClient: PgClientInTransaction) => Promise<T>): Promise<T>;
|
|
21
|
+
export declare function withPgClientTransaction<T>(pgClient: pg.ClientBase, isolationLevel: IsolationLevel, callback: (pgClient: PgClientInTransaction) => Promise<T>): Promise<T>;
|
|
15
22
|
export declare function withPgPoolTransaction<T>(pool: pg.Pool, callback: (pgClient: PgClientInTransaction) => Promise<T>, retryCount?: number, maxRetries?: number): Promise<T>;
|
|
16
23
|
export declare function withPgPoolTransaction<T>(pool: pg.Pool, isolationLevel: IsolationLevel, callback: (pgClient: PgClientInTransaction) => Promise<T>, retryCount?: number, maxRetries?: number): Promise<T>;
|
|
17
24
|
export {};
|
|
@@ -2,6 +2,16 @@ import * as pg from "pg";
|
|
|
2
2
|
import { logger } from "../logger.js";
|
|
3
3
|
import { setTimeout } from "node:timers/promises";
|
|
4
4
|
const PgClientInTransactionBrand = Symbol("PgClientInTransaction");
|
|
5
|
+
export class RollbackFailedError extends Error {
|
|
6
|
+
originalError;
|
|
7
|
+
rollbackError;
|
|
8
|
+
constructor(originalError, rollbackError) {
|
|
9
|
+
super("Transaction rollback failed");
|
|
10
|
+
this.originalError = originalError;
|
|
11
|
+
this.rollbackError = rollbackError;
|
|
12
|
+
this.name = "RollbackFailedError";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
5
15
|
const PG_ERROR_CODE = {
|
|
6
16
|
deadlock: "40P01",
|
|
7
17
|
serializationFailure: "40001",
|
|
@@ -23,25 +33,20 @@ export const IsolationLevel = {
|
|
|
23
33
|
REPEATABLE_READ: "REPEATABLE READ",
|
|
24
34
|
SERIALIZABLE: "SERIALIZABLE",
|
|
25
35
|
};
|
|
26
|
-
export async function
|
|
36
|
+
export async function withPgClientTransaction(pgClient, callbackOrIsolationLevel, maybeCallback) {
|
|
27
37
|
let callback;
|
|
28
38
|
let isolationLevel = IsolationLevel.READ_COMMITTED;
|
|
29
|
-
let retryCount = 0;
|
|
30
39
|
if (typeof callbackOrIsolationLevel === "function") {
|
|
31
40
|
callback = callbackOrIsolationLevel;
|
|
32
|
-
if (typeof callbackOrRetryCount === "number") {
|
|
33
|
-
retryCount = callbackOrRetryCount;
|
|
34
|
-
maxRetries = retryCountOrMaxRetries;
|
|
35
|
-
}
|
|
36
41
|
}
|
|
37
42
|
else {
|
|
38
43
|
isolationLevel = callbackOrIsolationLevel;
|
|
39
|
-
|
|
40
|
-
|
|
44
|
+
if (typeof maybeCallback !== "function") {
|
|
45
|
+
throw new Error("withPgClientTransaction requires a callback function");
|
|
46
|
+
}
|
|
47
|
+
callback = maybeCallback;
|
|
41
48
|
}
|
|
42
|
-
let pgClient = null;
|
|
43
49
|
try {
|
|
44
|
-
pgClient = await pool.connect();
|
|
45
50
|
if (isolationLevel === IsolationLevel.READ_COMMITTED) {
|
|
46
51
|
await pgClient.query("BEGIN");
|
|
47
52
|
}
|
|
@@ -55,39 +60,65 @@ export async function withPgPoolTransaction(pool, callbackOrIsolationLevel, call
|
|
|
55
60
|
return result;
|
|
56
61
|
}
|
|
57
62
|
catch (error) {
|
|
58
|
-
if (pgClient) {
|
|
63
|
+
if (SIDECAR.has(pgClient)) {
|
|
59
64
|
try {
|
|
60
65
|
await pgClient.query("ROLLBACK");
|
|
61
66
|
}
|
|
62
67
|
catch (rollbackError) {
|
|
63
68
|
logger.error(error, "Original error");
|
|
64
69
|
logger.error(rollbackError, "Rollback failed");
|
|
65
|
-
|
|
66
|
-
pgClient.release(true);
|
|
67
|
-
pgClient = null;
|
|
68
|
-
throw error;
|
|
69
|
-
}
|
|
70
|
-
if (retryCount < maxRetries &&
|
|
71
|
-
error instanceof pg.DatabaseError &&
|
|
72
|
-
(error.code === PG_ERROR_CODE.deadlock ||
|
|
73
|
-
error.code === PG_ERROR_CODE.serializationFailure)) {
|
|
74
|
-
const backoffMs = Math.min(100 * Math.pow(2, retryCount), 2000);
|
|
75
|
-
logger.warn(`Retrying transaction in ${Math.floor(backoffMs)}ms (attempt ${retryCount + 2}/${maxRetries + 1}) due to pg error code ${error.code}: ${error.message}`);
|
|
76
|
-
await setTimeout(backoffMs);
|
|
77
|
-
if (isolationLevel === IsolationLevel.READ_COMMITTED) {
|
|
78
|
-
return withPgPoolTransaction(pool, callback, retryCount + 1, maxRetries);
|
|
79
|
-
}
|
|
80
|
-
else {
|
|
81
|
-
return withPgPoolTransaction(pool, isolationLevel, callback, retryCount + 1, maxRetries);
|
|
82
|
-
}
|
|
70
|
+
throw new RollbackFailedError(error, rollbackError);
|
|
83
71
|
}
|
|
84
72
|
}
|
|
85
73
|
throw error;
|
|
86
74
|
}
|
|
87
75
|
finally {
|
|
88
|
-
|
|
89
|
-
|
|
76
|
+
SIDECAR.delete(pgClient);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export async function withPgPoolTransaction(pool, callbackOrIsolationLevel, callbackOrRetryCount, retryCountOrMaxRetries = 0, maxRetries = 3) {
|
|
80
|
+
let callback;
|
|
81
|
+
let isolationLevel = IsolationLevel.READ_COMMITTED;
|
|
82
|
+
let retryCount = 0;
|
|
83
|
+
if (typeof callbackOrIsolationLevel === "function") {
|
|
84
|
+
callback = callbackOrIsolationLevel;
|
|
85
|
+
if (typeof callbackOrRetryCount === "number") {
|
|
86
|
+
retryCount = callbackOrRetryCount;
|
|
87
|
+
maxRetries = retryCountOrMaxRetries;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
isolationLevel = callbackOrIsolationLevel;
|
|
92
|
+
if (typeof callbackOrRetryCount !== "function") {
|
|
93
|
+
throw new Error("withPgPoolTransaction requires a callback function");
|
|
94
|
+
}
|
|
95
|
+
callback = callbackOrRetryCount;
|
|
96
|
+
retryCount = retryCountOrMaxRetries;
|
|
97
|
+
}
|
|
98
|
+
let pgClient = await pool.connect();
|
|
99
|
+
try {
|
|
100
|
+
return await withPgClientTransaction(pgClient, isolationLevel, callback);
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
if (error instanceof RollbackFailedError) {
|
|
104
|
+
pgClient.release(true);
|
|
105
|
+
pgClient = null;
|
|
106
|
+
throw error.originalError;
|
|
107
|
+
}
|
|
108
|
+
if (retryCount < maxRetries &&
|
|
109
|
+
error instanceof pg.DatabaseError &&
|
|
110
|
+
(error.code === PG_ERROR_CODE.deadlock ||
|
|
111
|
+
error.code === PG_ERROR_CODE.serializationFailure)) {
|
|
112
|
+
const backoffMs = Math.min(100 * Math.pow(2, retryCount), 2000);
|
|
113
|
+
logger.warn(`Retrying transaction in ${Math.floor(backoffMs)}ms (attempt ${retryCount + 2}/${maxRetries + 1}) due to pg error code ${error.code}: ${error.message}`);
|
|
90
114
|
pgClient.release();
|
|
115
|
+
pgClient = null;
|
|
116
|
+
await setTimeout(backoffMs);
|
|
117
|
+
return withPgPoolTransaction(pool, isolationLevel, callback, retryCount + 1, maxRetries);
|
|
91
118
|
}
|
|
119
|
+
throw error;
|
|
120
|
+
}
|
|
121
|
+
finally {
|
|
122
|
+
pgClient?.release();
|
|
92
123
|
}
|
|
93
124
|
}
|
package/dist/src/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import { NODE_ENV, PORT, SUPERUSER_DATABASE_URL,
|
|
1
|
+
import { NODE_ENV, PORT, SUPERUSER_DATABASE_URL, POSTGRAPHILE_DATABASE_URL, } from "./config.js";
|
|
2
2
|
import { logger } from "./logger.js";
|
|
3
|
+
import { getPgClient } from "./db/index.js";
|
|
3
4
|
import { createHubServer } from "./server/index.js";
|
|
4
5
|
export { HubGameConfigPlugin, } from "./plugins/hub-game-config-plugin.js";
|
|
5
6
|
export { validateRisk, } from "./risk-policy.js";
|
|
@@ -21,12 +22,20 @@ function validateOptions(options) {
|
|
|
21
22
|
}
|
|
22
23
|
export async function startAndListen(options) {
|
|
23
24
|
validateOptions(options);
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
const pgClient = getPgClient(options.superuserDatabaseUrl ?? SUPERUSER_DATABASE_URL);
|
|
26
|
+
await pgClient.connect();
|
|
27
|
+
try {
|
|
28
|
+
await runMigrations({
|
|
29
|
+
pgClient,
|
|
30
|
+
userDatabaseMigrationsPath: options.userDatabaseMigrationsPath,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
finally {
|
|
34
|
+
await pgClient.end();
|
|
35
|
+
}
|
|
27
36
|
const hubServer = createHubServer({
|
|
28
37
|
superuserDatabaseUrl: options.superuserDatabaseUrl ?? SUPERUSER_DATABASE_URL,
|
|
29
|
-
postgraphileDatabaseUrl: options.postgraphileDatabaseUrl ??
|
|
38
|
+
postgraphileDatabaseUrl: options.postgraphileDatabaseUrl ?? POSTGRAPHILE_DATABASE_URL,
|
|
30
39
|
configureApp: options.configureApp,
|
|
31
40
|
plugins: options.plugins,
|
|
32
41
|
exportSchemaSDLPath: options.exportSchemaSDLPath,
|
package/dist/src/migrations.d.ts
CHANGED
package/dist/src/migrations.js
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
import PgUpgradeSchema, { DatabaseAheadError, } from "@moneypot/pg-upgrade-schema";
|
|
2
|
-
import * as db from "./db/index.js";
|
|
3
|
-
import * as config from "./config.js";
|
|
4
2
|
import { join } from "path";
|
|
5
3
|
import { logger } from "./logger.js";
|
|
4
|
+
import { existsSync, statSync } from "fs";
|
|
6
5
|
export async function runMigrations(options) {
|
|
6
|
+
const { pgClient } = options;
|
|
7
7
|
if (options.userDatabaseMigrationsPath) {
|
|
8
|
-
const { existsSync, statSync } = await import("fs");
|
|
9
8
|
if (!existsSync(options.userDatabaseMigrationsPath)) {
|
|
10
9
|
throw new Error(`userDatabaseMigrationsPath does not exist: ${options.userDatabaseMigrationsPath}`);
|
|
11
10
|
}
|
|
@@ -14,38 +13,28 @@ export async function runMigrations(options) {
|
|
|
14
13
|
throw new Error(`userDatabaseMigrationsPath is not a directory: ${options.userDatabaseMigrationsPath}`);
|
|
15
14
|
}
|
|
16
15
|
}
|
|
17
|
-
const superuserDatabaseUrl = options.superuserDatabaseUrl ??
|
|
18
|
-
process.env.SUPERUSER_DATABASE_URL ??
|
|
19
|
-
config.SUPERUSER_DATABASE_URL;
|
|
20
|
-
const pgClient = db.getPgClient(superuserDatabaseUrl);
|
|
21
|
-
await pgClient.connect();
|
|
22
16
|
try {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
process.exit(1);
|
|
36
|
-
}
|
|
37
|
-
throw e;
|
|
38
|
-
}
|
|
39
|
-
if (options.userDatabaseMigrationsPath) {
|
|
40
|
-
await PgUpgradeSchema.default({
|
|
41
|
-
pgClient,
|
|
42
|
-
dirname: options.userDatabaseMigrationsPath,
|
|
43
|
-
schemaName: "hub_user_versions",
|
|
44
|
-
silent: process.env.NODE_ENV === "test",
|
|
45
|
-
});
|
|
17
|
+
await PgUpgradeSchema.default({
|
|
18
|
+
pgClient,
|
|
19
|
+
dirname: join(import.meta.dirname, "pg-versions"),
|
|
20
|
+
schemaName: "hub_core_versions",
|
|
21
|
+
silent: process.env.NODE_ENV === "test",
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
logger.error(e, "Error upgrading core schema");
|
|
26
|
+
if (e instanceof DatabaseAheadError) {
|
|
27
|
+
logger.error(`${"⚠️".repeat(10)}\n@moneypot/hub database was reset to prepare for a production release and you must reset your database to continue. Please see <https://www.npmjs.com/package/@moneypot/hub#change-log> for more info.`);
|
|
28
|
+
process.exit(1);
|
|
46
29
|
}
|
|
30
|
+
throw e;
|
|
47
31
|
}
|
|
48
|
-
|
|
49
|
-
await
|
|
32
|
+
if (options.userDatabaseMigrationsPath) {
|
|
33
|
+
await PgUpgradeSchema.default({
|
|
34
|
+
pgClient,
|
|
35
|
+
dirname: options.userDatabaseMigrationsPath,
|
|
36
|
+
schemaName: "hub_user_versions",
|
|
37
|
+
silent: process.env.NODE_ENV === "test",
|
|
38
|
+
});
|
|
50
39
|
}
|
|
51
40
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { ClientBase } from "pg";
|
|
2
2
|
import { GraphQLClient } from "graphql-request";
|
|
3
3
|
import { Result } from "../util.js";
|
|
4
4
|
import { QueryExecutor } from "../db/index.js";
|
|
5
|
-
export declare function verifyJwtFromDbCacheAndEnsureNotAlreadyUsed(pgClient:
|
|
5
|
+
export declare function verifyJwtFromDbCacheAndEnsureNotAlreadyUsed(pgClient: ClientBase, { casinoId, jwt, }: {
|
|
6
6
|
casinoId: string;
|
|
7
7
|
jwt: string;
|
|
8
8
|
}): Promise<Result<{
|
package/dist/src/test/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Pool } from "pg";
|
|
1
|
+
import { Client, Pool } from "pg";
|
|
2
2
|
import { randomBytes, randomUUID } from "node:crypto";
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
4
|
import { DbHashKind, } from "../db/types.js";
|
|
@@ -35,10 +35,17 @@ export async function startTestServer({ plugins = [...defaultPlugins], userDatab
|
|
|
35
35
|
process.env.LOG_LEVEL = process.env.LOG_LEVEL ?? "error";
|
|
36
36
|
process.env.HASHCHAINSERVER_URL = "mock-server";
|
|
37
37
|
process.env.HASHCHAINSERVER_APPLICATION_SECRET = "test-secret";
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
38
|
+
const pgClient = new Client({ connectionString });
|
|
39
|
+
await pgClient.connect();
|
|
40
|
+
try {
|
|
41
|
+
await runMigrations({
|
|
42
|
+
pgClient,
|
|
43
|
+
userDatabaseMigrationsPath,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
finally {
|
|
47
|
+
await pgClient.end();
|
|
48
|
+
}
|
|
42
49
|
const hubServer = createHubServer({
|
|
43
50
|
port: 0,
|
|
44
51
|
plugins,
|