@moneypot/hub 1.9.0 → 1.10.0-dev.1

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 (32) hide show
  1. package/dist/cli/add-casino.js +24 -17
  2. package/dist/src/context.d.ts +12 -0
  3. package/dist/src/context.js +31 -0
  4. package/dist/src/db/index.d.ts +3 -6
  5. package/dist/src/db/index.js +5 -13
  6. package/dist/src/hash-chain/plugins/hub-create-hash-chain.js +3 -2
  7. package/dist/src/index.d.ts +2 -0
  8. package/dist/src/index.js +10 -8
  9. package/dist/src/plugins/hub-add-casino.js +4 -2
  10. package/dist/src/plugins/hub-authenticate.js +3 -2
  11. package/dist/src/plugins/hub-claim-faucet.js +5 -3
  12. package/dist/src/plugins/hub-make-outcome-bet.js +6 -4
  13. package/dist/src/plugins/hub-withdraw.js +3 -2
  14. package/dist/src/process-transfers/index.d.ts +5 -2
  15. package/dist/src/process-transfers/index.js +15 -11
  16. package/dist/src/process-transfers/polling-processor.d.ts +3 -1
  17. package/dist/src/process-transfers/polling-processor.js +17 -13
  18. package/dist/src/process-transfers/process-transfer.d.ts +3 -1
  19. package/dist/src/process-transfers/process-transfer.js +7 -8
  20. package/dist/src/process-transfers/websocket-processor.d.ts +3 -1
  21. package/dist/src/process-transfers/websocket-processor.js +3 -3
  22. package/dist/src/process-withdrawal-request.d.ts +3 -1
  23. package/dist/src/process-withdrawal-request.js +6 -5
  24. package/dist/src/server/graphile.config.d.ts +3 -1
  25. package/dist/src/server/graphile.config.js +5 -4
  26. package/dist/src/server/index.d.ts +3 -1
  27. package/dist/src/server/index.js +5 -4
  28. package/dist/src/server/middleware/authentication.d.ts +2 -1
  29. package/dist/src/server/middleware/authentication.js +5 -5
  30. package/dist/src/take-request/process-take-request.d.ts +2 -1
  31. package/dist/src/take-request/process-take-request.js +11 -10
  32. package/package.json +1 -1
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { z } from "zod";
3
3
  import { dbInsertCasino } from "../src/db/internal.js";
4
- import { superuserPool, upsertCurrencies } from "../src/db/index.js";
4
+ import { upsertCurrencies } from "../src/db/index.js";
5
5
  import { createGraphqlClient } from "../src/graphql-client.js";
6
6
  import { GET_CURRENCIES } from "../src/graphql-queries.js";
7
7
  import { gql } from "../src/__generated__/gql.js";
8
+ import { createServerContext, closeServerContext } from "../src/context.js";
8
9
  const GET_CURRENT_CONTROLLER = gql(`
9
10
  query AddCasino_GetCurrentController {
10
11
  currentController {
@@ -66,24 +67,30 @@ function parseArgs(args) {
66
67
  return parsedArgs;
67
68
  }
68
69
  async function addCasino(input) {
69
- const graphqlClient = createGraphqlClient({
70
- graphqlUrl: input.graphql_url,
71
- apiKey: input.api_key,
72
- });
73
- console.log(`Verifying controller creds with ${input.graphql_url}...`);
74
- const result1 = await graphqlClient.request(GET_CURRENT_CONTROLLER);
75
- if (!result1.currentController) {
76
- throw new Error("Bad controller ID or API key");
70
+ const context = createServerContext();
71
+ try {
72
+ const graphqlClient = createGraphqlClient({
73
+ graphqlUrl: input.graphql_url,
74
+ apiKey: input.api_key,
75
+ });
76
+ console.log(`Verifying controller creds with ${input.graphql_url}...`);
77
+ const result1 = await graphqlClient.request(GET_CURRENT_CONTROLLER);
78
+ if (!result1.currentController) {
79
+ throw new Error("Bad controller ID or API key");
80
+ }
81
+ const result2 = await graphqlClient.request(GET_CURRENCIES);
82
+ const currencies = result2.allCurrencies?.nodes.flatMap((x) => (x ? [x] : [])) || [];
83
+ console.log(`Found ${currencies.length} currencies:`, currencies.map((x) => x.id).join(", "));
84
+ const fields = { ...input, controller_id: result1.currentController.id };
85
+ const casino = await dbInsertCasino(context.superuserPool, fields);
86
+ if (currencies.length > 0) {
87
+ await upsertCurrencies(context.superuserPool, { casinoId: casino.id, currencies });
88
+ }
89
+ return casino;
77
90
  }
78
- const result2 = await graphqlClient.request(GET_CURRENCIES);
79
- const currencies = result2.allCurrencies?.nodes.flatMap((x) => (x ? [x] : [])) || [];
80
- console.log(`Found ${currencies.length} currencies:`, currencies.map((x) => x.id).join(", "));
81
- const fields = { ...input, controller_id: result1.currentController.id };
82
- const casino = await dbInsertCasino(superuserPool, fields);
83
- if (currencies.length > 0) {
84
- await upsertCurrencies(superuserPool, { casinoId: casino.id, currencies });
91
+ finally {
92
+ await closeServerContext(context);
85
93
  }
86
- return casino;
87
94
  }
88
95
  function printUsageExample() {
89
96
  console.error("\nUsage example:");
@@ -0,0 +1,12 @@
1
+ import * as pg from "pg";
2
+ import { DatabaseNotifier } from "./db/index.js";
3
+ export interface ServerContext {
4
+ postgraphilePool: pg.Pool;
5
+ superuserPool: pg.Pool;
6
+ notifier: DatabaseNotifier;
7
+ }
8
+ export declare function createServerContext(overrides?: {
9
+ superuserDatabaseUrl?: string;
10
+ postgraphileDatabaseUrl?: string;
11
+ }): ServerContext;
12
+ export declare function closeServerContext(context: ServerContext): Promise<void>;
@@ -0,0 +1,31 @@
1
+ import * as pg from "pg";
2
+ import * as config from "./config.js";
3
+ import { logger } from "./logger.js";
4
+ import { DatabaseNotifier } from "./db/index.js";
5
+ export function createServerContext(overrides) {
6
+ const postgraphileDatabaseUrl = overrides?.postgraphileDatabaseUrl ?? config.DATABASE_URL;
7
+ const superuserDatabaseUrl = overrides?.superuserDatabaseUrl ?? config.SUPERUSER_DATABASE_URL;
8
+ const context = {
9
+ postgraphilePool: new pg.Pool({
10
+ connectionString: postgraphileDatabaseUrl,
11
+ }),
12
+ superuserPool: new pg.Pool({
13
+ connectionString: superuserDatabaseUrl,
14
+ }),
15
+ notifier: new DatabaseNotifier(postgraphileDatabaseUrl),
16
+ };
17
+ return context;
18
+ }
19
+ export async function closeServerContext(context) {
20
+ try {
21
+ await Promise.all([
22
+ context.notifier.close(),
23
+ context.postgraphilePool.end(),
24
+ context.superuserPool.end(),
25
+ ]);
26
+ }
27
+ catch (err) {
28
+ logger.warn(err, "Error closing server context resources");
29
+ throw err;
30
+ }
31
+ }
@@ -7,8 +7,6 @@ export * from "./types.js";
7
7
  export * from "./public.js";
8
8
  export * from "./util.js";
9
9
  export declare function getPgClient(connectionString: string): InstanceType<typeof pg.Client>;
10
- export declare const postgraphilePool: pg.Pool;
11
- export declare const superuserPool: pg.Pool;
12
10
  export interface QueryExecutor {
13
11
  query<T extends pg.QueryResultRow = any>(queryText: string, values?: any[]): Promise<pg.QueryResult<T>>;
14
12
  }
@@ -23,13 +21,12 @@ export declare function userFromActiveSessionKey(pgClient: QueryExecutor, sessio
23
21
  user: DbUser;
24
22
  sessionId: DbSession["id"];
25
23
  } | null>;
26
- declare class DatabaseNotifier extends stream.EventEmitter {
24
+ export declare class DatabaseNotifier extends stream.EventEmitter {
27
25
  private pgClient;
28
26
  constructor(connectionString: string);
29
27
  close(): Promise<void>;
30
28
  listen(): Promise<void>;
31
29
  }
32
- export declare const notifier: DatabaseNotifier;
33
30
  export declare function getTransferCursor(pgClient: QueryExecutor, { casinoId, }: {
34
31
  casinoId: string;
35
32
  }): Promise<string | undefined>;
@@ -47,7 +44,7 @@ export declare function upsertExperience(pgClient: QueryExecutor, { casinoId, mp
47
44
  mpExperienceId: string;
48
45
  name: string;
49
46
  }): Promise<DbExperience>;
50
- export declare function insertDeposit(o: {
47
+ export declare function insertDeposit(pool: pg.Pool, o: {
51
48
  casinoId: string;
52
49
  mpTransferId: string;
53
50
  userId: string;
@@ -78,7 +75,7 @@ export declare function getPendingWithdrawals(pgClient: QueryExecutor, { casinoI
78
75
  mp_experience_id: string;
79
76
  mp_user_id: string;
80
77
  })[]>;
81
- export declare function settleWithdrawal({ withdrawalId, newStatus, }: {
78
+ export declare function settleWithdrawal(pool: pg.Pool, { withdrawalId, newStatus, }: {
82
79
  withdrawalId: string;
83
80
  newStatus: Extract<DbTransferStatusKind, "COMPLETED" | "CANCELED">;
84
81
  }): Promise<void>;
@@ -1,5 +1,4 @@
1
1
  import * as pg from "pg";
2
- import * as config from "../config.js";
3
2
  import stream from "node:stream";
4
3
  import { exactlyOneRow, maybeOneRow } from "./util.js";
5
4
  import { logger } from "../logger.js";
@@ -13,12 +12,6 @@ export function getPgClient(connectionString) {
13
12
  const client = new pg.Client({ connectionString });
14
13
  return client;
15
14
  }
16
- export const postgraphilePool = new pg.Pool({
17
- connectionString: config.DATABASE_URL,
18
- });
19
- export const superuserPool = new pg.Pool({
20
- connectionString: config.SUPERUSER_DATABASE_URL,
21
- });
22
15
  export class UserFriendlyError extends Error {
23
16
  constructor(userFriendlyMessage) {
24
17
  super(userFriendlyMessage);
@@ -93,7 +86,7 @@ export async function userFromActiveSessionKey(pgClient, sessionKey) {
93
86
  sessionId: session_id,
94
87
  };
95
88
  }
96
- class DatabaseNotifier extends stream.EventEmitter {
89
+ export class DatabaseNotifier extends stream.EventEmitter {
97
90
  pgClient;
98
91
  constructor(connectionString) {
99
92
  super();
@@ -124,7 +117,6 @@ class DatabaseNotifier extends stream.EventEmitter {
124
117
  });
125
118
  }
126
119
  }
127
- export const notifier = new DatabaseNotifier(config.DATABASE_URL);
128
120
  export async function getTransferCursor(pgClient, { casinoId, }) {
129
121
  const row = await pgClient
130
122
  .query("select cursor from hub_hidden.transfer_cursor where casino_id = $1", [casinoId])
@@ -175,11 +167,11 @@ export async function upsertExperience(pgClient, { casinoId, mpExperienceId, nam
175
167
  `, [casinoId, mpExperienceId, name])
