@moneypot/hub 0.0.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 (106) hide show
  1. package/README.md +108 -0
  2. package/dist/cli/add-casino.d.ts +2 -0
  3. package/dist/cli/add-casino.js +116 -0
  4. package/dist/dashboard/assets/index-BtrbrisP.js +360 -0
  5. package/dist/dashboard/assets/index-tK7EUtyc.css +5 -0
  6. package/dist/dashboard/index.html +13 -0
  7. package/dist/src/GraphQLError.d.ts +8 -0
  8. package/dist/src/GraphQLError.js +79 -0
  9. package/dist/src/__generated__/fragment-masking.d.ts +19 -0
  10. package/dist/src/__generated__/fragment-masking.js +16 -0
  11. package/dist/src/__generated__/gql.d.ts +26 -0
  12. package/dist/src/__generated__/gql.js +15 -0
  13. package/dist/src/__generated__/graphql.d.ts +3129 -0
  14. package/dist/src/__generated__/graphql.js +454 -0
  15. package/dist/src/__generated__/index.d.ts +2 -0
  16. package/dist/src/__generated__/index.js +2 -0
  17. package/dist/src/config.d.ts +14 -0
  18. package/dist/src/config.js +57 -0
  19. package/dist/src/db/index.d.ts +89 -0
  20. package/dist/src/db/index.js +339 -0
  21. package/dist/src/db/internal.d.ts +7 -0
  22. package/dist/src/db/internal.js +33 -0
  23. package/dist/src/db/public.d.ts +7 -0
  24. package/dist/src/db/public.js +20 -0
  25. package/dist/src/db/types.d.ts +80 -0
  26. package/dist/src/db/types.js +1 -0
  27. package/dist/src/db/util.d.ts +6 -0
  28. package/dist/src/db/util.js +9 -0
  29. package/dist/src/express.d.ts +13 -0
  30. package/dist/src/express.js +1 -0
  31. package/dist/src/grafast.d.ts +1 -0
  32. package/dist/src/grafast.js +1 -0
  33. package/dist/src/graphile.d.ts +1 -0
  34. package/dist/src/graphile.js +1 -0
  35. package/dist/src/graphql-client.d.ts +6 -0
  36. package/dist/src/graphql-client.js +8 -0
  37. package/dist/src/graphql-queries.d.ts +18 -0
  38. package/dist/src/graphql-queries.js +123 -0
  39. package/dist/src/graphql.d.ts +1 -0
  40. package/dist/src/graphql.js +1 -0
  41. package/dist/src/index.d.ts +15 -0
  42. package/dist/src/index.js +65 -0
  43. package/dist/src/logger.d.ts +9 -0
  44. package/dist/src/logger.js +21 -0
  45. package/dist/src/pg-versions/001-schema.sql +456 -0
  46. package/dist/src/plugins/caas-add-casino.d.ts +1 -0
  47. package/dist/src/plugins/caas-add-casino.js +150 -0
  48. package/dist/src/plugins/caas-authenticate.d.ts +1 -0
  49. package/dist/src/plugins/caas-authenticate.js +175 -0
  50. package/dist/src/plugins/caas-balance-alert.d.ts +1 -0
  51. package/dist/src/plugins/caas-balance-alert.js +43 -0
  52. package/dist/src/plugins/caas-claim-faucet.d.ts +1 -0
  53. package/dist/src/plugins/caas-claim-faucet.js +85 -0
  54. package/dist/src/plugins/caas-current-x.d.ts +1 -0
  55. package/dist/src/plugins/caas-current-x.js +62 -0
  56. package/dist/src/plugins/caas-schema-prefix.d.ts +1 -0
  57. package/dist/src/plugins/caas-schema-prefix.js +25 -0
  58. package/dist/src/plugins/caas-user-balance-by-currency.d.ts +1 -0
  59. package/dist/src/plugins/caas-user-balance-by-currency.js +55 -0
  60. package/dist/src/plugins/caas-withdraw.d.ts +1 -0
  61. package/dist/src/plugins/caas-withdraw.js +133 -0
  62. package/dist/src/plugins/debug.d.ts +1 -0
  63. package/dist/src/plugins/debug.js +14 -0
  64. package/dist/src/plugins/hub-add-casino.d.ts +1 -0
  65. package/dist/src/plugins/hub-add-casino.js +150 -0
  66. package/dist/src/plugins/hub-authenticate.d.ts +1 -0
  67. package/dist/src/plugins/hub-authenticate.js +175 -0
  68. package/dist/src/plugins/hub-balance-alert.d.ts +1 -0
  69. package/dist/src/plugins/hub-balance-alert.js +43 -0
  70. package/dist/src/plugins/hub-claim-faucet.d.ts +1 -0
  71. package/dist/src/plugins/hub-claim-faucet.js +85 -0
  72. package/dist/src/plugins/hub-current-x.d.ts +1 -0
  73. package/dist/src/plugins/hub-current-x.js +62 -0
  74. package/dist/src/plugins/hub-schema-prefix.d.ts +1 -0
  75. package/dist/src/plugins/hub-schema-prefix.js +25 -0
  76. package/dist/src/plugins/hub-user-balance-by-currency.d.ts +1 -0
  77. package/dist/src/plugins/hub-user-balance-by-currency.js +55 -0
  78. package/dist/src/plugins/hub-withdraw.d.ts +1 -0
  79. package/dist/src/plugins/hub-withdraw.js +133 -0
  80. package/dist/src/plugins/id-to-node-id.d.ts +1 -0
  81. package/dist/src/plugins/id-to-node-id.js +31 -0
  82. package/dist/src/plugins/validate-fields.d.ts +1 -0
  83. package/dist/src/plugins/validate-fields.js +61 -0
  84. package/dist/src/process-transfers.d.ts +7 -0
  85. package/dist/src/process-transfers.js +413 -0
  86. package/dist/src/process-withdrawal-request.d.ts +5 -0
  87. package/dist/src/process-withdrawal-request.js +129 -0
  88. package/dist/src/server/graphile.config.d.ts +33 -0
  89. package/dist/src/server/graphile.config.js +166 -0
  90. package/dist/src/server/handle-errors.d.ts +10 -0
  91. package/dist/src/server/handle-errors.js +88 -0
  92. package/dist/src/server/index.d.ts +2 -0
  93. package/dist/src/server/index.js +69 -0
  94. package/dist/src/server/middleware/authentication.d.ts +4 -0
  95. package/dist/src/server/middleware/authentication.js +55 -0
  96. package/dist/src/server/middleware/cors.d.ts +3 -0
  97. package/dist/src/server/middleware/cors.js +14 -0
  98. package/dist/src/services/jwt-service.d.ts +13 -0
  99. package/dist/src/services/jwt-service.js +131 -0
  100. package/dist/src/smart-tags.d.ts +1 -0
  101. package/dist/src/smart-tags.js +55 -0
  102. package/dist/src/util.d.ts +12 -0
  103. package/dist/src/util.js +4 -0
  104. package/dist/src/validate.d.ts +9 -0
  105. package/dist/src/validate.js +91 -0
  106. package/package.json +69 -0
