@moneypot/hub 0.0.1 → 0.0.2

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 CHANGED
@@ -44,65 +44,3 @@ startAndListen(options)
44
44
  })
45
45
  .catch(console.error);
46
46
  ```
47
-
48
- ## Development
49
-
50
- To work on this library , there's a mini project in the `./demo` directory that uses `@moneypot/hub` as a lib.
51
-
52
- ```sh
53
- createdb hub_demo
54
- ```
55
-
56
- Ensure this role exists:
57
-
58
- ```sql
59
- CREATE ROLE app_postgraphile LOGIN PASSWORD 'pass';
60
- ```
61
-
62
- Create `.env`:
63
-
64
- ```ini
65
- DATABASE_URL="postgres://app_postgraphile:pass@localhost/hub_demo"
66
- SUPERUSER_DATABASE_URL="postgres://app_superuser:pass@localhost/hub_demo"
67
- GRAPHILE_ENV=development
68
- NODE_ENV=development
69
- ```
70
-
71
- Run the demo:
72
-
73
- ```sh
74
- cd demo
75
- npm install
76
- npm run dev
77
- ```
78
-
79
- The hub-demo server should be running at <http://localhost:8888/graphql>.
80
-
81
- ### Dashboard development
82
-
83
- To work on the `./dashboard` react app, ensure that hub-demo is running.
84
-
85
- Terminal 1: Run demo controller inside this repo's `demo` dir:
86
-
87
- ```sh
88
- cd demo
89
- npm install
90
- npm run dev
91
- # Listening on :8888
92
- ```
93
-
94
- Terminal 2: Run dashboard project pointed at demo controller
95
-
96
- ```
97
- cd dashboard
98
- npm install
99
- VITE_GRAPHQL_URL=http://localhost:8888/graphql npm run dev
100
- ```
101
-
102
- Now visit the localhost url that it prints out.
103
-
104
- (The `VITE_GRAPHQL_URL` override is necessary since the dashboard defaults to `window.location.origin + "/graphql"` as its api endpoint since it usually runs bundled with the hub server.)
105
-
106
- ## Change log
107
-
108
- - 1.0.0: Initial release.
@@ -176,7 +176,7 @@ attempted value: ${c}
176
176
  }
177
177
  }
178
178
  }
179
- `),Tz={name:"moneypot.dev",baseUrl:"https://moneypot.dev",graphqlUrl:"https://api.moneypot.dev/graphql",apiKey:""},wz=wo().shape({name:ld().required(),baseUrl:E1().required(),graphqlUrl:b1().required(),apiKey:_z().required()}),Az=zt(({show:e,onHide:t})=>{const r=sn(),[i]=Hx(r,{document:Oz}),u=id({initialValues:Tz,validationSchema:wz,onSubmit:async(c,{setSubmitting:h,setStatus:d})=>(d(null),h(!0),i({variables:{input:{name:c.name,baseUrl:c.baseUrl,graphqlUrl:c.graphqlUrl,apiKey:c.apiKey}}}).then(m=>{var E;((E=m.hubAddCasino)==null?void 0:E.casino)&&(r.refetchCasinos(),t())}).catch(m=>{d(Bn(m))}).finally(()=>{h(!1)}))});return v.jsx(It,{show:e,onHide:t,size:"lg",children:v.jsxs(Ve,{onSubmit:u.handleSubmit,children:[v.jsx(It.Header,{closeButton:!0,children:v.jsx(It.Title,{children:"Add Casino"})}),v.jsxs(It.Body,{children:[u.status&&v.jsx(ur,{variant:"danger",className:"mb-3",children:u.status}),v.jsxs("fieldset",{disabled:u.isSubmitting,children:[v.jsxs(Ve.Group,{children:[v.jsx(Ve.Label,{children:"Name (for your own reference)"}),v.jsx(Ve.Control,{type:"text",name:"name",placeholder:"Ex: moneypot.dev",value:u.values.name,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.name&&!!u.errors.name}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.name})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsx(Ve.Label,{children:"Website base URL"}),v.jsx(Ve.Control,{type:"text",name:"baseUrl",placeholder:"Ex: https://moneypot.dev",value:u.values.baseUrl,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.baseUrl&&!!u.errors.baseUrl}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.baseUrl})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsx(Ve.Label,{children:"GraphQL URL"}),v.jsx(Ve.Control,{type:"text",name:"graphqlUrl",placeholder:"Ex: https://api.moneypot.dev/graphql",value:u.values.graphqlUrl,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.graphqlUrl&&!!u.errors.graphqlUrl}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.graphqlUrl})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsxs(Ve.Label,{children:["API Key"," ",v.jsx(xz,{text:"This should be a valid api key that you created for your controller on the casino website"})]}),v.jsx(Ve.Control,{type:"text",name:"apiKey",placeholder:"Ex: 01234567-89ab-cdef-0123-456789abcdef",value:u.values.apiKey,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.apiKey&&!!u.errors.apiKey}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.apiKey})]})]})]}),v.jsx(It.Footer,{children:v.jsx(Qt,{type:"submit",disabled:u.isSubmitting,children:u.isSubmitting?"Testing...":"Test connection and save"})})]})})}),Nz=zt(()=>{var i;const e=sn(),[t,r]=b.useState(!1);return v.jsxs("div",{children:[v.jsx(Az,{show:t,onHide:()=>r(!1)}),v.jsxs("h2",{children:["Casinos",v.jsx(Qt,{className:"float-end",onClick:()=>r(!0),children:"Add casino"})]}),v.jsxs(_i,{striped:!0,bordered:!0,hover:!0,children:[v.jsx("thead",{children:v.jsxs("tr",{children:[v.jsx("th",{children:"ID"}),v.jsx("th",{children:"Name"}),v.jsx("th",{children:"Base URL"}),v.jsx("th",{children:"GraphQL URL"}),v.jsx("th",{children:"Added"})]})}),v.jsx("tbody",{children:(i=e.loggedIn)==null?void 0:i.casinos.map(l=>v.jsxs("tr",{children:[v.jsx("td",{children:v.jsx($n,{to:`/casinos/${l.id}`,children:l.id})}),v.jsx("td",{children:l.name}),v.jsxs("td",{style:{whiteSpace:"nowrap"},children:[v.jsx("span",{className:"user-select-all",children:l.baseUrl})," ",v.jsx("a",{href:l.baseUrl,target:"_blank",rel:"noreferrer",className:"text-decoration-none",children:v.jsx(Gm,{style:{verticalAlign:"baseline"}})})]}),v.jsxs("td",{style:{whiteSpace:"nowrap"},children:[v.jsx("span",{className:"user-select-all",children:l.graphqlUrl})," ",v.jsx("a",{href:l.graphqlUrl,target:"_blank",rel:"noreferrer",className:"text-decoration-none",children:v.jsx(Gm,{style:{verticalAlign:"baseline"}})})]}),v.jsx("td",{children:v.jsx(Da,{date:El(l.id),variant:"long"})})]},l.id))})]})]})});function Oi(){var i,l;const{casinoId:e}=j_(),t=sn();return b.useEffect(()=>{var u;e&&!((u=t.loggedIn)!=null&&u.casinos.find(c=>c.id===e))&&console.warn(`Casino ${e} not found`)},[e,(i=t.loggedIn)==null?void 0:i.casinos]),((l=t.loggedIn)==null?void 0:l.casinos.find(u=>u.id===e))||null}const v_=[{path:"",title:"Casino"},{path:"users",title:"Users"},{path:"experiences",title:"Experiences"},{path:"bankrolls",title:"Bankrolls"},{path:"currencies",title:"Currencies"},{path:"deposits",title:"Deposits"},{path:"withdrawals",title:"Withdrawals"},{path:"jwks",title:"Pubkeys"}];function Cz(e,t){return e.sort((r,i)=>i.length-r.length).find(r=>t.includes("/"+r))}const Rz=zt(()=>{var h;const e=ka(),t=sn(),r=Mf(),i=Oi();if(!t.loggedIn)return v.jsx("div",{children:"You must be logged in"});if(!i)return v.jsx(Yr,{});const l=Cz(v_.map(d=>d.path),e.pathname),u=e.pathname.match(/^\/casinos\/[^/]+/),c=d=>{var E;if(!u)return;const m=d.target.value,y=(E=t.loggedIn)==null?void 0:E.casinos.find(g=>g.id===m);y&&r(e.pathname.replace(/\/casinos\/[^/]+/,`/casinos/${y.id}`))};return v.jsxs("div",{children:[v.jsxs(bT,{direction:"horizontal",style:{alignItems:"baseline"},children:[v.jsxs(Ip,{children:[v.jsx(Ip.Item,{linkAs:$n,linkProps:{to:"/casinos"},children:"Casinos"}),v.jsx(Ip.Item,{active:!0,children:i.name})]}),v.jsxs(Ve.Select,{className:"ms-2 d-inline-block",style:{width:"auto"},size:"sm",value:i.id,onChange:c,children:[v.jsx("option",{disabled:!0,children:"Select casino"}),(h=t.loggedIn)==null?void 0:h.casinos.map(d=>v.jsx("option",{value:d.id,children:d.name},d.id))]})]}),v.jsx("ul",{className:"nav nav-tabs mb-4 ",children:v_.map(d=>v.jsx("li",{className:"nav-item",children:v.jsx($n,{to:d.path,className:`nav-link ${l===d.path?"active":""}`,children:d.title})},d.path))}),v.jsx(M_,{})]})}),vi=({text:e,sourceRef:t,popoverDuration:r=1e3,popoverPlacement:i="top"})=>{const[l,u]=b.useState("idle"),[c,h]=b.useState(!1),[d,m]=b.useState("success"),y=b.useRef(null),E=b.useRef(null),g=b.useRef(null);b.useEffect(()=>()=>{y.current&&clearTimeout(y.current),E.current&&clearTimeout(E.current)},[]);const _=x=>{m(x),h(!0),E.current&&clearTimeout(E.current),E.current=setTimeout(()=>{h(!1)},r)},T=()=>{y.current&&clearTimeout(y.current),navigator.clipboard.writeText(e).then(()=>{u("success"),y.current=setTimeout(()=>u("idle"),r),_("success")}).catch(x=>{console.error("Failed to copy text: ",x),u("error"),y.current=setTimeout(()=>u("idle"),r),_("error")})},N=()=>{switch(l){case"success":return v.jsx(Km,{className:"text-success-emphasis"});case"error":return v.jsx(Jm,{className:"text-danger-emphasis"});default:return v.jsx(u1,{})}},A=()=>d==="success"?"Copied!":"Error";return v.jsxs(v.Fragment,{children:[v.jsx(Qt,{ref:g,variant:"outline-secondary",size:"sm",onClick:T,"aria-label":"Copy to clipboard",children:N()}),v.jsx(Jy,{show:c,target:(t==null?void 0:t.current)||g.current,placement:i,children:v.jsx(Um,{id:"copy-popover",children:v.jsx(Um.Body,{children:d==="success"?v.jsxs("span",{className:"text-success-emphasis",children:[v.jsx(Km,{})," ",A()]}):v.jsxs("span",{className:"text-danger-emphasis",children:[v.jsx(Jm,{})," ",A()]})})})})]})},jz=Cr(`
179
+ `),Tz={name:"moneypot.dev",baseUrl:"https://moneypot.dev",graphqlUrl:"https://api.moneypot.dev/graphql",apiKey:""},wz=wo().shape({name:ld().required(),baseUrl:E1().required(),graphqlUrl:b1().required(),apiKey:_z().required()}),Az=zt(({show:e,onHide:t})=>{const r=sn(),[i]=Hx(r,{document:Oz}),u=id({initialValues:Tz,validationSchema:wz,onSubmit:async(c,{setSubmitting:h,setStatus:d})=>(d(null),h(!0),i({variables:{input:{name:c.name,baseUrl:c.baseUrl,graphqlUrl:c.graphqlUrl,apiKey:c.apiKey}}}).then(m=>{var E;((E=m.hubAddCasino)==null?void 0:E.casino)&&(r.refetchCasinos(),t())}).catch(m=>{d(Bn(m))}).finally(()=>{h(!1)}))});return v.jsx(It,{show:e,onHide:t,size:"lg",children:v.jsxs(Ve,{onSubmit:u.handleSubmit,children:[v.jsx(It.Header,{closeButton:!0,children:v.jsx(It.Title,{children:"Add Casino"})}),v.jsxs(It.Body,{children:[u.status&&v.jsx(ur,{variant:"danger",className:"mb-3",children:u.status}),v.jsxs("fieldset",{disabled:u.isSubmitting,children:[v.jsxs(Ve.Group,{children:[v.jsx(Ve.Label,{children:"Name (for your own reference)"}),v.jsx(Ve.Control,{type:"text",name:"name",placeholder:"Ex: moneypot.dev",value:u.values.name,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.name&&!!u.errors.name}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.name})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsx(Ve.Label,{children:"Website base URL"}),v.jsx(Ve.Control,{type:"text",name:"baseUrl",placeholder:"Ex: https://moneypot.dev",value:u.values.baseUrl,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.baseUrl&&!!u.errors.baseUrl}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.baseUrl})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsx(Ve.Label,{children:"GraphQL URL"}),v.jsx(Ve.Control,{type:"text",name:"graphqlUrl",placeholder:"Ex: https://api.moneypot.dev/graphql",value:u.values.graphqlUrl,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.graphqlUrl&&!!u.errors.graphqlUrl}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.graphqlUrl})]}),v.jsxs(Ve.Group,{className:"mt-2",children:[v.jsxs(Ve.Label,{children:["Controller API Key"," ",v.jsx(xz,{text:"This should be a valid api key that you created for your controller on the casino website"})]}),v.jsx(Ve.Control,{type:"text",name:"apiKey",placeholder:"Ex: 01234567-89ab-cdef-0123-456789abcdef",value:u.values.apiKey,onChange:u.handleChange,onBlur:u.handleBlur,isInvalid:u.touched.apiKey&&!!u.errors.apiKey}),v.jsx(Ve.Control.Feedback,{type:"invalid",children:u.errors.apiKey})]})]})]}),v.jsx(It.Footer,{children:v.jsx(Qt,{type:"submit",disabled:u.isSubmitting,children:u.isSubmitting?"Testing...":"Test connection and save"})})]})})}),Nz=zt(()=>{var i;const e=sn(),[t,r]=b.useState(!1);return v.jsxs("div",{children:[v.jsx(Az,{show:t,onHide:()=>r(!1)}),v.jsxs("h2",{children:["Casinos",v.jsx(Qt,{className:"float-end",onClick:()=>r(!0),children:"Add casino"})]}),v.jsxs(_i,{striped:!0,bordered:!0,hover:!0,children:[v.jsx("thead",{children:v.jsxs("tr",{children:[v.jsx("th",{children:"ID"}),v.jsx("th",{children:"Name"}),v.jsx("th",{children:"Base URL"}),v.jsx("th",{children:"GraphQL URL"}),v.jsx("th",{children:"Added"})]})}),v.jsx("tbody",{children:(i=e.loggedIn)==null?void 0:i.casinos.map(l=>v.jsxs("tr",{children:[v.jsx("td",{children:v.jsx($n,{to:`/casinos/${l.id}`,children:l.id})}),v.jsx("td",{children:l.name}),v.jsxs("td",{style:{whiteSpace:"nowrap"},children:[v.jsx("span",{className:"user-select-all",children:l.baseUrl})," ",v.jsx("a",{href:l.baseUrl,target:"_blank",rel:"noreferrer",className:"text-decoration-none",children:v.jsx(Gm,{style:{verticalAlign:"baseline"}})})]}),v.jsxs("td",{style:{whiteSpace:"nowrap"},children:[v.jsx("span",{className:"user-select-all",children:l.graphqlUrl})," ",v.jsx("a",{href:l.graphqlUrl,target:"_blank",rel:"noreferrer",className:"text-decoration-none",children:v.jsx(Gm,{style:{verticalAlign:"baseline"}})})]}),v.jsx("td",{children:v.jsx(Da,{date:El(l.id),variant:"long"})})]},l.id))})]})]})});function Oi(){var i,l;const{casinoId:e}=j_(),t=sn();return b.useEffect(()=>{var u;e&&!((u=t.loggedIn)!=null&&u.casinos.find(c=>c.id===e))&&console.warn(`Casino ${e} not found`)},[e,(i=t.loggedIn)==null?void 0:i.casinos]),((l=t.loggedIn)==null?void 0:l.casinos.find(u=>u.id===e))||null}const v_=[{path:"",title:"Casino"},{path:"users",title:"Users"},{path:"experiences",title:"Experiences"},{path:"bankrolls",title:"Bankrolls"},{path:"currencies",title:"Currencies"},{path:"deposits",title:"Deposits"},{path:"withdrawals",title:"Withdrawals"},{path:"jwks",title:"Pubkeys"}];function Cz(e,t){return e.sort((r,i)=>i.length-r.length).find(r=>t.includes("/"+r))}const Rz=zt(()=>{var h;const e=ka(),t=sn(),r=Mf(),i=Oi();if(!t.loggedIn)return v.jsx("div",{children:"You must be logged in"});if(!i)return v.jsx(Yr,{});const l=Cz(v_.map(d=>d.path),e.pathname),u=e.pathname.match(/^\/casinos\/[^/]+/),c=d=>{var E;if(!u)return;const m=d.target.value,y=(E=t.loggedIn)==null?void 0:E.casinos.find(g=>g.id===m);y&&r(e.pathname.replace(/\/casinos\/[^/]+/,`/casinos/${y.id}`))};return v.jsxs("div",{children:[v.jsxs(bT,{direction:"horizontal",style:{alignItems:"baseline"},children:[v.jsxs(Ip,{children:[v.jsx(Ip.Item,{linkAs:$n,linkProps:{to:"/casinos"},children:"Casinos"}),v.jsx(Ip.Item,{active:!0,children:i.name})]}),v.jsxs(Ve.Select,{className:"ms-2 d-inline-block",style:{width:"auto"},size:"sm",value:i.id,onChange:c,children:[v.jsx("option",{disabled:!0,children:"Select casino"}),(h=t.loggedIn)==null?void 0:h.casinos.map(d=>v.jsx("option",{value:d.id,children:d.name},d.id))]})]}),v.jsx("ul",{className:"nav nav-tabs mb-4 ",children:v_.map(d=>v.jsx("li",{className:"nav-item",children:v.jsx($n,{to:d.path,className:`nav-link ${l===d.path?"active":""}`,children:d.title})},d.path))}),v.jsx(M_,{})]})}),vi=({text:e,sourceRef:t,popoverDuration:r=1e3,popoverPlacement:i="top"})=>{const[l,u]=b.useState("idle"),[c,h]=b.useState(!1),[d,m]=b.useState("success"),y=b.useRef(null),E=b.useRef(null),g=b.useRef(null);b.useEffect(()=>()=>{y.current&&clearTimeout(y.current),E.current&&clearTimeout(E.current)},[]);const _=x=>{m(x),h(!0),E.current&&clearTimeout(E.current),E.current=setTimeout(()=>{h(!1)},r)},T=()=>{y.current&&clearTimeout(y.current),navigator.clipboard.writeText(e).then(()=>{u("success"),y.current=setTimeout(()=>u("idle"),r),_("success")}).catch(x=>{console.error("Failed to copy text: ",x),u("error"),y.current=setTimeout(()=>u("idle"),r),_("error")})},N=()=>{switch(l){case"success":return v.jsx(Km,{className:"text-success-emphasis"});case"error":return v.jsx(Jm,{className:"text-danger-emphasis"});default:return v.jsx(u1,{})}},A=()=>d==="success"?"Copied!":"Error";return v.jsxs(v.Fragment,{children:[v.jsx(Qt,{ref:g,variant:"outline-secondary",size:"sm",onClick:T,"aria-label":"Copy to clipboard",children:N()}),v.jsx(Jy,{show:c,target:(t==null?void 0:t.current)||g.current,placement:i,children:v.jsx(Um,{id:"copy-popover",children:v.jsx(Um.Body,{children:d==="success"?v.jsxs("span",{className:"text-success-emphasis",children:[v.jsx(Km,{})," ",A()]}):v.jsxs("span",{className:"text-danger-emphasis",children:[v.jsx(Jm,{})," ",A()]})})})})]})},jz=Cr(`
180
180
  query ListCasinoUsers($casinoId: UUID!, $after: Cursor) {
181
181
  hubCasinoById(id: $casinoId) {
182
182
  id
@@ -4,7 +4,7 @@
4
4
  <meta charset="UTF-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <title>Dashboard</title>
7
- <script type="module" crossorigin src="/dashboard/assets/index-BtrbrisP.js"></script>
7
+ <script type="module" crossorigin src="/dashboard/assets/index-Tqyvm1Se.js"></script>
8
8
  <link rel="stylesheet" crossorigin href="/dashboard/assets/index-tK7EUtyc.css">
9
9
  </head>
10
10
  <body>
@@ -69,11 +69,8 @@ export const HubAddCasinoPlugin = makeExtendSchemaPlugin((build) => {
69
69
  }
70
70
  throw e;
71
71
  });
72
- if (!result) {
73
- throw new GraphQLError("Invalid API key");
74
- }
75
- if (!result.currentController) {
76
- throw new GraphQLError("Invalid API key");
72
+ if (!result || !result.currentController) {
73
+ throw new GraphQLError("Invalid API key for your casino controller. Go to your controller on the casino website and double check.");
77
74
  }
78
75
  let casino;
79
76
  try {
@@ -127,6 +124,14 @@ export const HubAddCasinoPlugin = makeExtendSchemaPlugin((build) => {
127
124
  SELECT casino_id, key
128
125
  FROM hub.currency
129
126
  WHERE casino_id = $1
127
+ `,
128
+ values: [casino.id],
129
+ });
130
+ await pgClient.query({
131
+ text: `
132
+ UPDATE hub.bankroll
133
+ SET amount = 1000000
134
+ WHERE casino_id = $1 AND currency_key = 'HOUSE'
130
135
  `,
131
136
  values: [casino.id],
132
137
  });
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@moneypot/hub",
3
3
  "author": "",