176
168
  .then(exactlyOneRow);
177
169
  }
178
- export async function insertDeposit(o) {
170
+ export async function insertDeposit(pool, o) {
179
171
  if (o.amount <= 0) {
180
172
  throw new UserFriendlyError("amount must be positive");
181
173
  }
182
- return withPgPoolTransaction(superuserPool, async (client) => {
174
+ return withPgPoolTransaction(pool, async (client) => {
183
175
  const result = await client.query(`
184
176
  INSERT INTO hub.deposit(casino_id, mp_transfer_id, user_id, experience_id, amount, currency_key)
185
177
  VALUES ($1, $2, $3, $4, $5, $6)
@@ -268,8 +260,8 @@ export async function getPendingWithdrawals(pgClient, { casinoId, limit, }) {
268
260
  `, [casinoId, limit]);
269
261
  return result.rows;
270
262
  }
271
- export async function settleWithdrawal({ withdrawalId, newStatus, }) {
272
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
263
+ export async function settleWithdrawal(pool, { withdrawalId, newStatus, }) {
264
+ return withPgPoolTransaction(pool, async (pgClient) => {
273
265
  const dbWithdrawal = await pgClient
274
266
  .query(`
275
267
  select *
@@ -2,7 +2,7 @@ import { context, object, sideEffect } from "@moneypot/hub/grafast";
2
2
  import { gql, makeExtendSchemaPlugin } from "@moneypot/hub/graphile";
3
3
  import { GraphQLError } from "graphql";
4
4
  import { PgAdvisoryLock } from "../../pg-advisory-lock.js";
5
- import { exactlyOneRow, superuserPool, withPgPoolTransaction, } from "@moneypot/hub/db";
5
+ import { exactlyOneRow, withPgPoolTransaction } from "@moneypot/hub/db";
6
6
  import * as HashCommon from "../get-hash.js";
7
7
  import { DbHashKind } from "../../db/types.js";
8
8
  import * as config from "../../config.js";
@@ -23,7 +23,8 @@ export const HubCreateHashChainPlugin = makeExtendSchemaPlugin((build) => {
23
23
  plans: {
24
24
  hubCreateHashChain: () => {
25
25
  const $identity = context().get("identity");
26
- const $hashChainId = sideEffect([$identity], ([identity]) => {
26
+ const $superuserPool = context().get("superuserPool");
27
+ const $hashChainId = sideEffect([$identity, $superuserPool], ([identity, superuserPool]) => {
27
28
  if (identity?.kind !== "user") {
28
29
  throw new GraphQLError("Unauthorized");
29
30
  }
@@ -1,10 +1,12 @@
1
1
  import { Express } from "express";
2
2
  import { type PluginIdentity } from "./server/graphile.config.js";
3
+ import { ServerContext } from "./context.js";
3
4
  declare global {
4
5
  namespace Grafast {
5
6
  interface Context {
6
7
  identity?: PluginIdentity;
7
8
  abortSignal: AbortSignal;
9
+ superuserPool: ServerContext["superuserPool"];
8
10
  }
9
11
  }
10
12
  }
package/dist/src/index.js CHANGED
@@ -5,6 +5,7 @@ import { createHubServer } from "./server/index.js";
5
5
  import { initializeTransferProcessors } from "./process-transfers/index.js";
6
6
  import { join } from "path";
7
7
  import { logger } from "./logger.js";
8
+ import { createServerContext, closeServerContext, } from "./context.js";
8
9
  export { MakeOutcomeBetPlugin, } from "./plugins/hub-make-outcome-bet.js";
9
10
  export { validateRisk, } from "./risk-policy.js";
10
11
  export { defaultPlugins, } from "./server/graphile.config.js";
@@ -49,6 +50,7 @@ async function initialize(options) {
49
50
  if (process.env.NODE_ENV !== "test") {
50
51
  initializeTransferProcessors({
51
52
  signal: options.signal,
53
+ pool: options.context.superuserPool,
52
54
  });
53
55
  }
54
56
  }
@@ -66,9 +68,11 @@ export async function startAndListen(options) {
66
68
  }
67
69
  const abortController = new AbortController();
68
70
  let isShuttingDown = false;
71
+ const context = createServerContext();
69
72
  await initialize({
70
73
  userDatabaseMigrationsPath: options.userDatabaseMigrationsPath,
71
74
  signal: abortController.signal,
75
+ context,
72
76
  });
73
77
  const hubServer = createHubServer({
74
78
  configureApp: options.configureApp,
@@ -76,6 +80,7 @@ export async function startAndListen(options) {
76
80
  exportSchemaSDLPath: options.exportSchemaSDLPath,
77
81
  extraPgSchemas: options.extraPgSchemas,
78
82
  abortSignal: abortController.signal,
83
+ context,
79
84
  });
80
85
  const gracefulShutdown = async ({ exit = true } = {}) => {
81
86
  if (isShuttingDown) {
@@ -89,13 +94,8 @@ export async function startAndListen(options) {
89
94
  logger.info("Closing resources...");
90
95
  hubServer.shutdown();
91
96
  try {
92
- const closeTasks = [db.notifier.close()];
93
- if (process.env.NODE_ENV !== "test") {
94
- closeTasks.push(db.postgraphilePool.end());
95
- closeTasks.push(db.superuserPool.end());
96
- }
97
97
  await Promise.race([
98
- Promise.all(closeTasks),
98
+ closeServerContext(context),
99
99
  new Promise((_, reject) => setTimeout(() => reject(new Error("Database cleanup timeout")), 3000)),
100
100
  ]);
101
101
  logger.info("Cleanup complete.");
@@ -107,8 +107,10 @@ export async function startAndListen(options) {
107
107
  process.exit(0);
108
108
  }
109
109
  };
110
- process.on("SIGINT", gracefulShutdown);
111
- process.on("SIGTERM", gracefulShutdown);
110
+ if (process.env.NODE_ENV !== "test") {
111
+ process.on("SIGINT", gracefulShutdown);
112
+ process.on("SIGTERM", gracefulShutdown);
113
+ }
112
114
  return hubServer.listen().then(() => {
113
115
  return {
114
116
  port: config.PORT,
@@ -4,7 +4,7 @@ import { exactlyOneRow } from "../db/util.js";
4
4
  import { gql as generatedGql } from "../__generated__/gql.js";
5
5
  import { GraphQLClient } from "graphql-request";
6
6
  import { GraphQLError } from "graphql";
7
- import { superuserPool, upsertCurrencies, withPgPoolTransaction, } from "../db/index.js";
7
+ import { upsertCurrencies, withPgPoolTransaction, } from "../db/index.js";
8
8
  import { logger } from "../logger.js";
9
9
  import { assert, is } from "tsafe";
10
10
  import * as jwtService from "../services/jwt-service.js";
@@ -48,8 +48,9 @@ export const HubAddCasinoPlugin = makeExtendSchemaPlugin((build) => {
48
48
  plans: {
49
49
  hubAddCasino(_, { $input }) {
50
50
  const $identity = context().get("identity");
51
+ const $superuserPool = context().get("superuserPool");
51
52
  const $abortSignal = context().get("abortSignal");
52
- const $casinoId = sideEffect([$input, $identity, $abortSignal], ([input, identity, abortSignal]) => {
53
+ const $casinoId = sideEffect([$input, $identity, $superuserPool, $abortSignal], ([input, identity, superuserPool, abortSignal]) => {
53
54
  return withPgPoolTransaction(superuserPool, async (pgClient) => {
54
55
  if (identity?.kind !== "operator") {
55
56
  throw new GraphQLError("Unauthorized");
@@ -141,6 +142,7 @@ export const HubAddCasinoPlugin = makeExtendSchemaPlugin((build) => {
141
142
  startCasinoTransferProcessor({
142
143
  casinoId: casino.id,
143
144
  signal: abortSignal,
145
+ pool: superuserPool,
144
146
  });
145
147
  return casino.id;
146
148
  });
@@ -5,7 +5,7 @@ import { GET_USER_FROM_USER_TOKEN } from "../graphql-queries.js";
5
5
  import { exactlyOneRow, maybeOneRow } from "../db/util.js";
6
6
  import { createGraphqlClient } from "../graphql-client.js";
7
7
  import { constant, context, error, object, sideEffect, } from "postgraphile/grafast";
8
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
8
+ import { withPgPoolTransaction, } from "../db/index.js";
9
9
  import { logger } from "../logger.js";
10
10
  import * as jwtService from "../services/jwt-service.js";
11
11
  import { extractGraphQLErrorInfo, isGraphQLError } from "../GraphQLError.js";
@@ -51,7 +51,8 @@ export const HubAuthenticatePlugin = makeExtendSchemaPlugin(() => {
51
51
  hubAuthenticate(_, { $input }) {
52
52
  try {
53
53
  const $context = context();
54
- const $success = sideEffect([$input, $context], ([rawInput, context]) => {
54
+ const $superuserPool = $context.get("superuserPool");
55
+ const $success = sideEffect([$input, $superuserPool, $context], ([rawInput, superuserPool, context]) => {
55
56
  return withPgPoolTransaction(superuserPool, async (pgClient) => {
56
57
  let input;
57
58
  try {
@@ -1,6 +1,6 @@
1
1
  import { object } from "grafast";
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
3
+ import { withPgPoolTransaction, } from "../db/index.js";
4
4
  import { constant, context, sideEffect } from "postgraphile/grafast";
5
5
  import { assert } from "tsafe";
6
6
  const CLAIM_AMOUNT = 1000;
@@ -20,8 +20,10 @@ export const HubClaimFaucetPlugin = makeExtendSchemaPlugin(() => {
20
20
  Mutation: {
21
21
  plans: {
22
22
  hubClaimFaucet() {
23
- const $identity = context().get("identity");
24
- const $result = sideEffect([$identity], ([identity]) => {
23
+ const $context = context();
24
+ const $identity = $context.get("identity");
25
+ const $superuserPool = $context.get("superuserPool");
26
+ const $result = sideEffect([$identity, $superuserPool], ([identity, superuserPool]) => {
25
27
  if (identity?.kind !== "user") {
26
28
  throw new Error("Must be logged in as user");
27
29
  }
@@ -2,7 +2,7 @@ import { access, context, object, ObjectStep, sideEffect, } from "postgraphile/g
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
3
  import * as z from "zod";
4
4
  import { GraphQLError } from "graphql";
5
- import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
5
+ import { DbHashKind, dbLockPlayerBalanceAndHouseBankroll, exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
6
6
  import { assert } from "tsafe";
7
7
  import { dbInsertHubHash, dbLockHubHashChain, } from "../hash-chain/db-hash-chain.js";
8
8
  import { getIntermediateHash, getPreimageHash, } from "../hash-chain/get-hash.js";
@@ -106,7 +106,8 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
106
106
  plans: {
107
107
  hubMakeOutcomeBet: (_, { $input }) => {
108
108
  const $identity = context().get("identity");
109
- const $result = sideEffect([$identity, $input], async ([identity, rawInput]) => {
109
+ const $superuserPool = context().get("superuserPool");
110
+ const $result = sideEffect([$identity, $superuserPool, $input], async ([identity, superuserPool, rawInput]) => {
110
111
  if (identity?.kind !== "user") {
111
112
  throw new GraphQLError("Unauthorized");
112
113
  }
@@ -217,6 +218,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
217
218
  if (dbHashChain.current_iteration === 1) {
218
219
  finishHashChainInBackground({
219
220
  hashChainId: input.hashChainId,
221
+ pool: superuserPool,
220
222
  }).catch((e) => {
221
223
  logger.error({ hashChainId: input.hashChainId, error: e }, "Error finishing hash chain in background");
222
224
  });
@@ -403,7 +405,7 @@ export function MakeOutcomeBetPlugin({ betConfigs }) {
403
405
  };
404
406
  }, "HubMakeOutcomeBetPlugin");
405
407
  }
406
- async function finishHashChainInBackground({ hashChainId, }) {
408
+ async function finishHashChainInBackground({ hashChainId, pool, }) {
407
409
  logger.debug({ hashChainId }, "Finishing hash chain in background");
408
410
  const preimageHashResult = await getPreimageHash({
409
411
  hashChainId,
@@ -416,7 +418,7 @@ async function finishHashChainInBackground({ hashChainId, }) {
416
418
  digest: preimageHashResult.hash,
417
419
  iteration: 0,
418
420
  }, "Inserting preimage hash");
419
- await withPgPoolTransaction(superuserPool, async (pgClient) => {
421
+ await withPgPoolTransaction(pool, async (pgClient) => {
420
422
  await dbInsertHubHash(pgClient, {
421
423
  hashChainId,
422
424
  kind: DbHashKind.PREIMAGE,
@@ -1,6 +1,6 @@
1
1
  import { constant, context, object, sideEffect } from "postgraphile/grafast";
2
2
  import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
3
+ import { withPgPoolTransaction } from "../db/index.js";
4
4
  import { exactlyOneRow, maybeOneRow } from "../db/util.js";
5
5
  import { GraphQLError } from "graphql";
6
6
  export const HubWithdrawPlugin = makeExtendSchemaPlugin((build) => {
@@ -26,7 +26,8 @@ export const HubWithdrawPlugin = makeExtendSchemaPlugin((build) => {
26
26
  plans: {
27
27
  hubWithdraw(_, { $input }) {
28
28
  const $identity = context().get("identity");
29
- const $withdrawalRequestId = sideEffect([$input, $identity], ([input, identity]) => {
29
+ const $superuserPool = context().get("superuserPool");
30
+ const $withdrawalRequestId = sideEffect([$input, $identity, $superuserPool], ([input, identity, superuserPool]) => {
30
31
  if (identity?.kind !== "user") {
31
32
  throw new GraphQLError("You must be logged in");
32
33
  }
@@ -1,9 +1,12 @@
1
1
  import { DbCasino, DbCasinoSecret } from "../db/index.js";
2
+ import * as pg from "pg";
2
3
  export type CasinoWithSecret = DbCasino & DbCasinoSecret;
3
- export declare function startCasinoTransferProcessor({ casinoId, signal, }: {
4
+ export declare function startCasinoTransferProcessor({ casinoId, signal, pool, }: {
4
5
  casinoId: string;
5
6
  signal: AbortSignal;
7
+ pool: pg.Pool;
6
8
  }): Promise<void>;
7
- export declare function initializeTransferProcessors({ signal, }: {
9
+ export declare function initializeTransferProcessors({ signal, pool, }: {
8
10
  signal: AbortSignal;
11
+ pool: pg.Pool;
9
12
  }): void;
@@ -1,6 +1,5 @@
1
1
  import { startPollingProcessor } from "./polling-processor.js";
2
2
  import { startWebsocketProcessor } from "./websocket-processor.js";
3
- import { superuserPool } from "../db/index.js";
4
3
  import { dbGetCasinoById, dbGetCasinoSecretById } from "../db/internal.js";
5
4
  import assert from "assert";
6
5
  import { logger } from "../logger.js";
@@ -9,28 +8,29 @@ import * as db from "../db/index.js";
9
8
  import * as pg from "pg";
10
9
  import { z } from "zod";
11
10
  const activeCasinos = new Set();
12
- export async function startCasinoTransferProcessor({ casinoId, signal, }) {
11
+ export async function startCasinoTransferProcessor({ casinoId, signal, pool, }) {
13
12
  if (activeCasinos.has(casinoId)) {
14
13
  throw new Error(`processor already running for casino ${casinoId}`);
15
14
  }
16
- const casino = await dbGetCasinoById(superuserPool, casinoId);
17
- const secret = await dbGetCasinoSecretById(superuserPool, casinoId);
15
+ const casino = await dbGetCasinoById(pool, casinoId);
16
+ const secret = await dbGetCasinoSecretById(pool, casinoId);
18
17
  assert(casino, `Casino not found for casino id ${casinoId}`);
19
18
  assert(secret, `Secret not found for casino id ${casinoId}`);
20
19
  activeCasinos.add(casinoId);
21
- startPollingProcessor({ casinoId, signal });
20
+ startPollingProcessor({ casinoId, signal, pool });
22
21
  startWebsocketProcessor({
23
22
  casinoId,
24
23
  graphqlUrl: casino.graphql_url,
25
24
  signal,
26
25
  controllerId: secret.controller_id,
27
26
  apiKey: secret.api_key,
27
+ pool,
28
28
  });
29
29
  }
30
- export function initializeTransferProcessors({ signal, }) {
30
+ export function initializeTransferProcessors({ signal, pool, }) {
31
31
  (async () => {
32
32
  try {
33
- const casinos = await db.listCasinos(superuserPool);
33
+ const casinos = await db.listCasinos(pool);
34
34
  for (const casino of casinos) {
35
35
  if (!URL.canParse(casino.graphql_url)) {
36
36
  logger.warn(`Skipping casino ${casino.id} due to invalid graphql_url: "${casino.graphql_url}"`);
@@ -41,16 +41,16 @@ export function initializeTransferProcessors({ signal, }) {
41
41
  logger.warn(`${casino.id} has localhost endpoint "${casino.graphql_url}" while NODE_ENV=production.`);
42
42
  }
43
43
  logger.info(`Starting casino processor for "${casino.name}" at "${casino.graphql_url}"`);
44
- startCasinoTransferProcessor({ casinoId: casino.id, signal });
44
+ startCasinoTransferProcessor({ casinoId: casino.id, signal, pool });
45
45
  }
46
- await listenForNewCasinos({ signal });
46
+ await listenForNewCasinos({ signal, pool });
47
47
  }
48
48
  catch (e) {
49
49
  logger.error(e, `Error initializing transfer processors`);
50
50
  }
51
51
  })();
52
52
  }
53
- async function listenForNewCasinos({ signal }) {
53
+ async function listenForNewCasinos({ signal, pool, }) {
54
54
  const pgClient = new pg.Client(config.SUPERUSER_DATABASE_URL);
55
55
  await pgClient.connect();
56
56
  const NewCasinoPayload = z.object({
@@ -76,7 +76,11 @@ async function listenForNewCasinos({ signal }) {
76
76
  logger.error(result.error, "Error parsing new casino notification");
77
77
  return;
78
78
  }
79
- startCasinoTransferProcessor({ casinoId: result.data.id, signal });
79
+ startCasinoTransferProcessor({
80
+ casinoId: result.data.id,
81
+ signal,
82
+ pool,
83
+ });
80
84
  break;
81
85
  }
82
86
  }
@@ -1,10 +1,12 @@
1
1
  import * as db from "../db/index.js";
2
+ import * as pg from "pg";
2
3
  export declare const PAGINATE_TRANSFERS: import("@graphql-typed-document-node/core").TypedDocumentNode<import("../__generated__/graphql.js").PaginateTransfersQuery, import("../__generated__/graphql.js").Exact<{
3
4
  controllerId: import("../__generated__/graphql.js").Scalars["UUID"]["input"];
4
5
  after?: import("../__generated__/graphql.js").InputMaybe<import("../__generated__/graphql.js").Scalars["Cursor"]["input"]>;
5
6
  limit?: import("../__generated__/graphql.js").InputMaybe<import("../__generated__/graphql.js").Scalars["Int"]["input"]>;
6
7
  }>>;
7
- export declare function startPollingProcessor({ casinoId, signal, }: {
8
+ export declare function startPollingProcessor({ casinoId, signal, pool, }: {
8
9
  casinoId: db.DbCasino["id"];
9
10
  signal: AbortSignal;
11
+ pool: pg.Pool;
10
12
  }): void;
@@ -8,7 +8,6 @@ import { processWithdrawalRequests } from "../process-withdrawal-request.js";
8
8
  import { processTakeRequests } from "../take-request/process-take-request.js";
9
9
  import * as jwtService from "../services/jwt-service.js";
10
10
  import { dbGetCasinoById, dbGetCasinoSecretById } from "../db/internal.js";
11
- import { superuserPool } from "../db/index.js";
12
11
  import { TransferStatusKind } from "../__generated__/graphql.js";
13
12
  import { MP_COMPLETE_TRANSFER, processTransfer } from "./process-transfer.js";
14
13
  import { gql } from "../__generated__/gql.js";
@@ -44,7 +43,7 @@ export const PAGINATE_TRANSFERS = gql(`
44
43
  `);
45
44
  const MIN_BACKOFF_TIME = 5000;
46
45
  const MAX_BACKOFF_TIME = 30 * 1000;
47
- export function startPollingProcessor({ casinoId, signal, }) {
46
+ export function startPollingProcessor({ casinoId, signal, pool, }) {
48
47
  if (signal.aborted) {
49
48
  logger.info(`[startTransferProcessor] AbortSignal aborted. Not starting processor for casino ${casinoId}`);
50
49
  return;
@@ -55,7 +54,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
55
54
  lastAttempt: 0,
56
55
  };
57
56
  (async () => {
58
- let cursor = await db.getTransferCursor(superuserPool, {
57
+ let cursor = await db.getTransferCursor(pool, {
59
58
  casinoId,
60
59
  });
61
60
  if (cursor && !isUuid(cursor)) {
@@ -80,13 +79,13 @@ export function startPollingProcessor({ casinoId, signal, }) {
80
79
  await new Promise((resolve) => setTimeout(resolve, timeToWait));
81
80
  }
82
81
  processorState.lastAttempt = Date.now();
83
- const casino = await dbGetCasinoById(superuserPool, casinoId);
82
+ const casino = await dbGetCasinoById(pool, casinoId);
84
83
  if (!casino) {
85
84
  logger.warn(`casino ${casinoId} not found. Stopping processor...`);
86
85
  shouldStop = true;
87
86
  break;
88
87
  }
89
- const casinoSecret = await dbGetCasinoSecretById(superuserPool, casino.id);
88
+ const casinoSecret = await dbGetCasinoSecretById(pool, casino.id);
90
89
  if (!casinoSecret) {
91
90
  logger.warn(`Casino secret hasn't been set for casino ${casinoId}. Skipping...`);
92
91
  continue;
@@ -99,7 +98,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
99
98
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (1)`);
100
99
  break;
101
100
  }
102
- await jwtService.refreshCasinoJwksTask(superuserPool, {
101
+ await jwtService.refreshCasinoJwksTask(pool, {
103
102
  casinoId: casino.id,
104
103
  graphqlClient,
105
104
  });
@@ -114,7 +113,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
114
113
  return (res.allCurrencies?.nodes || []).flatMap((x) => x ? [x] : []);
115
114
  });
116
115
  if (currencies.length > 0) {
117
- await db.upsertCurrencies(superuserPool, {
116
+ await db.upsertCurrencies(pool, {
118
117
  casinoId: casino.id,
119
118
  currencies,
120
119
  });
@@ -130,6 +129,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
130
129
  graphqlClient,
131
130
  casinoInfo: { ...casino, ...casinoSecret },
132
131
  signal,
132
+ pool,
133
133
  });
134
134
  if (signal.aborted) {
135
135
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (4)`);
@@ -139,6 +139,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
139
139
  abortSignal: signal,
140
140
  casinoId: casino.id,
141
141
  graphqlClient,
142
+ pool,
142
143
  });
143
144
  if (signal.aborted) {
144
145
  logger.info(`[startTransferProcessor] Aborted by graceful shutdown. (5)`);
@@ -149,6 +150,7 @@ export function startPollingProcessor({ casinoId, signal, }) {
149
150
  controllerId: casinoSecret.controller_id,
150
151
  casinoId: casino.id,
151
152
  graphqlClient,
153
+ pool,
152
154
  });
153
155
  processorState.backoffTime = MIN_BACKOFF_TIME;
154
156
  }
@@ -161,11 +163,11 @@ export function startPollingProcessor({ casinoId, signal, }) {
161
163
  logger.info(`processor stopped for casino ${casinoId}`);
162
164
  })();
163
165
  }
164
- async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
166
+ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, pool, }) {
165
167
  if (abortSignal.aborted) {
166
168
  return;
167
169
  }
168
- const withdrawals = await db.getPendingWithdrawals(superuserPool, {
170
+ const withdrawals = await db.getPendingWithdrawals(pool, {
169
171
  casinoId,
170
172
  limit: 10,
171
173
  });
@@ -185,7 +187,7 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
185
187
  case undefined:
186
188
  break;
187
189
  case "CompleteTransferSuccess": {
188
- await db.settleWithdrawal({
190
+ await db.settleWithdrawal(pool, {
189
191
  withdrawalId: withdrawal.id,
190
192
  newStatus: "COMPLETED",
191
193
  });
@@ -198,7 +200,7 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
198
200
  switch (currentState) {
199
201
  case TransferStatusKind.Canceled:
200
202
  case TransferStatusKind.Completed:
201
- await db.settleWithdrawal({
203
+ await db.settleWithdrawal(pool, {
202
204
  withdrawalId: withdrawal.id,
203
205
  newStatus: currentState,
204
206
  });
@@ -224,13 +226,14 @@ async function processWithdrawals({ abortSignal, casinoId, graphqlClient, }) {
224
226
  }
225
227
  }
226
228
  }
227
- async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, }) {
229
+ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoInfo, signal, pool, }) {
228
230
  let hasNextPage = true;
229
231
  const timeout = (ms) => new Promise((res) => setTimeout(res, ms));
230
232
  while (hasNextPage && !signal.aborted) {
231
233
  await processWithdrawalRequests({
232
234
  casinoId: casinoInfo.id,
233
235
  graphqlClient,
236
+ pool,
234
237
  });
235
238
  if (signal.aborted) {
236
239
  logger.info(`[processTransfersUntilEmpty] Aborted by graceful shutdown.`);
@@ -266,8 +269,9 @@ async function processTransfersUntilEmpty({ afterCursor, graphqlClient, casinoIn
266
269
  controllerId: casinoInfo.controller_id,
267
270
  transfer,
268
271
  graphqlClient,
272
+ pool,
269
273
  });
270
- await db.setTransferCursor(superuserPool, {
274
+ await db.setTransferCursor(pool, {
271
275
  cursor,
272
276
  casinoId: casinoInfo.id,
273
277
  });
@@ -1,13 +1,15 @@
1
1
  import { TransferFieldsFragment } from "../__generated__/graphql.js";
2
2
  import { GraphQLClient } from "graphql-request";
3
+ import * as pg from "pg";
3
4
  export declare const MP_COMPLETE_TRANSFER: import("@graphql-typed-document-node/core").TypedDocumentNode<import("../__generated__/graphql.js").CompleteTransferMutation, import("../__generated__/graphql.js").Exact<{
4
5
  mpTransferId: import("../__generated__/graphql.js").Scalars["UUID"]["input"];
5
6
  }>>;
6
- export declare function processTransfer({ casinoId, controllerId, transfer, graphqlClient, }: {
7
+ export declare function processTransfer({ casinoId, controllerId, transfer, graphqlClient, pool, }: {
7
8
  casinoId: string;
8
9
  controllerId: string;
9
10
  transfer: Extract<TransferFieldsFragment, {
10
11
  __typename: "ExperienceTransfer";
11
12
  }>;
12
13
  graphqlClient: GraphQLClient;
14
+ pool: pg.Pool;
13
15
  }): Promise<void>;
@@ -1,6 +1,5 @@
1
1
  import * as db from "../db/index.js";
2
2
  import { TransferStatusKind, } from "../__generated__/graphql.js";
3
- import { superuserPool } from "../db/index.js";
4
3
  import { gql } from "../__generated__/gql.js";
5
4
  import { logger } from "../logger.js";
6
5
  import { assert } from "tsafe";
@@ -50,7 +49,7 @@ const MP_CLAIM_TRANSFER = gql(`
50
49
  }
51
50
  }
52
51
  `);
53
- export async function processTransfer({ casinoId, controllerId, transfer, graphqlClient, }) {
52
+ export async function processTransfer({ casinoId, controllerId, transfer, graphqlClient, pool, }) {
54
53
  assert(transfer, "Expected transfer");
55
54
  assert(transfer.__typename === "ExperienceTransfer", `Expected ExperienceTransfer but got ${transfer.__typename}`);
56
55
  logger.debug(`processing transfer ${transfer.id} for casino ${casinoId}...`);
@@ -62,17 +61,17 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
62
61
  : transfer.holderByToHolderId;
63
62
  assert(user?.__typename === "User", "Expected user transfer participant");
64
63
  const currency = transfer.currencyByCurrency;
65
- const dbSender = await db.upsertUser(superuserPool, {
64
+ const dbSender = await db.upsertUser(pool, {
66
65
  casinoId,
67
66
  mpUserId: user.id,
68
67
  uname: user.uname,
69
68
  });
70
- const dbExperience = await db.upsertExperience(superuserPool, {
69
+ const dbExperience = await db.upsertExperience(pool, {
71
70
  casinoId,
72
71
  mpExperienceId: transfer.experienceByExperienceId.id,
73
72
  name: transfer.experienceByExperienceId.name,
74
73
  });
75
- await db.upsertCurrencies(superuserPool, {
74
+ await db.upsertCurrencies(pool, {
76
75
  casinoId,
77
76
  currencies: [currency],
78
77
  });
@@ -85,7 +84,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
85
84
  case TransferStatusKind.Expired:
86
85
  return;
87
86
  case TransferStatusKind.Completed: {
88
- await db.insertDeposit({
87
+ await db.insertDeposit(pool, {
89
88
  casinoId,
90
89
  mpTransferId: transfer.id,
91
90
  userId: dbSender.id,
@@ -110,7 +109,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
110
109
  if (data.claimTransfer?.result.__typename !== "ClaimTransferSuccess") {
111
110
  throw new Error(`Failed to claim transfer: ${JSON.stringify(data.claimTransfer)}`);
112
111
  }
113
- await db.insertDeposit({
112
+ await db.insertDeposit(pool, {
114
113
  casinoId,
115
114
  mpTransferId: transfer.id,
116
115
  userId: dbSender.id,
@@ -130,7 +129,7 @@ export async function processTransfer({ casinoId, controllerId, transfer, graphq
130
129
  logger.debug(`I sent ${user.uname} ${transfer.amount} base units of ${currency.id}`);
131
130
  switch (transfer.status) {
132
131
  case TransferStatusKind.Canceled:
133
- await db.settleWithdrawal({
132
+ await db.settleWithdrawal(pool, {
134
133
  withdrawalId: transfer.id,
135
134
  newStatus: "CANCELED",
136
135
  });
@@ -1,7 +1,9 @@
1
- export declare function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, }: {
1
+ import * as pg from "pg";
2
+ export declare function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, pool, }: {
2
3
  casinoId: string;
3
4
  graphqlUrl: string;
4
5
  signal: AbortSignal;
5
6
  controllerId: string;
6
7
  apiKey: string;
8
+ pool: pg.Pool;
7
9
  }): void;
@@ -7,7 +7,6 @@ import { TRANSFER_FIELDS } from "./graphql.js";
7
7
  import { logger } from "../logger.js";
8
8
  import { processTransfer } from "./process-transfer.js";
9
9
  import assert from "assert";
10
- import { superuserPool } from "../db/index.js";
11
10
  import { mpGetTakeRequest, processSingleTakeRequest, } from "../take-request/process-take-request.js";
12
11
  function httpToWs(url) {
13
12
  if (url.protocol === "http:") {
@@ -50,7 +49,7 @@ async function mpGetExperienceTransfer(graphqlClient, id) {
50
49
  const mpTransfer = useFragment(TRANSFER_FIELDS, x);
51
50
  return mpTransfer;
52
51
  }
53
- export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, }) {
52
+ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controllerId, apiKey, pool, }) {
54
53
  logger.info(`Starting websocket processor for ${graphqlUrl} using apiKey ${apiKey}`);
55
54
  const client = createWebsocketClient({
56
55
  url: httpToWs(new URL(graphqlUrl)).toString(),
@@ -90,6 +89,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
90
89
  controllerId,
91
90
  transfer: mpTransfer,
92
91
  graphqlClient,
92
+ pool,
93
93
  });
94
94
  });
95
95
  const dispose2 = createSubscription(client, MP_NEW_TAKE_REQUEST, async (result) => {
@@ -108,7 +108,7 @@ export function startWebsocketProcessor({ casinoId, graphqlUrl, signal, controll
108
108
  mpTakeRequest,
109
109
  casinoId,
110
110
  graphqlClient,
111
- pool: superuserPool,
111
+ pool,
112
112
  });
113
113
  });
114
114
  signal.addEventListener("abort", () => {
@@ -1,5 +1,7 @@
1
+ import * as pg from "pg";
1
2
  import { GraphQLClient } from "graphql-request";
2
- export declare function processWithdrawalRequests({ casinoId, graphqlClient, }: {
3
+ export declare function processWithdrawalRequests({ casinoId, graphqlClient, pool, }: {
3
4
  casinoId: string;
4
5
  graphqlClient: GraphQLClient;
6
+ pool: pg.Pool;
5
7
  }): Promise<void>;
@@ -1,11 +1,11 @@
1
1
  import { assert } from "tsafe";
2
2
  import { TransferStatusKind } from "./__generated__/graphql.js";
3
- import { withPgPoolTransaction, superuserPool } from "./db/index.js";
3
+ import { withPgPoolTransaction } from "./db/index.js";
4
4
  import { maybeOneRow, exactlyOneRow } from "./db/util.js";
5
5
  import { START_PENDING_EXPERIENCE_TRANSFER } from "./graphql-queries.js";
6
6
  import { logger } from "./logger.js";
7
- async function processWithdrawalRequest({ requestId, graphqlClient, }) {
8
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
7
+ async function processWithdrawalRequest({ requestId, graphqlClient, pool, }) {
8
+ return withPgPoolTransaction(pool, async (pgClient) => {
9
9
  const dbWithdrawalRequest = await pgClient
10
10
  .query({
11
11
  text: `
@@ -120,8 +120,8 @@ async function processWithdrawalRequest({ requestId, graphqlClient, }) {
120
120
  });
121
121
  });
122
122
  }
123
- export async function processWithdrawalRequests({ casinoId, graphqlClient, }) {
124
- const pendingRequests = await superuserPool.query({
123
+ export async function processWithdrawalRequests({ casinoId, graphqlClient, pool, }) {
124
+ const pendingRequests = await pool.query({
125
125
  text: `
126
126
  SELECT wr.*
127
127
  FROM hub.withdrawal_request wr
@@ -136,6 +136,7 @@ export async function processWithdrawalRequests({ casinoId, graphqlClient, }) {
136
136
  await processWithdrawalRequest({
137
137
  requestId: request.id,
138
138
  graphqlClient,
139
+ pool,
139
140
  });
140
141
  }
141
142
  catch (error) {
@@ -1,5 +1,6 @@
1
1
  import "graphile-config";
2
2
  import "postgraphile";
3
+ import { ServerContext } from "../context.js";
3
4
  export type UserSessionContext = {
4
5
  user_id: string;
5
6
  mp_user_id: string;
@@ -15,9 +16,10 @@ export type PluginIdentity = {
15
16
  };
16
17
  export declare const requiredPlugins: readonly GraphileConfig.Plugin[];
17
18
  export declare const defaultPlugins: readonly GraphileConfig.Plugin[];
18
- export declare function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, }: {
19
+ export declare function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, context, }: {
19
20
  plugins: readonly GraphileConfig.Plugin[];
20
21
  exportSchemaSDLPath?: string;
21
22
  extraPgSchemas: string[];
22
23
  abortSignal: AbortSignal;
24
+ context: ServerContext;
23
25
  }): GraphileConfig.Preset;
@@ -48,7 +48,7 @@ export const defaultPlugins = [
48
48
  HubClaimFaucetPlugin,
49
49
  customPgOmitArchivedPlugin("deleted"),
50
50
  ];
51
- export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, }) {
51
+ export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, context, }) {
52
52
  if (exportSchemaSDLPath) {
53
53
  if (!exportSchemaSDLPath.startsWith("/")) {
54
54
  throw new Error("exportSchemaSDLPath must be an absolute path");
@@ -96,7 +96,7 @@ export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abo
96
96
  grafast: {
97
97
  context(ctx, _args) {
98
98
  if (ctx.ws) {
99
- return handleWebsocketContext(ctx.ws);
99
+ return handleWebsocketContext(context, ctx.ws);
100
100
  }
101
101
  const expressReq = ctx.expressv4.req;
102
102
  const reqIdentity = expressReq.identity;
@@ -132,6 +132,7 @@ export function createPreset({ plugins, exportSchemaSDLPath, extraPgSchemas, abo
132
132
  pgSettings,
133
133
  identity: pluginIdentity,
134
134
  abortSignal,
135
+ superuserPool: context.superuserPool,
135
136
  };
136
137
  },
137
138
  },
@@ -147,12 +148,12 @@ function getSessionIdFromWebsocketCtx(ws) {
147
148
  const uuid = value.slice("session:".length);
148
149
  return isUuid(uuid) ? uuid : "";
149
150
  }
150
- async function handleWebsocketContext(ws) {
151
+ async function handleWebsocketContext(context, ws) {
151
152
  const sessionId = getSessionIdFromWebsocketCtx(ws);
152
153
  if (!sessionId) {
153
154
  throw new Error("Unauthorized");
154
155
  }
155
- const result = await db.userFromActiveSessionKey(db.superuserPool, sessionId);
156
+ const result = await db.userFromActiveSessionKey(context.superuserPool, sessionId);
156
157
  if (!result) {
157
158
  throw new Error("Unauthorized");
158
159
  }
@@ -1,8 +1,10 @@
1
1
  import { ServerOptions } from "../index.js";
2
+ import { ServerContext } from "../context.js";
2
3
  export type HubServer = {
3
4
  listen: () => Promise<void>;
4
5
  shutdown: () => Promise<void>;
5
6
  };
6
- export declare function createHubServer({ configureApp, plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, }: Pick<ServerOptions, "plugins" | "exportSchemaSDLPath" | "extraPgSchemas" | "configureApp"> & {
7
+ export declare function createHubServer({ configureApp, plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, context, }: Pick<ServerOptions, "plugins" | "exportSchemaSDLPath" | "extraPgSchemas" | "configureApp"> & {
7
8
  abortSignal: AbortSignal;
9
+ context: ServerContext;
8
10
  }): HubServer;
@@ -31,11 +31,11 @@ const dashboardDir = path.join(distDir, "dashboard");
31
31
  if (!fs.existsSync(dashboardDir)) {
32
32
  throw new Error(`Could not find dashboard directory. Expected it to be at "${dashboardDir}"`);
33
33
  }
34
- function createExpressServer() {
34
+ function createExpressServer(context) {
35
35
  const app = express();
36
36
  app.disable("x-powered-by");
37
37
  app.use(cors());
38
- app.use(authentication());
38
+ app.use(authentication(context));
39
39
  app.use("/dashboard", express.static(dashboardDir));
40
40
  app.use("/dashboard/*splat", (_, res) => {
41
41
  res.sendFile(path.join(dashboardDir, "index.html"));
@@ -45,13 +45,14 @@ function createExpressServer() {
45
45
  });
46
46
  return app;
47
47
  }
48
- export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, }) {
49
- const expressServer = createExpressServer();
48
+ export function createHubServer({ configureApp, plugins, exportSchemaSDLPath, extraPgSchemas, abortSignal, context, }) {
49
+ const expressServer = createExpressServer(context);
50
50
  const preset = createPreset({
51
51
  plugins: plugins ?? defaultPlugins,
52
52
  exportSchemaSDLPath,
53
53
  extraPgSchemas: extraPgSchemas ?? [],
54
54
  abortSignal,
55
+ context,
55
56
  });
56
57
  const pgl = postgraphile.default(preset);
57
58
  const serv = pgl.createServ(grafserv);
@@ -1,4 +1,5 @@
1
1
  import { Response, NextFunction } from "express";
2
2
  import { HubRequest } from "../../express.js";
3
- declare const authentication: () => (req: HubRequest, _: Response, next: NextFunction) => Promise<void>;
3
+ import { ServerContext } from "../../context.js";
4
+ declare const authentication: (context: ServerContext) => (req: HubRequest, _: Response, next: NextFunction) => Promise<void>;
4
5
  export default authentication;
@@ -1,7 +1,7 @@
1
1
  import { isUuid } from "../../util.js";
2
2
  import * as db from "../../db/index.js";
3
- async function checkAndUpdateApiKey(key) {
4
- const row = await db.superuserPool
3
+ async function checkAndUpdateApiKey(key, pool) {
4
+ const row = await pool
5
5
  .query(`
6
6
  UPDATE hub.api_key
7
7
  SET last_used_at = now()
@@ -11,7 +11,7 @@ async function checkAndUpdateApiKey(key) {
11
11
  .then(db.maybeOneRow);
12
12
  return !!row;
13
13
  }
14
- const authentication = () => {
14
+ const authentication = (context) => {
15
15
  return async (req, _, next) => {
16
16
  const header = req.get("authorization");
17
17
  if (!header) {
@@ -28,7 +28,7 @@ const authentication = () => {
28
28
  }
29
29
  if (tokenType === "session") {
30
30
  const sessionKey = token;
31
- const result = await db.userFromActiveSessionKey(db.superuserPool, sessionKey);
31
+ const result = await db.userFromActiveSessionKey(context.superuserPool, sessionKey);
32
32
  if (result) {
33
33
  req.identity = {
34
34
  kind: "user",
@@ -40,7 +40,7 @@ const authentication = () => {
40
40
  }
41
41
  else if (tokenType === "apikey") {
42
42
  const apiKey = token;
43
- const isValid = await checkAndUpdateApiKey(apiKey);
43
+ const isValid = await checkAndUpdateApiKey(apiKey, context.superuserPool);
44
44
  if (isValid) {
45
45
  req.identity = {
46
46
  kind: "operator",
@@ -3,11 +3,12 @@ import { MpTakeRequestFieldsFragment } from "../__generated__/graphql.js";
3
3
  import * as pg from "pg";
4
4
  export declare const MP_TAKE_REQUEST_FIELDS: import("@graphql-typed-document-node/core").TypedDocumentNode<MpTakeRequestFieldsFragment, unknown>;
5
5
  export declare function mpGetTakeRequest(graphqlClient: GraphQLClient, id: string): Promise<MpTakeRequestFieldsFragment | null>;
6
- export declare function processTakeRequests({ abortSignal, controllerId, casinoId, graphqlClient, }: {
6
+ export declare function processTakeRequests({ abortSignal, controllerId, casinoId, graphqlClient, pool, }: {
7
7
  abortSignal: AbortSignal;
8
8
  controllerId: string;
9
9
  casinoId: string;
10
10
  graphqlClient: GraphQLClient;
11
+ pool: pg.Pool;
11
12
  }): Promise<void>;
12
13
  export declare function processSingleTakeRequest({ mpTakeRequestId, mpTakeRequest, casinoId, graphqlClient, pool, }: {
13
14
  mpTakeRequestId: string;
@@ -1,6 +1,6 @@
1
1
  import { gql } from "../__generated__/gql.js";
2
2
  import { TakeRequestStatus as MpTakeRequestStatus, TransferStatusKind as MpTransferStatus, } from "../__generated__/graphql.js";
3
- import { exactlyOneRow, maybeOneRow, superuserPool, withPgPoolTransaction, } from "../db/index.js";
3
+ import { exactlyOneRow, maybeOneRow, withPgPoolTransaction, } from "../db/index.js";
4
4
  import { assert } from "tsafe";
5
5
  import { PgAdvisoryLock } from "../pg-advisory-lock.js";
6
6
  import { useFragment } from "../__generated__/fragment-masking.js";
@@ -168,7 +168,7 @@ var LocalTakeRequestStatus;
168
168
  LocalTakeRequestStatus["FAILED"] = "FAILED";
169
169
  LocalTakeRequestStatus["REJECTED"] = "REJECTED";
170
170
  })(LocalTakeRequestStatus || (LocalTakeRequestStatus = {}));
171
- export async function processTakeRequests({ abortSignal, controllerId, casinoId, graphqlClient, }) {
171
+ export async function processTakeRequests({ abortSignal, controllerId, casinoId, graphqlClient, pool, }) {
172
172
  if (abortSignal.aborted) {
173
173
  return;
174
174
  }
@@ -182,15 +182,16 @@ export async function processTakeRequests({ abortSignal, controllerId, casinoId,
182
182
  mpTakeRequest: takeRequest,
183
183
  casinoId,
184
184
  graphqlClient,
185
- pool: superuserPool,
185
+ pool,
186
186
  });
187
187
  }
188
188
  await processPendingTransferCompletions({
189
189
  casinoId,
190
190
  graphqlClient,
191
191
  abortSignal,
192
+ pool,
192
193
  });
193
- await processStuckRequests({ casinoId, graphqlClient, abortSignal });
194
+ await processStuckRequests({ casinoId, graphqlClient, abortSignal, pool });
194
195
  }
195
196
  async function fetchPendingTakeRequests(graphqlClient, controllerId) {
196
197
  const result = await graphqlClient.request(MP_PAGINATE_PENDING_TAKE_REQUESTS, {
@@ -434,11 +435,11 @@ async function attemptTransfer({ pgClient, takeRequestId, mpTakeRequestId, mpExp
434
435
  return null;
435
436
  }
436
437
  }
437
- async function processPendingTransferCompletions({ casinoId, graphqlClient, abortSignal, }) {
438
+ async function processPendingTransferCompletions({ casinoId, graphqlClient, abortSignal, pool, }) {
438
439
  if (abortSignal.aborted) {
439
440
  return;
440
441
  }
441
- const pendingCompletions = await superuserPool
442
+ const pendingCompletions = await pool
442
443
  .query({
443
444
  text: `
444
445
  SELECT id, mp_take_request_id, mp_transfer_id, transfer_completion_attempted_at
@@ -476,7 +477,7 @@ async function processPendingTransferCompletions({ casinoId, graphqlClient, abor
476
477
  mpTransferId: request.mp_transfer_id,
477
478
  graphqlClient,
478
479
  casinoId,
479
- pool: superuserPool,
480
+ pool,
480
481
  });
481
482
  }
482
483
  }
@@ -681,11 +682,11 @@ export async function completeTransfer({ mpTakeRequestId, takeRequestId, mpTrans
681
682
  }
682
683
  });
683
684
  }
684
- async function processStuckRequests({ casinoId, graphqlClient, abortSignal, }) {
685
+ async function processStuckRequests({ casinoId, graphqlClient, abortSignal, pool, }) {
685
686
  if (abortSignal.aborted) {
686
687
  return;
687
688
  }
688
- const stuckRequests = await superuserPool
689
+ const stuckRequests = await pool
689
690
  .query({
690
691
  text: `
691
692
  SELECT id, mp_take_request_id
@@ -710,7 +711,7 @@ async function processStuckRequests({ casinoId, graphqlClient, abortSignal, }) {
710
711
  mpTakeRequestId: request.mp_take_request_id,
711
712
  casinoId,
712
713
  graphqlClient,
713
- pool: superuserPool,
714
+ pool,
714
715
  });
715
716
  }
716
717
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moneypot/hub",
3
- "version": "1.9.0",
3
+ "version": "1.10.0-dev.1",
4
4
  "author": "moneypot.com",
5
5
  "homepage": "https://moneypot.com/hub",
6
6
  "keywords": [