@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.
- package/README.md +108 -0
- package/dist/cli/add-casino.d.ts +2 -0
- package/dist/cli/add-casino.js +116 -0
- package/dist/dashboard/assets/index-BtrbrisP.js +360 -0
- package/dist/dashboard/assets/index-tK7EUtyc.css +5 -0
- package/dist/dashboard/index.html +13 -0
- package/dist/src/GraphQLError.d.ts +8 -0
- package/dist/src/GraphQLError.js +79 -0
- package/dist/src/__generated__/fragment-masking.d.ts +19 -0
- package/dist/src/__generated__/fragment-masking.js +16 -0
- package/dist/src/__generated__/gql.d.ts +26 -0
- package/dist/src/__generated__/gql.js +15 -0
- package/dist/src/__generated__/graphql.d.ts +3129 -0
- package/dist/src/__generated__/graphql.js +454 -0
- package/dist/src/__generated__/index.d.ts +2 -0
- package/dist/src/__generated__/index.js +2 -0
- package/dist/src/config.d.ts +14 -0
- package/dist/src/config.js +57 -0
- package/dist/src/db/index.d.ts +89 -0
- package/dist/src/db/index.js +339 -0
- package/dist/src/db/internal.d.ts +7 -0
- package/dist/src/db/internal.js +33 -0
- package/dist/src/db/public.d.ts +7 -0
- package/dist/src/db/public.js +20 -0
- package/dist/src/db/types.d.ts +80 -0
- package/dist/src/db/types.js +1 -0
- package/dist/src/db/util.d.ts +6 -0
- package/dist/src/db/util.js +9 -0
- package/dist/src/express.d.ts +13 -0
- package/dist/src/express.js +1 -0
- package/dist/src/grafast.d.ts +1 -0
- package/dist/src/grafast.js +1 -0
- package/dist/src/graphile.d.ts +1 -0
- package/dist/src/graphile.js +1 -0
- package/dist/src/graphql-client.d.ts +6 -0
- package/dist/src/graphql-client.js +8 -0
- package/dist/src/graphql-queries.d.ts +18 -0
- package/dist/src/graphql-queries.js +123 -0
- package/dist/src/graphql.d.ts +1 -0
- package/dist/src/graphql.js +1 -0
- package/dist/src/index.d.ts +15 -0
- package/dist/src/index.js +65 -0
- package/dist/src/logger.d.ts +9 -0
- package/dist/src/logger.js +21 -0
- package/dist/src/pg-versions/001-schema.sql +456 -0
- package/dist/src/plugins/caas-add-casino.d.ts +1 -0
- package/dist/src/plugins/caas-add-casino.js +150 -0
- package/dist/src/plugins/caas-authenticate.d.ts +1 -0
- package/dist/src/plugins/caas-authenticate.js +175 -0
- package/dist/src/plugins/caas-balance-alert.d.ts +1 -0
- package/dist/src/plugins/caas-balance-alert.js +43 -0
- package/dist/src/plugins/caas-claim-faucet.d.ts +1 -0
- package/dist/src/plugins/caas-claim-faucet.js +85 -0
- package/dist/src/plugins/caas-current-x.d.ts +1 -0
- package/dist/src/plugins/caas-current-x.js +62 -0
- package/dist/src/plugins/caas-schema-prefix.d.ts +1 -0
- package/dist/src/plugins/caas-schema-prefix.js +25 -0
- package/dist/src/plugins/caas-user-balance-by-currency.d.ts +1 -0
- package/dist/src/plugins/caas-user-balance-by-currency.js +55 -0
- package/dist/src/plugins/caas-withdraw.d.ts +1 -0
- package/dist/src/plugins/caas-withdraw.js +133 -0
- package/dist/src/plugins/debug.d.ts +1 -0
- package/dist/src/plugins/debug.js +14 -0
- package/dist/src/plugins/hub-add-casino.d.ts +1 -0
- package/dist/src/plugins/hub-add-casino.js +150 -0
- package/dist/src/plugins/hub-authenticate.d.ts +1 -0
- package/dist/src/plugins/hub-authenticate.js +175 -0
- package/dist/src/plugins/hub-balance-alert.d.ts +1 -0
- package/dist/src/plugins/hub-balance-alert.js +43 -0
- package/dist/src/plugins/hub-claim-faucet.d.ts +1 -0
- package/dist/src/plugins/hub-claim-faucet.js +85 -0
- package/dist/src/plugins/hub-current-x.d.ts +1 -0
- package/dist/src/plugins/hub-current-x.js +62 -0
- package/dist/src/plugins/hub-schema-prefix.d.ts +1 -0
- package/dist/src/plugins/hub-schema-prefix.js +25 -0
- package/dist/src/plugins/hub-user-balance-by-currency.d.ts +1 -0
- package/dist/src/plugins/hub-user-balance-by-currency.js +55 -0
- package/dist/src/plugins/hub-withdraw.d.ts +1 -0
- package/dist/src/plugins/hub-withdraw.js +133 -0
- package/dist/src/plugins/id-to-node-id.d.ts +1 -0
- package/dist/src/plugins/id-to-node-id.js +31 -0
- package/dist/src/plugins/validate-fields.d.ts +1 -0
- package/dist/src/plugins/validate-fields.js +61 -0
- package/dist/src/process-transfers.d.ts +7 -0
- package/dist/src/process-transfers.js +413 -0
- package/dist/src/process-withdrawal-request.d.ts +5 -0
- package/dist/src/process-withdrawal-request.js +129 -0
- package/dist/src/server/graphile.config.d.ts +33 -0
- package/dist/src/server/graphile.config.js +166 -0
- package/dist/src/server/handle-errors.d.ts +10 -0
- package/dist/src/server/handle-errors.js +88 -0
- package/dist/src/server/index.d.ts +2 -0
- package/dist/src/server/index.js +69 -0
- package/dist/src/server/middleware/authentication.d.ts +4 -0
- package/dist/src/server/middleware/authentication.js +55 -0
- package/dist/src/server/middleware/cors.d.ts +3 -0
- package/dist/src/server/middleware/cors.js +14 -0
- package/dist/src/services/jwt-service.d.ts +13 -0
- package/dist/src/services/jwt-service.js +131 -0
- package/dist/src/smart-tags.d.ts +1 -0
- package/dist/src/smart-tags.js +55 -0
- package/dist/src/util.d.ts +12 -0
- package/dist/src/util.js +4 -0
- package/dist/src/validate.d.ts +9 -0
- package/dist/src/validate.js +91 -0
- 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;
|