4
4
  "keywords": [],
5
- "version": "0.0.1",
5
+ "version": "0.0.2",
6
6
  "description": "",
7
7
  "type": "module",
8
8
  "main": "dist/src/index.js",
@@ -65,5 +65,5 @@
65
65
  "typescript": "^5.4.5",
66
66
  "typescript-eslint": "^8.0.1"
67
67
  },
68
- "license": ""
68
+ "license": "UNLICENSED"
69
69
  }
@@ -1 +0,0 @@
1
- export declare const CaasAddCasinoPlugin: GraphileConfig.Plugin;
@@ -1,150 +0,0 @@
1
- import { constant, context, object, sideEffect } from "postgraphile/grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { exactlyOneRow } from "../db/util.js";
4
- import { gql as generatedGql } from "../__generated__/gql.js";
5
- import { GraphQLClient } from "graphql-request";
6
- import { GraphQLError } from "graphql";
7
- import { superuserPool, upsertCurrencies, withPgPoolTransaction, } from "../db/index.js";
8
- import { logger } from "../logger.js";
9
- import { assert, is } from "tsafe";
10
- import * as jwtService from "../services/jwt-service.js";
11
- import { startTransferProcessor } from "../process-transfers.js";
12
- const GET_CURRENT_CONTROLLER = generatedGql(`
13
- query GetCurrentController {
14
- currentController {
15
- id
16
- }
17
- allCurrencies {
18
- nodes {
19
- id
20
- displayUnitName
21
- displayUnitScale
22
- }
23
- }
24
- }
25
- `);
26
- export const CaasAddCasinoPlugin = makeExtendSchemaPlugin((build) => {
27
- const casinoTable = build.input.pgRegistry.pgResources.caas_casino;
28
- return {
29
- typeDefs: gql `
30
- input CaasAddCasinoInput {
31
- name: String!
32
- baseUrl: String!
33
- graphqlUrl: String!
34
- apiKey: String!
35
- }
36
-
37
- type CaasAddCasinoPayload {
38
- casino: CaasCasino
39
- query: Query
40
- }
41
-
42
- extend type Mutation {
43
- caasAddCasino(input: CaasAddCasinoInput!): CaasAddCasinoPayload
44
- }
45
- `,
46
- plans: {
47
- Mutation: {
48
- caasAddCasino(_, { $input }) {
49
- const $identity = context().get("identity");
50
- const $casinoId = sideEffect([$input, $identity], ([input, identity]) => {
51
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
52
- if (identity?.kind !== "operator") {
53
- throw new GraphQLError("Unauthorized");
54
- }
55
- assert(is(input));
56
- const { name, baseUrl, graphqlUrl, apiKey } = input;
57
- const graphqlClient = new GraphQLClient(graphqlUrl, {
58
- headers: {
59
- Authorization: `apikey:${apiKey}`,
60
- },
61
- });
62
- console.log(`[caasAddCasino] Making request to ${graphqlUrl}`);
63
- const result = await graphqlClient
64
- .request(GET_CURRENT_CONTROLLER)
65
- .catch((e) => {
66
- if (e.cause?.code === "ECONNREFUSED" ||
67
- e.cause?.code === "ENOTFOUND") {
68
- throw new GraphQLError(`Cannot connect to graphqlUrl`);
69
- }
70
- throw e;
71
- });
72
- if (!result) {
73
- throw new GraphQLError("Invalid API key");
74
- }
75
- if (!result.currentController) {
76
- throw new GraphQLError("Invalid API key");
77
- }
78
- let casino;
79
- try {
80
- casino = await pgClient
81
- .query({
82
- text: `
83
- INSERT INTO caas.casino(name, base_url, graphql_url)
84
- VALUES($1, $2, $3)
85
- returning id
86
- `,
87
- values: [name, baseUrl, graphqlUrl],
88
- })
89
- .then((res) => {
90
- console.log("res", res.rows);
91
- return res;
92
- })
93
- .then(exactlyOneRow);
94
- }
95
- catch (e) {
96
- if (e instanceof Error &&
97
- "code" in e &&
98
- e.code === "23505" &&
99
- "constraint" in e) {
100
- switch (e.constraint) {
101
- case "casino_graphql_url_idx":
102
- throw new GraphQLError("Casino with that graphqlUrl already exists");
103
- case "casino_base_url_idx":
104
- throw new GraphQLError("Casino with that baseUrl already exists");
105
- default:
106
- throw new GraphQLError(`Duplicate constraint violation: ${e.constraint}`);
107
- }
108
- }
109
- logger.error("Error adding casino", e);
110
- throw e;
111
- }
112
- await pgClient.query({
113
- text: `
114
- INSERT INTO caas.casino_secret(id, controller_id, api_key)
115
- VALUES($1, $2, $3)
116
- `,
117
- values: [casino.id, result.currentController.id, apiKey],
118
- });
119
- const currencies = result.allCurrencies?.nodes.flatMap((x) => x || []) || [];
120
- await upsertCurrencies(pgClient, {
121
- casinoId: casino.id,
122
- currencies,
123
- });
124
- await pgClient.query({
125
- text: `
126
- INSERT INTO caas.bankroll (casino_id, currency_key)
127
- SELECT casino_id, key
128
- FROM caas.currency
129
- WHERE casino_id = $1
130
- `,
131
- values: [casino.id],
132
- });
133
- logger.info(`Fetching JWKS for new casino from ${graphqlUrl}...`);
134
- await jwtService.refreshCasinoJwksTask(pgClient, {
135
- graphqlClient,
136
- casinoId: casino.id,
137
- });
138
- startTransferProcessor({ casinoId: casino.id });
139
- return casino.id;
140
- });
141
- });
142
- return object({
143
- casino: casinoTable.get({ id: $casinoId }),
144
- query: constant(true),
145
- });
146
- },
147
- },
148
- },
149
- };
150
- });
@@ -1 +0,0 @@
1
- export declare const CaasAuthenticatePlugin: GraphileConfig.Plugin;
@@ -1,175 +0,0 @@
1
- import { GraphQLError } from "graphql";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { assert, is } from "tsafe";
4
- import { GET_USER_FROM_USER_TOKEN } from "../graphql-queries.js";
5
- import { exactlyOneRow, maybeOneRow } from "../db/util.js";
6
- import { createGraphqlClient } from "../graphql-client.js";
7
- import { constant, context, error, object, sideEffect, } from "postgraphile/grafast";
8
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
9
- import { logger } from "../logger.js";
10
- import * as jwtService from "../services/jwt-service.js";
11
- import { extractGraphQLErrorInfo, isGraphQLError } from "../GraphQLError.js";
12
- export const CaasAuthenticatePlugin = makeExtendSchemaPlugin(() => {
13
- return {
14
- typeDefs: gql `
15
- input CaasAuthenticateInput {
16
- casinoBaseUrl: String!
17
- userToken: String!
18
- }
19
-
20
- type CaasAuthenticateSuccess {
21
- experienceId: UUID!
22
- sessionKey: UUID!
23
- userId: UUID!
24
- uname: String!
25
- }
26
-
27
- type CaasAuthenticatePayload {
28
- query: Query
29
- success: CaasAuthenticateSuccess
30
- }
31
-
32
- extend type Mutation {
33
- caasAuthenticate(input: CaasAuthenticateInput!): CaasAuthenticatePayload
34
- }
35
- `,
36
- plans: {
37
- Mutation: {
38
- caasAuthenticate(_, { $input }) {
39
- try {
40
- const $context = context();
41
- const $success = sideEffect([$input, $context], ([input, context]) => {
42
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
43
- assert(is(input));
44
- const { userToken: jwt, casinoBaseUrl } = input;
45
- const casino = await pgClient
46
- .query({
47
- text: `
48
- SELECT c.*, s.api_key
49
- FROM caas.casino c
50
- LEFT JOIN caas.casino_secret s ON c.id = s.id
51
- WHERE c.base_url = $1`,
52
- values: [casinoBaseUrl],
53
- })
54
- .then(maybeOneRow);
55
- if (!casino) {
56
- throw new GraphQLError(`CAAS is unaware of casino with a base url of provided casinoBaseUrl`);
57
- }
58
- if (!casino.api_key) {
59
- throw new GraphQLError("Casino secret not configured");
60
- }
61
- const graphqlClient = createGraphqlClient({
62
- graphqlUrl: casino.graphql_url,
63
- apiKey: casino.api_key,
64
- });
65
- const verifyResult = await jwtService.verifyJwtFromDbCacheAndEnsureNotAlreadyUsed(pgClient, {
66
- casinoId: casino.id,
67
- jwt,
68
- });
69
- if (!verifyResult.ok) {
70
- throw new GraphQLError(`Error verifying userToken: ${verifyResult.error}`);
71
- }
72
- const { userToken } = verifyResult.value;
73
- let res;
74
- try {
75
- res = await graphqlClient.request(GET_USER_FROM_USER_TOKEN, {
76
- token: userToken,
77
- });
78
- }
79
- catch (e) {
80
- logger.error(`[caasAuthenticate] Error when making GET_USER_FROM_USER_TOKEN to casino:`, e);
81
- if (isGraphQLError(e)) {
82
- const errorInfo = extractGraphQLErrorInfo(e);
83
- if (errorInfo.code === "UNAUTHENTICATED") {
84
- throw new GraphQLError("Invalid api key");
85
- }
86
- else {
87
- throw new GraphQLError(errorInfo.message);
88
- }
89
- }
90
- throw error;
91
- }
92
- const result = res.userFromUserToken;
93
- if (!result || !result.user || !result.experience) {
94
- return null;
95
- }
96
- const mpUserId = result.user.id;
97
- assert(mpUserId);
98
- const uname = result.user.uname;
99
- assert(uname);
100
- const dbUser = await pgClient
101
- .query({
102
- text: `
103
- INSERT INTO caas.user(casino_id, mp_user_id, uname)
104
- VALUES($1, $2, $3)
105
- ON CONFLICT (casino_id, mp_user_id) DO UPDATE
106
- SET uname = EXCLUDED.uname
107
- RETURNING id, uname
108
- `,
109
- values: [casino.id, mpUserId, uname],
110
- })
111
- .then(exactlyOneRow);
112
- const userId = dbUser.id;
113
- const mpExperience = result.experience;
114
- assert(mpExperience);
115
- const dbExperience = await pgClient
116
- .query({
117
- text: `
118
- INSERT INTO caas.experience(casino_id, mp_experience_id, name)
119
- VALUES($1, $2, $3)
120
- ON CONFLICT (casino_id, mp_experience_id) DO UPDATE
121
- SET name = EXCLUDED.name
122
- RETURNING id
123
- `,
124
- values: [casino.id, mpExperience.id, mpExperience.name],
125
- })
126
- .then(exactlyOneRow);
127
- const dbSession = await pgClient
128
- .query({
129
- text: `
130
- INSERT INTO caas.session(casino_id, user_id, experience_id, user_token)
131
- VALUES($1, $2, $3, $4)
132
- RETURNING id, key
133
- `,
134
- values: [casino.id, userId, dbExperience.id, userToken],
135
- })
136
- .then(exactlyOneRow);
137
- const ret = {
138
- userId,
139
- uname: dbUser.uname,
140
- experienceId: dbExperience.id,
141
- sessionKey: dbSession.key,
142
- };
143
- context.identity = {
144
- kind: "user",
145
- session: {
146
- user_id: userId,
147
- mp_user_id: mpUserId,
148
- casino_id: casino.id,
149
- experience_id: dbExperience.id,
150
- session_id: dbSession.id,
151
- },
152
- };
153
- context.pgSettings = {
154
- "session.user_id": userId,
155
- "session.casino_id": casino.id,
156
- "session.experience_id": dbExperience.id,
157
- "session.session_id": dbSession.id,
158
- };
159
- return ret;
160
- });
161
- });
162
- return object({
163
- query: constant(true),
164
- success: $success,
165
- });
166
- }
167
- catch (error) {
168
- logger.error(error);
169
- throw error;
170
- }
171
- },
172
- },
173
- },
174
- };
175
- });
@@ -1 +0,0 @@
1
- export declare const CaasBalanceAlertPlugin: GraphileConfig.Plugin;
@@ -1,43 +0,0 @@
1
- import { makeExtendSchemaPlugin, gql } from "postgraphile/utils";
2
- import { context, lambda, listen } from "postgraphile/grafast";
3
- import { jsonParse } from "postgraphile/@dataplan/json";
4
- export const CaasBalanceAlertPlugin = makeExtendSchemaPlugin(() => {
5
- return {
6
- typeDefs: gql `
7
- extend type Subscription {
8
- caasBalanceAlert: CaasBalanceAlertPayload
9
- }
10
-
11
- type CaasBalanceAlertPayload {
12
- currencyKey: String
13
- }
14
- `,
15
- plans: {
16
- Subscription: {
17
- caasBalanceAlert: {
18
- subscribePlan(_$root) {
19
- const $pgSubscriber = context().get("pgSubscriber");
20
- const $identity = context().get("identity");
21
- const $channelKey = lambda($identity, (identity) => {
22
- if (identity?.kind === "user") {
23
- return `caas:user:${identity.session.user_id}:balance_alert`;
24
- }
25
- else {
26
- return "";
27
- }
28
- });
29
- return listen($pgSubscriber, $channelKey, jsonParse);
30
- },
31
- plan($event) {
32
- return $event;
33
- },
34
- },
35
- },
36
- CaasBalanceAlertPayload: {
37
- currencyKey($event) {
38
- return $event.get("currency_key");
39
- },
40
- },
41
- },
42
- };
43
- });
@@ -1 +0,0 @@
1
- export declare const CaasClaimFaucetPlugin: GraphileConfig.Plugin;
@@ -1,85 +0,0 @@
1
- import { object } from "grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction } from "../db/index.js";
4
- import { constant, context, sideEffect } from "postgraphile/grafast";
5
- const CLAIM_AMOUNT = 1000;
6
- export const CaasClaimFaucetPlugin = makeExtendSchemaPlugin(() => {
7
- return {
8
- typeDefs: gql `
9
- type CaasClaimFaucetPayload {
10
- success: Boolean!
11
- query: Query
12
- }
13
-
14
- extend type Mutation {
15
- caasClaimFaucet: CaasClaimFaucetPayload
16
- }
17
- `,
18
- plans: {
19
- Mutation: {
20
- caasClaimFaucet() {
21
- const $identity = context().get("identity");
22
- const $result = sideEffect([$identity], ([identity]) => {
23
- if (identity?.kind !== "user") {
24
- throw new Error("Must be logged in as user");
25
- }
26
- const { session } = identity;
27
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
28
- await upsertPlayCurrency(pgClient, session.casino_id);
29
- await pgClient.query({
30
- text: `
31
- insert into caas.faucet_claim (user_id, casino_id, experience_id, currency_key, amount)
32
- values ($1, $2, $3, $4, $5)
33
- `,
34
- values: [
35
- session.user_id,
36
- session.casino_id,
37
- session.experience_id,
38
- "PLAY",
39
- CLAIM_AMOUNT,
40
- ],
41
- });
42
- await pgClient.query({
43
- text: `
44
- INSERT INTO caas.balance (user_id, experience_id, casino_id, currency_key, amount)
45
- VALUES ($1, $2, $3, $4, $5)
46
- ON CONFLICT (user_id, experience_id, casino_id, currency_key) DO UPDATE
47
- SET amount = balance.amount + EXCLUDED.amount
48
- `,
49
- values: [
50
- session.user_id,
51
- session.experience_id,
52
- session.casino_id,
53
- "PLAY",
54
- CLAIM_AMOUNT,
55
- ],
56
- });
57
- return true;
58
- });
59
- });
60
- return object({
61
- result: $result,
62
- });
63
- },
64
- },
65
- CaasClaimFaucetPayload: {
66
- success($data) {
67
- return $data.get("result");
68
- },
69
- query() {
70
- return constant(true);
71
- },
72
- },
73
- },
74
- };
75
- });
76
- async function upsertPlayCurrency(pgClient, casinoId) {
77
- return pgClient.query({
78
- text: `
79
- insert into caas.currency (casino_id, key, display_unit_name, display_unit_scale)
80
- values ($1, 'PLAY', 'tokens', 1)
81
- on conflict (casino_id, key) do nothing
82
- `,
83
- values: [casinoId],
84
- });
85
- }
@@ -1 +0,0 @@
1
- export declare const CaasCurrentXPlugin: GraphileConfig.Plugin;
@@ -1,62 +0,0 @@
1
- import { gql, makeExtendSchemaPlugin } from "graphile-utils";
2
- import { context, inhibitOnNull, lambda } from "postgraphile/grafast";
3
- export const CaasCurrentXPlugin = makeExtendSchemaPlugin((build) => {
4
- const userTable = build.input.pgRegistry.pgResources.caas_user;
5
- const casinoTable = build.input.pgRegistry.pgResources.caas_casino;
6
- const experienceTable = build.input.pgRegistry.pgResources.caas_experience;
7
- const sessionTable = build.input.pgRegistry.pgResources.caas_session;
8
- return {
9
- typeDefs: gql `
10
- extend type Query {
11
- caasCurrentUser: CaasUser
12
- caasCurrentCasino: CaasCasino
13
- caasCurrentExperience: CaasExperience
14
- caasCurrentSession: CaasSession
15
- }
16
- `,
17
- plans: {
18
- Query: {
19
- caasCurrentUser() {
20
- const $identity = context().get("identity");
21
- const $userId = lambda($identity, (identity) => {
22
- if (identity?.kind === "user") {
23
- return identity.session.user_id;
24
- }
25
- return null;
26
- });
27
- return userTable.get({ id: inhibitOnNull($userId) });
28
- },
29
- caasCurrentCasino() {
30
- const $identity = context().get("identity");
31
- const $casinoId = lambda($identity, (identity) => {
32
- if (identity?.kind === "user") {
33
- return identity.session.casino_id;
34
- }
35
- return null;
36
- });
37
- return casinoTable.get({ id: inhibitOnNull($casinoId) });
38
- },
39
- caasCurrentExperience() {
40
- const $identity = context().get("identity");
41
- const $experienceId = lambda($identity, (identity) => {
42
- if (identity?.kind === "user") {
43
- return identity.session.experience_id;
44
- }
45
- return null;
46
- });
47
- return experienceTable.get({ id: inhibitOnNull($experienceId) });
48
- },
49
- caasCurrentSession() {
50
- const $identity = context().get("identity");
51
- const $sessionId = lambda($identity, (identity) => {
52
- if (identity?.kind === "user") {
53
- return identity.session.session_id;
54
- }
55
- return null;
56
- });
57
- return sessionTable.get({ id: inhibitOnNull($sessionId) });
58
- },
59
- },
60
- },
61
- };
62
- });
@@ -1 +0,0 @@
1
- export declare const CaasPrefixPlugin: GraphileConfig.Plugin;
@@ -1,25 +0,0 @@
1
- export const CaasPrefixPlugin = {
2
- name: "CaasPrefixPlugin",
3
- version: "1.0.0",
4
- after: ["PgTablesPlugin"],
5
- inflection: {
6
- replace: {
7
- _schemaPrefix(previous, options, { pgNamespace, serviceName, }) {
8
- const pgService = options.pgServices?.find((db) => db.name === serviceName);
9
- const databasePrefix = serviceName === "main" ? "" : `${serviceName}_`;
10
- let schemaPrefix;
11
- if (pgNamespace.nspname === "caas" ||
12
- pgNamespace.nspname.startsWith("caas_")) {
13
- schemaPrefix = `${pgNamespace.nspname}_`;
14
- }
15
- else if (pgNamespace.nspname === pgService?.schemas?.[0]) {
16
- schemaPrefix = "";
17
- }
18
- else {
19
- schemaPrefix = `${pgNamespace.nspname}_`;
20
- }
21
- return `${databasePrefix}${schemaPrefix}`;
22
- },
23
- },
24
- },
25
- };
@@ -1 +0,0 @@
1
- export declare const CaasUserBalanceByCurrencyPlugin: GraphileConfig.Plugin;
@@ -1,55 +0,0 @@
1
- import { access, context, loadOne, object, } from "postgraphile/grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool } from "../db/index.js";
4
- import { pgSelectSingleFromRecord, } from "postgraphile/@dataplan/pg";
5
- export const CaasUserBalanceByCurrencyPlugin = makeExtendSchemaPlugin((build) => {
6
- const balances = build.input.pgRegistry.pgResources.caas_balance;
7
- return {
8
- typeDefs: gql `
9
- extend type CaasUser {
10
- balanceByCurrency(currency: String!): CaasBalance
11
- }
12
- `,
13
- plans: {
14
- CaasUser: {
15
- balanceByCurrency: ($record, { $currency }) => {
16
- const $identity = context().get("identity");
17
- const $params = object({
18
- currency: $currency,
19
- targetUserId: $record.get("id"),
20
- casino_id: access($identity, ["session", "casino_id"]),
21
- experience_id: access($identity, ["session", "experience_id"]),
22
- });
23
- const $balance = loadOne($params, batchGetUserBalanceByCurrency);
24
- return pgSelectSingleFromRecord(balances, $balance);
25
- },
26
- },
27
- },
28
- };
29
- });
30
- async function batchGetUserBalanceByCurrency(paramsArray) {
31
- const values = [];
32
- const valuePlaceholders = [];
33
- paramsArray.forEach((p, index) => {
34
- const baseIndex = index * 4 + 1;
35
- valuePlaceholders.push(`($${baseIndex}, $${baseIndex + 1}::uuid, $${baseIndex + 2}::uuid, $${baseIndex + 3}::uuid)`);
36
- values.push(p.currency, p.targetUserId, p.casino_id, p.experience_id);
37
- });
38
- const sql = `
39
- SELECT b.*
40
- FROM caas.balance b
41
- JOIN (
42
- VALUES
43
- ${valuePlaceholders.join(",\n ")}
44
- ) AS vals(currency_key, user_id, casino_id, experience_id)
45
- ON b.currency_key = vals.currency_key
46
- AND b.user_id = vals.user_id
47
- AND b.casino_id = vals.casino_id
48
- AND b.experience_id = vals.experience_id
49
- `;
50
- const { rows } = await superuserPool.query(sql, values);
51
- return paramsArray.map((p) => rows.find((row) => row.currency_key === p.currency &&
52
- row.user_id === p.targetUserId &&
53
- row.casino_id === p.casino_id &&
54
- row.experience_id === p.experience_id));
55
- }
@@ -1 +0,0 @@
1
- export declare const CaasWithdrawPlugin: GraphileConfig.Plugin;
@@ -1,133 +0,0 @@
1
- import { constant, context, object, sideEffect } from "postgraphile/grafast";
2
- import { gql, makeExtendSchemaPlugin } from "postgraphile/utils";
3
- import { superuserPool, withPgPoolTransaction, } from "../db/index.js";
4
- import { exactlyOneRow, maybeOneRow } from "../db/util.js";
5
- import { GraphQLError } from "graphql";
6
- export const CaasWithdrawPlugin = makeExtendSchemaPlugin((build) => {
7
- const caasWithdrawalRequests = build.input.pgRegistry.pgResources.caas_withdrawal_request;
8
- return {
9
- typeDefs: gql `
10
- input CaasWithdrawInput {
11
- amount: Int!
12
- currency: String!
13
- }
14
-
15
- type CaasWithdrawPayload {
16
- withdrawalRequest: CaasWithdrawalRequest!
17
- query: Query
18
- }
19
-
20
- extend type Mutation {
21
- caasWithdraw(input: CaasWithdrawInput!): CaasWithdrawPayload
22
- }
23
- `,
24
- plans: {
25
- Mutation: {
26
- caasWithdraw(_, { $input }) {
27
- const $identity = context().get("identity");
28
- const $withdrawalRequestId = sideEffect([$input, $identity], ([input, identity]) => {
29
- if (identity?.kind !== "user") {
30
- throw new GraphQLError("You must be logged in");
31
- }
32
- const { session } = identity;
33
- return withPgPoolTransaction(superuserPool, async (pgClient) => {
34
- const { amount, currency } = input;
35
- if (amount <= 0) {
36
- throw new GraphQLError("Withdraw amount must be greater than zero");
37
- }
38
- if (!Number.isInteger(amount)) {
39
- throw new GraphQLError("Withdraw amount must be an integer");
40
- }
41
- const dbCurrency = await pgClient
42
- .query({
43
- text: `
44
- SELECT key
45
- FROM caas.currency
46
- WHERE key = $1 AND casino_id = $2
47
- `,
48
- values: [currency, session.casino_id],
49
- })
50
- .then(maybeOneRow);
51
- if (!dbCurrency) {
52
- throw new GraphQLError("Currency not supported");
53
- }
54
- const balance = await pgClient
55
- .query({
56
- text: `
57
- select *
58
- from caas.balance
59
- where currency_key = $1
60
- and user_id = $2
61
- and casino_id = $3
62
- and experience_id = $4
63
-
64
- FOR UPDATE
65
- `,
66
- values: [
67
- currency,
68
- session.user_id,
69
- session.casino_id,
70
- session.experience_id,
71
- ],
72
- })
73
- .then(maybeOneRow);
74
- if (!balance || balance.amount < amount) {
75
- throw new GraphQLError("Insufficient funds for withdrawal");
76
- }
77
- const dbWithdrawalRequest = await pgClient
78
- .query({
79
- text: `
80
- insert into caas.withdrawal_request(
81
- user_id,
82
- experience_id,
83
- casino_id,
84
- amount,
85
- currency_key
86
- )
87
- values ($1, $2, $3, $4, $5)
88
- returning id
89
- `,
90
- values: [
91
- session.user_id,
92
- session.experience_id,
93
- session.casino_id,
94
- amount,
95
- currency,
96
- ],
97
- })
98
- .then(exactlyOneRow);
99
- await pgClient.query({
100
- text: `
101
- update caas.balance
102
- set amount = amount - $1
103
- where user_id = $2
104
- and experience_id = $3
105
- and currency_key = $4
106
- and casino_id = $5
107
- `,
108
- values: [
109
- amount,
110
- session.user_id,
111
- session.experience_id,
112
- currency,
113
- session.casino_id,
114
- ],
115
- });
116
- return dbWithdrawalRequest.id;
117
- });
118
- });
119
- return object({
120
- withdrawalRequestId: $withdrawalRequestId,
121
- query: constant(true),
122
- });
123
- },
124
- },
125
- CaasWithdrawPayload: {
126
- withdrawalRequest($data) {
127
- const $id = $data.get("withdrawalRequestId");
128
- return caasWithdrawalRequests.get({ id: $id });
129
- },
130
- },
131
- },
132
- };
133
- });