@@ -0,0 +1,123 @@
1
+ import { gql } from "./__generated__/gql.js";
2
+ export const GET_USER_FROM_USER_TOKEN = gql(`
3
+ mutation GetUserFromUserToken($token: UUID!) {
4
+ userFromUserToken(input: { token: $token }) {
5
+ user {
6
+ id
7
+ uname
8
+ }
9
+ experience {
10
+ id
11
+ name
12
+ }
13
+ }
14
+ }
15
+ `);
16
+ export const START_PENDING_EXPERIENCE_TRANSFER = gql(`
17
+ mutation StartPendingExperienceTransfer(
18
+ $mpUserId: UUID!
19
+ $mpExperienceId: UUID!
20
+ $amount: Int!
21
+ $currency: String!
22
+ $metadata: JSON!
23
+ ) {
24
+ transferCurrencyExperienceToUser(
25
+ input: {
26
+ userId: $mpUserId
27
+ experienceId: $mpExperienceId
28
+ amount: $amount
29
+ currency: $currency
30
+ metadata: $metadata
31
+ }
32
+ ) {
33
+ result {
34
+ ... on TransferCurrencyExperienceToUserSuccess {
35
+ __typename
36
+ transfer {
37
+ id
38
+ status
39
+ }
40
+ }
41
+ ... on TransferMetadataIdExists {
42
+ __typename
43
+ transfer {
44
+ id
45
+ status
46
+ }
47
+ }
48
+ }
49
+ }
50
+ }
51
+ `);
52
+ export const PAGINATE_TRANSFERS = gql(`
53
+ query PaginateTransfers(
54
+ $controllerId: UUID!
55
+ $after: Cursor
56
+ $limit: Int = 10
57
+ ) {
58
+ transfersByHolder(
59
+ input: {
60
+ holderId: $controllerId
61
+ after: $after
62
+ first: $limit
63
+ orderBy: ID_ASC
64
+ type: EXPERIENCE
65
+ }
66
+ ) {
67
+ pageInfo {
68
+ endCursor
69
+ hasNextPage
70
+ }
71
+ edges {
72
+ cursor
73
+ node {
74
+ __typename
75
+ id
76
+ amount
77
+ status
78
+ statusAt
79
+ currencyByCurrency {
80
+ id
81
+ displayUnitName
82
+ displayUnitScale
83
+ }
84
+ fromHolderId
85
+ holderByFromHolderId {
86
+ __typename
87
+ id
88
+ ... on User {
89
+ uname
90
+ }
91
+ }
92
+ toHolderId
93
+ holderByToHolderId {
94
+ __typename
95
+ id
96
+ ... on User {
97
+ uname
98
+ }
99
+ }
100
+ ... on ExperienceTransfer {
101
+ id
102
+ experienceId
103
+ experienceByExperienceId {
104
+ id
105
+ name
106
+ }
107
+ }
108
+ }
109
+ }
110
+ }
111
+ }
112
+ `);
113
+ export const GET_CURRENCIES = gql(`
114
+ query GetCurrencies {
115
+ allCurrencies {
116
+ nodes {
117
+ id
118
+ displayUnitName
119
+ displayUnitScale
120
+ }
121
+ }
122
+ }
123
+ `);
@@ -0,0 +1 @@
1
+ export * from "graphql";
@@ -0,0 +1 @@
1
+ export * from "graphql";
@@ -0,0 +1,15 @@
1
+ import { Express } from "express";
2
+ import { Logger } from "./logger.js";
3
+ export { defaultPlugins, type PluginContext, } from "./server/graphile.config.js";
4
+ export type ServerOptions = {
5
+ configureApp?: (app: Express) => void;
6
+ plugins?: readonly GraphileConfig.Plugin[];
7
+ extraPgSchemas?: string[];
8
+ exportSchemaSDLPath: string;
9
+ userDatabaseMigrationsPath?: string;
10
+ logger?: Logger;
11
+ };
12
+ type ListenInfo = {
13
+ port: number;
14
+ };
15
+ export declare function startAndListen(options: ServerOptions): Promise<ListenInfo>;
@@ -0,0 +1,65 @@
1
+ import PgUpgradeSchema, { DatabaseAheadError, } from "@moneypot/pg-upgrade-schema";
2
+ import * as db from "./db/index.js";
3
+ import config from "./config.js";
4
+ import * as server from "./server/index.js";
5
+ import { initializeTransferProcessors } from "./process-transfers.js";
6
+ import { join } from "path";
7
+ import { logger, setLogger } from "./logger.js";
8
+ export { defaultPlugins, } from "./server/graphile.config.js";
9
+ async function initialize(options) {
10
+ if (options.logger) {
11
+ setLogger(options.logger);
12
+ }
13
+ const pgClient = db.getPgClient(config.SUPERUSER_DATABASE_URL);
14
+ await pgClient.connect();
15
+ try {
16
+ await PgUpgradeSchema.default({
17
+ pgClient,
18
+ dirname: join(import.meta.dirname, "pg-versions"),
19
+ schemaName: "hub_core_versions",
20
+ });
21
+ }
22
+ catch (e) {
23
+ logger.error("Error upgrading core schema", e);
24
+ if (e instanceof DatabaseAheadError) {
25
+ logger.error("⚠️".repeat(80));
26
+ logger.error("@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.");
27
+ logger.error("⚠️".repeat(80));
28
+ process.exit(1);
29
+ }
30
+ }
31
+ if (options.userDatabaseMigrationsPath) {
32
+ await PgUpgradeSchema.default({
33
+ pgClient,
34
+ dirname: options.userDatabaseMigrationsPath,
35
+ schemaName: "hub_user_versions",
36
+ });
37
+ }
38
+ await pgClient.end();
39
+ initializeTransferProcessors();
40
+ }
41
+ export async function startAndListen(options) {
42
+ if (options.userDatabaseMigrationsPath &&
43
+ !options.userDatabaseMigrationsPath.startsWith("/")) {
44
+ throw new Error(`userDatabaseMigrationsPath must be an absolute path, got ${options.userDatabaseMigrationsPath}`);
45
+ }
46
+ if (!options.exportSchemaSDLPath.startsWith("/")) {
47
+ throw new Error(`exportSchemaSDLPath must be an absolute path, got ${options.exportSchemaSDLPath}`);
48
+ }
49
+ await initialize({
50
+ userDatabaseMigrationsPath: options.userDatabaseMigrationsPath,
51
+ logger: options.logger,
52
+ });
53
+ return server
54
+ .listen({
55
+ configureApp: options.configureApp,
56
+ plugins: options.plugins,
57
+ exportSchemaSDLPath: options.exportSchemaSDLPath,
58
+ extraPgSchemas: options.extraPgSchemas,
59
+ })
60
+ .then(() => {
61
+ return {
62
+ port: config.PORT,
63
+ };
64
+ });
65
+ }
@@ -0,0 +1,9 @@
1
+ export declare class Logger {
2
+ debug(...args: any[]): void;
3
+ error(...args: any[]): void;
4
+ info(...args: any[]): void;
5
+ trace(...args: any[]): void;
6
+ warn(...args: any[]): void;
7
+ }
8
+ export declare let logger: Logger;
9
+ export declare function setLogger(instance: Logger): void;
@@ -0,0 +1,21 @@
1
+ export class Logger {
2
+ debug(...args) {
3
+ logger.debug(...args);
4
+ }
5
+ error(...args) {
6
+ logger.error(...args);
7
+ }
8
+ info(...args) {
9
+ logger.info(...args);
10
+ }
11
+ trace(...args) {
12
+ logger.trace(...args);
13
+ }
14
+ warn(...args) {
15
+ logger.warn(...args);
16
+ }
17
+ }
18
+ export let logger = console;
19
+ export function setLogger(instance) {
20
+ logger = instance;
21
+ }
@@ -0,0 +1,456 @@
1
+ -- Flattened migrations from 001-015
2
+
3
+ -- Schema Setup
4
+ drop schema if exists public cascade;
5
+ create schema public;
6
+
7
+ DROP SCHEMA IF EXISTS hub CASCADE;
8
+ CREATE SCHEMA hub;
9
+
10
+ DROP SCHEMA IF EXISTS hub_hidden CASCADE;
11
+ CREATE SCHEMA hub_hidden;
12
+
13
+ DROP SCHEMA IF EXISTS hub_secret CASCADE;
14
+ CREATE SCHEMA hub_secret;
15
+
16
+ create extension if not exists pgcrypto;
17
+ create extension if not exists "uuid-ossp";
18
+
19
+
20
+ -- PostgreSQL role setup
21
+ -- We need an app_postgraphile role to exist here so that we can grant it permissions.
22
+ -- The user can either create it themselves before they run the migrations, or they
23
+ -- can update the password after the fact.
24
+ DO $$
25
+ DECLARE
26
+ random_password text := gen_random_uuid()::text;
27
+ BEGIN
28
+ IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'app_postgraphile') THEN
29
+ EXECUTE format('CREATE ROLE app_postgraphile LOGIN PASSWORD %L', random_password);
30
+ END IF;
31
+ END $$;
32
+
33
+ -- TODO: We may want an hub_secret for things that postgraphile user can't even do (e.g. things we only manipulate in security definer functions)
34
+
35
+ -- UUID v7 generation functions
36
+ CREATE OR REPLACE FUNCTION hub_hidden.uuid_generate_v7() RETURNS uuid LANGUAGE plpgsql PARALLEL SAFE AS $$
37
+ DECLARE
38
+ -- The current UNIX timestamp in milliseconds
39
+ unix_time_ms CONSTANT bytea NOT NULL DEFAULT substring(int8send((extract(epoch FROM clock_timestamp()) * 1000)::bigint) from 3);
40
+
41
+ -- The buffer used to create the UUID, starting with the UNIX timestamp and followed by random bytes
42
+ buffer bytea NOT NULL DEFAULT unix_time_ms || gen_random_bytes(10);
43
+ BEGIN
44
+ -- Set most significant 4 bits of 7th byte to 7 (for UUID v7), keeping the last 4 bits unchanged
45
+ buffer = set_byte(buffer, 6, (b'0111' || get_byte(buffer, 6)::bit(4))::bit(8)::int);
46
+
47
+ -- Set most significant 2 bits of 9th byte to 2 (the UUID variant specified in RFC 4122), keeping the last 6 bits unchanged
48
+ buffer = set_byte(buffer, 8, (b'10' || get_byte(buffer, 8)::bit(6))::bit(8)::int);
49
+
50
+ RETURN encode(buffer, 'hex');
51
+ END
52
+ $$;
53
+
54
+ CREATE OR REPLACE FUNCTION extract_timestamp_from_uuid_v7(uuid_v7 UUID) RETURNS TIMESTAMPTZ AS $$
55
+ SELECT to_timestamp(('x'||replace(uuid_v7::text, '-', ''))::bit(48)::bigint / 1000) AS result;
56
+ $$ LANGUAGE sql IMMUTABLE;
57
+
58
+ -- Helper functions for RLS
59
+ create or replace function hub_hidden.is_operator() returns boolean as $$
60
+ select nullif(current_setting('operator.api_key', true), '') is not null;
61
+ $$ language sql stable;
62
+
63
+ create or replace function hub_hidden.current_user_id() returns uuid as $$
64
+ select nullif(current_setting('session.user_id', true), '')::uuid;
65
+ $$ language sql stable;
66
+
67
+ create or replace function hub_hidden.current_casino_id() returns uuid as $$
68
+ select nullif(current_setting('session.casino_id', true), '')::uuid;
69
+ $$ language sql stable;
70
+
71
+ create or replace function hub_hidden.current_experience_id() returns uuid as $$
72
+ select nullif(current_setting('session.experience_id', true), '')::uuid;
73
+ $$ language sql stable;
74
+
75
+ create or replace function hub_hidden.current_session_id() returns uuid as $$
76
+ select nullif(current_setting('session.id', true), '')::uuid;
77
+ $$ language sql stable;
78
+
79
+ -- Enums
80
+ create type hub.transfer_status_kind as enum ('PENDING', 'COMPLETED', 'CANCELED', 'UNCLAIMED', 'EXPIRED');
81
+
82
+ -- Tables
83
+ CREATE TABLE hub.casino(
84
+ id uuid PRIMARY KEY DEFAULT hub_hidden.uuid_generate_v7(),
85
+ base_url TEXT NOT NULL, -- todo: this should probably be an array..
86
+ name TEXT NOT NULL,
87
+ graphql_url TEXT NOT NULL
88
+ );
89
+
90
+ create unique index casino_base_url_key on hub.casino(base_url);
91
+ create unique index casino_graphql_url_key on hub.casino(graphql_url);
92
+
93
+ create table hub.casino_secret(
94
+ id uuid primary key references hub.casino(id),
95
+ controller_id uuid not null, -- our controller id in the casino's system
96
+ api_key uuid not null
97
+ );
98
+
99
+ create table hub.jwk_set (
100
+ casino_id uuid primary key references hub.casino(id),
101
+ jwks jsonb not null,
102
+ updated_at timestamptz not null default now()
103
+ );
104
+
105
+ -- For audit/debugging
106
+ create table hub.jwk_set_snapshot (
107
+ id uuid primary key default hub_hidden.uuid_generate_v7(),
108
+ casino_id uuid not null references hub.casino(id),
109
+ jwks jsonb not null
110
+ );
111
+
112
+ create index jwks_snapshot_casino_id_idx on hub.jwk_set_snapshot(casino_id);
113
+
114
+ create table hub.api_key (
115
+ id uuid primary key default hub_hidden.uuid_generate_v7(),
116
+ key uuid unique not null default gen_random_uuid(), -- uuid v4
117
+ last_used_at timestamptz null,
118
+ revoked_at timestamptz null
119
+ );
120
+
121
+ -- This will get populated from each casino graphql API.
122
+ create table hub.currency(
123
+ key text not null,
124
+ casino_id uuid not null references hub.casino(id),
125
+ display_unit_name text not null,
126
+ display_unit_scale int not null,
127
+ primary key (key, casino_id)
128
+ );
129
+
130
+ create index currency_casino_id_idx on hub.currency(casino_id);
131
+
132
+ create table hub.user (
133
+ id uuid NOT NULL PRIMARY KEY DEFAULT hub_hidden.uuid_generate_v7(),
134
+ casino_id uuid NOT NULL references hub.casino(id),
135
+ mp_user_id uuid NOT NULL,
136
+ uname text NOT NULL -- from the casino
137
+ );
138
+
139
+ -- A user can only represent one mp_user_id (Moneypot record) per MP casino.
140
+ create unique index user_casino_id_mp_user_id_key on hub.user(casino_id, mp_user_id);
141
+
142
+ create table hub_hidden.transfer_cursor (
143
+ casino_id uuid primary key references hub.casino(id),
144
+ cursor text not null
145
+ );
146
+
147
+ create table hub.experience (
148
+ id uuid NOT NULL PRIMARY KEY DEFAULT hub_hidden.uuid_generate_v7(),
149
+ casino_id uuid NOT NULL references hub.casino(id),
150
+ mp_experience_id uuid NOT NULL, -- graphql id of experience in MP casino
151
+ name text NOT NULL
152
+ );
153
+ -- An experience can only represent one mp_experience_id (Moneypot record) per MP casino.
154
+ CREATE UNIQUE INDEX experience_casino_id_mp_experience_id_key ON hub.experience(casino_id, mp_experience_id);
155
+ CREATE INDEX experience_mp_experience_id_idx ON hub.experience(mp_experience_id);
156
+
157
+ create table hub.bankroll (
158
+ id uuid NOT NULL PRIMARY KEY DEFAULT hub_hidden.uuid_generate_v7(),
159
+ casino_id uuid NOT NULL references hub.casino(id),
160
+ currency_key text NOT NULL,
161
+ amount float8 not null default 0,
162
+ bets bigint not null default 0,
163
+ wagered float8 not null default 0,
164
+ expected_value float8 not null default 0,
165
+ constraint amount_non_negative check (amount >= 0),
166
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
167
+ );
168
+
169
+ CREATE UNIQUE INDEX bankroll_casino_id_currency_key_key ON hub.bankroll(casino_id, currency_key);
170
+ CREATE INDEX bankroll_casino_id_idx ON hub.bankroll(casino_id);
171
+
172
+ create table hub.session (
173
+ id uuid primary key default hub_hidden.uuid_generate_v7(), --
174
+ casino_id uuid NOT NULL references hub.casino(id),
175
+ user_id uuid not null references hub.user(id), -- graphql id of MP user
176
+ experience_id uuid not null references hub.experience(id), -- graphql id of MP experience
177
+ user_token uuid not null, -- uuid from MP user_token
178
+ expired_at timestamptz not null DEFAULT now() + interval '1 year',
179
+ key UUID NOT NULL DEFAULT gen_random_uuid()
180
+ );
181
+
182
+ CREATE INDEX session_casino_id_idx ON hub.session(casino_id);
183
+ CREATE INDEX session_user_id_idx ON hub.session(user_id);
184
+ CREATE INDEX session_experience_id_idx ON hub.session(experience_id);
185
+ CREATE UNIQUE INDEX session_user_token_idx ON hub.session(user_token);
186
+ CREATE UNIQUE INDEX session_key_idx ON hub.session(key);
187
+
188
+ -- MP transfers turn into deposits and withdraws in hub
189
+ create table hub.deposit (
190
+ id uuid primary key default hub_hidden.uuid_generate_v7(),
191
+ casino_id uuid not null references hub.casino(id),
192
+ mp_transfer_id text not null, -- graphql id for (external) MP casino transfer
193
+ user_id uuid not null references hub.user(id),
194
+ experience_id uuid not null references hub.experience(id),
195
+ amount float8 not null check (amount > 0),
196
+ currency_key text not null,
197
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
198
+ );
199
+
200
+ create unique index deposit_casino_id_mp_transfer_id_key on hub.deposit(casino_id, mp_transfer_id);
201
+ CREATE INDEX deposit_user_id_idx ON hub.deposit(user_id);
202
+ CREATE INDEX deposit_experience_id_idx ON hub.deposit(experience_id);
203
+ CREATE INDEX deposit_casino_id_idx ON hub.deposit(casino_id);
204
+
205
+ create table hub.withdrawal_request (
206
+ id uuid not null primary key default hub_hidden.uuid_generate_v7(),
207
+ -- Save all the things we need to make the MP transferCurrencyExperienceToUser request
208
+ casino_id uuid not null references hub.casino(id),
209
+ experience_id uuid not null references hub.experience(id),
210
+ user_id uuid not null references hub.user(id),
211
+ amount float not null check (amount > 0),
212
+ currency_key text not null,
213
+
214
+ -- This is set once we can confirm the transfer was created in MP
215
+ -- while submitting metadata={id: withdrawal_request.id}
216
+ mp_transfer_id text null,
217
+
218
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
219
+ );
220
+
221
+ CREATE INDEX withdrawal_request_user_id_idx ON hub.withdrawal_request(user_id);
222
+ CREATE INDEX withdrawal_request_experience_id_idx ON hub.withdrawal_request(experience_id);
223
+ CREATE INDEX withdrawal_request_casino_id_idx ON hub.withdrawal_request(casino_id);
224
+
225
+ CREATE UNIQUE INDEX withdrawal_request_casino_id_mp_transfer_id_key ON hub.withdrawal_request(casino_id, mp_transfer_id)
226
+ WHERE mp_transfer_id IS NOT NULL;
227
+
228
+ create table hub.withdrawal (
229
+ id uuid primary key default hub_hidden.uuid_generate_v7(),
230
+ casino_id uuid not null references hub.casino(id),
231
+ mp_transfer_id text not null, -- Gets set once we confirm that MP casino has the transfer
232
+ user_id uuid not null references hub.user(id),
233
+ experience_id uuid not null references hub.experience(id),
234
+ amount float8 not null check (amount > 0),
235
+ currency_key text not null,
236
+ status hub.transfer_status_kind not null,
237
+ status_at timestamptz not null default now(),
238
+ withdrawal_request_id uuid not null references hub.withdrawal_request(id),
239
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
240
+ );
241
+
242
+ create unique index withdrawal_casino_id_mp_transfer_id_key on hub.withdrawal(casino_id, mp_transfer_id);
243
+ CREATE INDEX withdrawal_user_id_idx ON hub.withdrawal(user_id);
244
+ CREATE INDEX withdrawal_experience_id_idx ON hub.withdrawal(experience_id);
245
+ CREATE UNIQUE INDEX withdrawal_withdrawal_request_id_key ON hub.withdrawal(withdrawal_request_id);
246
+ CREATE INDEX withdrawal_casino_id_idx ON hub.withdrawal(casino_id);
247
+ CREATE INDEX withdrawal_withdrawal_request_id_idx ON hub.withdrawal(withdrawal_request_id);
248
+
249
+ create table hub.faucet_claim (
250
+ id uuid primary key default hub_hidden.uuid_generate_v7(),
251
+ user_id uuid not null references hub.user(id),
252
+ casino_id uuid not null references hub.casino(id),
253
+ experience_id uuid not null references hub.experience(id),
254
+ currency_key text not null,
255
+ amount float8 not null check (amount > 0),
256
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
257
+ );
258
+
259
+ CREATE INDEX faucet_claim_user_id_idx ON hub.faucet_claim(user_id);
260
+ CREATE INDEX faucet_claim_experience_id_idx ON hub.faucet_claim(experience_id);
261
+ CREATE INDEX faucet_claim_casino_id_idx ON hub.faucet_claim(casino_id);
262
+ CREATE INDEX faucet_claim_currency_key_idx ON hub.faucet_claim(currency_key);
263
+
264
+ -- User balances derived from transfers per experience
265
+ create table hub.balance (
266
+ casino_id uuid not null references hub.casino(id),
267
+ user_id uuid not null references hub.user(id),
268
+ experience_id uuid not null references hub.experience(id),
269
+ currency_key text not null,
270
+ amount float8 not null default 0,
271
+ primary key (casino_id, user_id, experience_id, currency_key),
272
+ constraint amount_non_negative check (amount >= 0),
273
+ foreign key (currency_key, casino_id) references hub.currency(key, casino_id)
274
+ );
275
+
276
+ CREATE INDEX balance_user_id_idx ON hub.balance(user_id);
277
+ CREATE INDEX balance_experience_id_idx ON hub.balance(experience_id);
278
+ CREATE INDEX balance_casino_id_idx ON hub.balance(casino_id);
279
+
280
+ -- Views
281
+ create view hub.active_session as
282
+ select * from hub.session where expired_at > now();
283
+
284
+ -- Triggers
285
+ -- Every time a new casino is inserted, a notification is sent to the channel 'new_casino'
286
+ CREATE OR REPLACE FUNCTION notify_new_casino()
287
+ RETURNS TRIGGER AS $$
288
+ BEGIN
289
+ PERFORM pg_notify('hub:new_casino', json_build_object(
290
+ 'id', NEW.id
291
+ )::text);
292
+ RETURN NEW;
293
+ END;
294
+ $$ LANGUAGE plpgsql;
295
+
296
+ CREATE TRIGGER new_casino_trigger
297
+ AFTER INSERT ON hub.casino
298
+ FOR EACH ROW
299
+ EXECUTE FUNCTION notify_new_casino();
300
+
301
+ CREATE OR REPLACE FUNCTION hub.notify_balance_change() RETURNS TRIGGER AS $$
302
+ BEGIN
303
+ -- For INSERT operations, always notify
304
+ IF TG_OP = 'INSERT' THEN
305
+ PERFORM pg_notify(
306
+ 'hub:user:' || NEW.user_id || ':balance_alert',
307
+ json_build_object('currency_key', NEW.currency_key)::text
308
+ );
309
+ -- For UPDATE operations, only notify if amount changed
310
+ ELSIF TG_OP = 'UPDATE' AND NEW.amount IS DISTINCT FROM OLD.amount THEN
311
+ PERFORM pg_notify(
312
+ 'hub:user:' || NEW.user_id || ':balance_alert',
313
+ json_build_object('currency_key', NEW.currency_key)::text
314
+ );
315
+ END IF;
316
+
317
+ -- trigger wants us to return new row
318
+ RETURN NEW;
319
+ END;
320
+ $$ LANGUAGE plpgsql;
321
+
322
+ CREATE TRIGGER balance_change_alert
323
+ AFTER INSERT OR UPDATE OF amount ON hub.balance
324
+ FOR EACH ROW
325
+ EXECUTE FUNCTION hub.notify_balance_change();
326
+
327
+ -- Grants
328
+ grant usage on schema public to app_postgraphile;
329
+ grant usage on schema hub to app_postgraphile;
330
+ -- hub_hidden utils like hub_hidden.current_user_id() should be accessible to app_postgraphile
331
+ grant usage on schema hub_hidden to app_postgraphile;
332
+
333
+ grant select on table hub.currency to app_postgraphile;
334
+ grant select on table hub.user to app_postgraphile;
335
+ grant select on table hub.experience to app_postgraphile;
336
+ grant select on table hub.bankroll to app_postgraphile;
337
+ grant select on table hub.session to app_postgraphile;
338
+ grant select on table hub.deposit to app_postgraphile;
339
+ grant select on table hub.withdrawal to app_postgraphile;
340
+ grant select on table hub.balance to app_postgraphile;
341
+ grant select on table hub.faucet_claim to app_postgraphile;
342
+ grant select on table hub.casino to app_postgraphile;
343
+ grant select on table hub.active_session to app_postgraphile;
344
+ grant select on table hub.casino_secret to app_postgraphile;
345
+ grant select on table hub.api_key to app_postgraphile;
346
+ grant select on table hub.jwk_set to app_postgraphile;
347
+ grant select on table hub.jwk_set_snapshot to app_postgraphile;
348
+ grant select on table hub.withdrawal_request to app_postgraphile;
349
+
350
+ grant update on hub.casino to app_postgraphile;
351
+ grant update on table hub.bankroll to app_postgraphile;
352
+
353
+ -- Row Level Security
354
+ alter table hub.casino enable row level security;
355
+ alter table hub.casino_secret enable row level security;
356
+ alter table hub.user enable row level security;
357
+ alter table hub.bankroll enable row level security;
358
+ alter table hub.experience enable row level security;
359
+ alter table hub.session enable row level security;
360
+ alter table hub.deposit enable row level security;
361
+ alter table hub.withdrawal enable row level security;
362
+ alter table hub.balance enable row level security;
363
+ alter table hub.api_key enable row level security;
364
+ alter table hub.currency enable row level security;
365
+ alter table hub.jwk_set enable row level security;
366
+ alter table hub.jwk_set_snapshot enable row level security;
367
+ alter table hub.withdrawal_request enable row level security;
368
+ alter table hub.faucet_claim enable row level security;
369
+
370
+ -- RLS Policies
371
+
372
+ -- PUBLIC POLICIES
373
+ create policy select_casino on hub.casino for select using (true);
374
+ create policy select_currency on hub.currency for select using (true);
375
+ create policy select_bankroll on hub.bankroll for select using (true);
376
+
377
+ -- OPERATOR-ONLY POLICIES
378
+ create policy select_experience on hub.experience for select using (
379
+ hub_hidden.is_operator()
380
+ );
381
+
382
+ create policy select_casino_secret on hub.casino_secret for select using (
383
+ hub_hidden.is_operator()
384
+ );
385
+
386
+ create policy select_api_key on hub.api_key for select using (
387
+ hub_hidden.is_operator()
388
+ );
389
+
390
+ create policy update_casino on hub.casino for update using (
391
+ hub_hidden.is_operator()
392
+ );
393
+
394
+ create policy select_jwks on hub.jwk_set for select using (
395
+ hub_hidden.is_operator()
396
+ );
397
+
398
+ create policy select_jwks_snapshot on hub.jwk_set_snapshot for select using (
399
+ hub_hidden.is_operator()
400
+ );
401
+
402
+ create policy update_bankroll on hub.bankroll for update using (
403
+ hub_hidden.is_operator()
404
+ );
405
+
406
+ -- MIXED-USE POLICIES
407
+ -- NOTE: Since hub.user.id is globally-unique in the hub database, it's a
408
+ -- sufficient authorization check to just use `user_id = hub_hidden.current_user_id()`.
409
+ create policy select_user on hub.user for select using (
410
+ hub_hidden.is_operator() or (
411
+ -- Users can only see their own records
412
+ id = hub_hidden.current_user_id()
413
+ )
414
+ );
415
+
416
+ create policy select_balance on hub.balance for select using (
417
+ hub_hidden.is_operator() OR (
418
+ -- Users can only see their own records
419
+ user_id = hub_hidden.current_user_id()
420
+ )
421
+ );
422
+
423
+ create policy select_deposit on hub.deposit for select using (
424
+ hub_hidden.is_operator() OR (
425
+ -- Users can only see their own records
426
+ user_id = hub_hidden.current_user_id()
427
+ )
428
+ );
429
+
430
+ create policy select_withdrawal on hub.withdrawal for select using (
431
+ hub_hidden.is_operator() OR (
432
+ -- Users can only see their own records
433
+ user_id = hub_hidden.current_user_id()
434
+ )
435
+ );
436
+
437
+ create policy select_session on hub.session for select using (
438
+ hub_hidden.is_operator() OR (
439
+ -- Users can only see their own records
440
+ user_id = hub_hidden.current_user_id()
441
+ )
442
+ );
443
+
444
+ create policy select_withdrawal_request on hub.withdrawal_request for select using (
445
+ hub_hidden.is_operator() OR (
446
+ -- Users can only see their own records
447
+ user_id = hub_hidden.current_user_id()
448
+ )
449
+ );
450
+
451
+ create policy select_faucet_claim on hub.faucet_claim for select using (
452
+ hub_hidden.is_operator() OR (
453
+ -- Users can only see their own records
454
+ user_id = hub_hidden.current_user_id()
455
+ )
456
+ );
@@ -0,0 +1 @@
1
+ export declare const CaasAddCasinoPlugin: GraphileConfig.Plugin;