@jskit-ai/google-rewarded-core 0.1.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 (42) hide show
  1. package/README.md +98 -0
  2. package/docs/protecting-server-actions.md +190 -0
  3. package/package.descriptor.mjs +186 -0
  4. package/package.json +10 -0
  5. package/src/server/GoogleRewardedCoreProvider.js +51 -0
  6. package/src/server/actions.js +102 -0
  7. package/src/server/inputSchemas.js +394 -0
  8. package/src/server/providerConfigs/GoogleRewardedProviderConfigsProvider.js +98 -0
  9. package/src/server/providerConfigs/actions.js +157 -0
  10. package/src/server/providerConfigs/registerRoutes.js +176 -0
  11. package/src/server/providerConfigs/repository.js +107 -0
  12. package/src/server/providerConfigs/service.js +61 -0
  13. package/src/server/registerRoutes.js +169 -0
  14. package/src/server/rules/GoogleRewardedRulesProvider.js +98 -0
  15. package/src/server/rules/actions.js +157 -0
  16. package/src/server/rules/registerRoutes.js +176 -0
  17. package/src/server/rules/repository.js +107 -0
  18. package/src/server/rules/service.js +61 -0
  19. package/src/server/service.js +552 -0
  20. package/src/server/support/requireGoogleRewardedUnlock.js +142 -0
  21. package/src/server/unlockReceipts/GoogleRewardedUnlockReceiptsProvider.js +98 -0
  22. package/src/server/unlockReceipts/actions.js +157 -0
  23. package/src/server/unlockReceipts/registerRoutes.js +176 -0
  24. package/src/server/unlockReceipts/repository.js +107 -0
  25. package/src/server/unlockReceipts/service.js +61 -0
  26. package/src/server/watchSessions/GoogleRewardedWatchSessionsProvider.js +98 -0
  27. package/src/server/watchSessions/actions.js +157 -0
  28. package/src/server/watchSessions/registerRoutes.js +176 -0
  29. package/src/server/watchSessions/repository.js +107 -0
  30. package/src/server/watchSessions/service.js +61 -0
  31. package/src/shared/googleRewardedProviderConfigResource.js +94 -0
  32. package/src/shared/googleRewardedRuleResource.js +136 -0
  33. package/src/shared/googleRewardedUnlockReceiptResource.js +113 -0
  34. package/src/shared/googleRewardedWatchSessionResource.js +139 -0
  35. package/src/shared/index.js +12 -0
  36. package/templates/migrations/google_rewarded_provider_configs_initial.cjs +27 -0
  37. package/templates/migrations/google_rewarded_rules_initial.cjs +31 -0
  38. package/templates/migrations/google_rewarded_unlock_receipts_initial.cjs +35 -0
  39. package/templates/migrations/google_rewarded_watch_sessions_initial.cjs +36 -0
  40. package/test/requireGoogleRewardedUnlock.test.js +274 -0
  41. package/test/routes.test.js +248 -0
  42. package/test/service.test.js +315 -0
@@ -0,0 +1,394 @@
1
+ import { createSchema } from "json-rest-schema";
2
+ import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
3
+
4
+ const gateQuerySchema = createSchema({
5
+ gateKey: {
6
+ type: "string",
7
+ required: true,
8
+ minLength: 1,
9
+ maxLength: 120
10
+ },
11
+ workspaceSlug: {
12
+ type: "string",
13
+ required: false,
14
+ minLength: 1,
15
+ maxLength: 120
16
+ }
17
+ });
18
+
19
+ const currentQueryInputValidator = deepFreeze({
20
+ schema: gateQuerySchema,
21
+ mode: "patch"
22
+ });
23
+
24
+ const startCommandInputValidator = deepFreeze({
25
+ schema: gateQuerySchema,
26
+ mode: "patch"
27
+ });
28
+
29
+ const grantCommandInputValidator = deepFreeze({
30
+ schema: createSchema({
31
+ sessionId: {
32
+ type: "id",
33
+ required: true
34
+ },
35
+ workspaceSlug: {
36
+ type: "string",
37
+ required: false,
38
+ minLength: 1,
39
+ maxLength: 120
40
+ }
41
+ }),
42
+ mode: "patch"
43
+ });
44
+
45
+ const closeCommandInputValidator = deepFreeze({
46
+ schema: createSchema({
47
+ sessionId: {
48
+ type: "id",
49
+ required: true
50
+ },
51
+ workspaceSlug: {
52
+ type: "string",
53
+ required: false,
54
+ minLength: 1,
55
+ maxLength: 120
56
+ }
57
+ }),
58
+ mode: "patch"
59
+ });
60
+
61
+ const gateRuleOutputSchema = createSchema({
62
+ id: {
63
+ type: "string",
64
+ required: false,
65
+ nullable: true,
66
+ minLength: 1
67
+ },
68
+ gateKey: {
69
+ type: "string",
70
+ required: true,
71
+ minLength: 1,
72
+ maxLength: 120
73
+ },
74
+ surface: {
75
+ type: "string",
76
+ required: true,
77
+ minLength: 1,
78
+ maxLength: 64
79
+ },
80
+ enabled: {
81
+ type: "boolean",
82
+ required: true
83
+ },
84
+ unlockMinutes: {
85
+ type: "integer",
86
+ required: true,
87
+ min: 0
88
+ },
89
+ cooldownMinutes: {
90
+ type: "integer",
91
+ required: true,
92
+ min: 0
93
+ },
94
+ dailyLimit: {
95
+ type: "integer",
96
+ required: false,
97
+ nullable: true,
98
+ min: 0
99
+ },
100
+ title: {
101
+ type: "string",
102
+ required: true
103
+ },
104
+ description: {
105
+ type: "string",
106
+ required: true
107
+ }
108
+ });
109
+
110
+ const providerConfigOutputSchema = createSchema({
111
+ id: {
112
+ type: "string",
113
+ required: false,
114
+ nullable: true,
115
+ minLength: 1
116
+ },
117
+ surface: {
118
+ type: "string",
119
+ required: true,
120
+ minLength: 1,
121
+ maxLength: 64
122
+ },
123
+ enabled: {
124
+ type: "boolean",
125
+ required: true
126
+ },
127
+ adUnitPath: {
128
+ type: "string",
129
+ required: true
130
+ },
131
+ scriptMode: {
132
+ type: "string",
133
+ required: true
134
+ }
135
+ });
136
+
137
+ const watchSessionOutputSchema = createSchema({
138
+ id: {
139
+ type: "string",
140
+ required: false,
141
+ nullable: true,
142
+ minLength: 1
143
+ },
144
+ gateKey: {
145
+ type: "string",
146
+ required: true,
147
+ minLength: 1,
148
+ maxLength: 120
149
+ },
150
+ providerConfigId: {
151
+ type: "string",
152
+ required: false,
153
+ nullable: true,
154
+ minLength: 1
155
+ },
156
+ status: {
157
+ type: "string",
158
+ required: true,
159
+ minLength: 1
160
+ },
161
+ startedAt: {
162
+ type: "string",
163
+ required: false,
164
+ nullable: true,
165
+ minLength: 1
166
+ },
167
+ rewardedAt: {
168
+ type: "string",
169
+ required: false,
170
+ nullable: true,
171
+ minLength: 1
172
+ },
173
+ completedAt: {
174
+ type: "string",
175
+ required: false,
176
+ nullable: true,
177
+ minLength: 1
178
+ },
179
+ closedAt: {
180
+ type: "string",
181
+ required: false,
182
+ nullable: true,
183
+ minLength: 1
184
+ }
185
+ });
186
+
187
+ const unlockReceiptOutputSchema = createSchema({
188
+ id: {
189
+ type: "string",
190
+ required: false,
191
+ nullable: true,
192
+ minLength: 1
193
+ },
194
+ gateKey: {
195
+ type: "string",
196
+ required: true,
197
+ minLength: 1,
198
+ maxLength: 120
199
+ },
200
+ providerConfigId: {
201
+ type: "string",
202
+ required: false,
203
+ nullable: true,
204
+ minLength: 1
205
+ },
206
+ watchSessionId: {
207
+ type: "string",
208
+ required: false,
209
+ nullable: true,
210
+ minLength: 1
211
+ },
212
+ grantedAt: {
213
+ type: "string",
214
+ required: false,
215
+ nullable: true,
216
+ minLength: 1
217
+ },
218
+ unlockedUntil: {
219
+ type: "string",
220
+ required: false,
221
+ nullable: true,
222
+ minLength: 1
223
+ }
224
+ });
225
+
226
+ function createGateStateOutputValidator({
227
+ includeSession = false
228
+ } = {}) {
229
+ const fields = {
230
+ gateKey: {
231
+ type: "string",
232
+ required: true,
233
+ minLength: 1,
234
+ maxLength: 120
235
+ },
236
+ workspaceSlug: {
237
+ type: "string",
238
+ required: true,
239
+ minLength: 1,
240
+ maxLength: 120
241
+ },
242
+ surface: {
243
+ type: "string",
244
+ required: true,
245
+ minLength: 1,
246
+ maxLength: 64
247
+ },
248
+ enabled: {
249
+ type: "boolean",
250
+ required: true
251
+ },
252
+ available: {
253
+ type: "boolean",
254
+ required: true
255
+ },
256
+ blocked: {
257
+ type: "boolean",
258
+ required: true
259
+ },
260
+ reason: {
261
+ type: "string",
262
+ required: true,
263
+ minLength: 1
264
+ },
265
+ rule: {
266
+ type: "object",
267
+ required: false,
268
+ nullable: true,
269
+ schema: gateRuleOutputSchema
270
+ },
271
+ providerConfig: {
272
+ type: "object",
273
+ required: false,
274
+ nullable: true,
275
+ schema: providerConfigOutputSchema
276
+ },
277
+ unlock: {
278
+ type: "object",
279
+ required: false,
280
+ nullable: true,
281
+ schema: unlockReceiptOutputSchema
282
+ },
283
+ cooldownUntil: {
284
+ type: "string",
285
+ required: false,
286
+ nullable: true,
287
+ minLength: 1
288
+ },
289
+ dailyLimitRemaining: {
290
+ type: "integer",
291
+ required: false,
292
+ nullable: true,
293
+ min: 0
294
+ }
295
+ };
296
+
297
+ if (includeSession === true) {
298
+ fields.session = {
299
+ type: "object",
300
+ required: false,
301
+ nullable: true,
302
+ schema: watchSessionOutputSchema
303
+ };
304
+ }
305
+
306
+ return deepFreeze({
307
+ schema: createSchema(fields),
308
+ mode: "replace"
309
+ });
310
+ }
311
+
312
+ const currentStateOutputValidator = createGateStateOutputValidator();
313
+ const startGateOutputValidator = createGateStateOutputValidator({
314
+ includeSession: true
315
+ });
316
+
317
+ const grantRewardOutputValidator = deepFreeze({
318
+ schema: createSchema({
319
+ unlocked: {
320
+ type: "boolean",
321
+ required: true
322
+ },
323
+ workspaceSlug: {
324
+ type: "string",
325
+ required: true,
326
+ minLength: 1,
327
+ maxLength: 120
328
+ },
329
+ gateKey: {
330
+ type: "string",
331
+ required: true,
332
+ minLength: 1,
333
+ maxLength: 120
334
+ },
335
+ unlock: {
336
+ type: "object",
337
+ required: false,
338
+ nullable: true,
339
+ schema: unlockReceiptOutputSchema
340
+ },
341
+ session: {
342
+ type: "object",
343
+ required: false,
344
+ nullable: true,
345
+ schema: watchSessionOutputSchema
346
+ }
347
+ }),
348
+ mode: "replace"
349
+ });
350
+
351
+ const closeSessionOutputValidator = deepFreeze({
352
+ schema: createSchema({
353
+ closed: {
354
+ type: "boolean",
355
+ required: true
356
+ },
357
+ workspaceSlug: {
358
+ type: "string",
359
+ required: true,
360
+ minLength: 1,
361
+ maxLength: 120
362
+ },
363
+ gateKey: {
364
+ type: "string",
365
+ required: true,
366
+ minLength: 1,
367
+ maxLength: 120
368
+ },
369
+ session: {
370
+ type: "object",
371
+ required: false,
372
+ nullable: true,
373
+ schema: watchSessionOutputSchema
374
+ },
375
+ reason: {
376
+ type: "string",
377
+ required: false,
378
+ nullable: true,
379
+ minLength: 1
380
+ }
381
+ }),
382
+ mode: "replace"
383
+ });
384
+
385
+ export {
386
+ currentQueryInputValidator,
387
+ startCommandInputValidator,
388
+ grantCommandInputValidator,
389
+ closeCommandInputValidator,
390
+ currentStateOutputValidator,
391
+ startGateOutputValidator,
392
+ grantRewardOutputValidator,
393
+ closeSessionOutputValidator
394
+ };
@@ -0,0 +1,98 @@
1
+ import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
2
+ import { resolveCrudSurfacePolicyFromAppConfig } from "@jskit-ai/crud-core/server/crudModuleConfig";
3
+ import { createCrudJsonApiServiceEvents } from "@jskit-ai/crud-core/server/serviceEvents";
4
+ import {
5
+ INTERNAL_JSON_REST_API,
6
+ addResourceIfMissing,
7
+ createJsonRestResourceScopeOptions
8
+ } from "@jskit-ai/json-rest-api-core/server/jsonRestApiHost";
9
+ import { withActionDefaults } from "@jskit-ai/kernel/shared/actions";
10
+ import { toDatabaseDateTimeUtc } from "@jskit-ai/database-runtime/shared";
11
+ import { createRepository } from "./repository.js";
12
+ import { createService } from "./service.js";
13
+ import { createActions } from "./actions.js";
14
+ import { registerRoutes } from "./registerRoutes.js";
15
+ import { resource } from "../../shared/googleRewardedProviderConfigResource.js";
16
+ const CRUD_MODULE_CONFIG = Object.freeze({
17
+ namespace: "google_rewarded_provider_configs",
18
+ surface: "admin",
19
+ ownershipFilter: "workspace",
20
+ relativePath: "/google-rewarded-provider-configs"
21
+ });
22
+ const baseServiceEvents = createCrudJsonApiServiceEvents(CRUD_MODULE_CONFIG.namespace);
23
+ const serviceEvents = {
24
+ ...baseServiceEvents
25
+ };
26
+
27
+ function resolveCrudPolicyFromApp(app) {
28
+ return resolveCrudSurfacePolicyFromAppConfig(CRUD_MODULE_CONFIG, resolveAppConfig(app), {
29
+ context: "GoogleRewardedProviderConfigsProvider"
30
+ });
31
+ }
32
+
33
+ class GoogleRewardedProviderConfigsProvider {
34
+ static id = "crud.google_rewarded_provider_configs";
35
+
36
+ static dependsOn = ["runtime.actions", "runtime.database", "auth.policy.fastify", "local.main", "json-rest-api.core"];
37
+
38
+ register(app) {
39
+ const crudPolicy = resolveCrudPolicyFromApp(app);
40
+
41
+ app.singleton("repository.google_rewarded_provider_configs", (scope) => {
42
+ const api = scope.make(INTERNAL_JSON_REST_API);
43
+ const knex = scope.make("jskit.database.knex");
44
+ return createRepository({
45
+ api,
46
+ knex
47
+ });
48
+ });
49
+
50
+ app.service(
51
+ "crud.google_rewarded_provider_configs",
52
+ (scope) => {
53
+ return createService({
54
+ googleRewardedProviderConfigsRepository: scope.make("repository.google_rewarded_provider_configs")
55
+ });
56
+ },
57
+ {
58
+ events: serviceEvents
59
+ }
60
+ );
61
+
62
+ app.actions(
63
+ withActionDefaults(
64
+ createActions({
65
+ surface: crudPolicy.surfaceId
66
+ }),
67
+ {
68
+ domain: "crud",
69
+ dependencies: {
70
+ googleRewardedProviderConfigsService: "crud.google_rewarded_provider_configs"
71
+ }
72
+ }
73
+ )
74
+ );
75
+ }
76
+
77
+ async boot(app) {
78
+ const crudPolicy = resolveCrudPolicyFromApp(app);
79
+ const api = app.make(INTERNAL_JSON_REST_API);
80
+ await addResourceIfMissing(
81
+ api,
82
+ "googleRewardedProviderConfigs",
83
+ createJsonRestResourceScopeOptions(resource, {
84
+ writeSerializers: {
85
+ "datetime-utc": toDatabaseDateTimeUtc
86
+ }
87
+ })
88
+ );
89
+ registerRoutes(app, {
90
+ routeOwnershipFilter: crudPolicy.ownershipFilter,
91
+ routeSurface: crudPolicy.surfaceId,
92
+ routeSurfaceRequiresWorkspace: crudPolicy.surfaceDefinition.requiresWorkspace === true,
93
+ routeRelativePath: crudPolicy.relativePath
94
+ });
95
+ }
96
+ }
97
+
98
+ export { GoogleRewardedProviderConfigsProvider };
@@ -0,0 +1,157 @@
1
+ import {
2
+ composeSchemaDefinitions,
3
+ recordIdParamsValidator
4
+ } from "@jskit-ai/kernel/shared/validators";
5
+ import {
6
+ createCrudCursorPaginationQueryValidator,
7
+ listSearchQueryValidator,
8
+ lookupIncludeQueryValidator,
9
+ createCrudParentFilterQueryValidator
10
+ } from "@jskit-ai/crud-core/server/listQueryValidators";
11
+ import { resource } from "../../shared/googleRewardedProviderConfigResource.js";
12
+ import { workspaceSlugParamsValidator } from "@jskit-ai/workspaces-core/server/validators/routeParamsValidator";
13
+
14
+ const listCursorPaginationQueryValidator = createCrudCursorPaginationQueryValidator({
15
+ orderBy: resource.defaultSort
16
+ });
17
+ const listParentFilterQueryValidator = createCrudParentFilterQueryValidator(resource);
18
+ const actionPermissions = Object.freeze({
19
+ list: "crud.google_rewarded_provider_configs.list",
20
+ view: "crud.google_rewarded_provider_configs.view",
21
+ create: "crud.google_rewarded_provider_configs.create",
22
+ update: "crud.google_rewarded_provider_configs.update",
23
+ delete: "crud.google_rewarded_provider_configs.delete"
24
+ });
25
+
26
+ function createActions({ surface } = {}) {
27
+ return Object.freeze([
28
+ {
29
+ id: "crud.google_rewarded_provider_configs.list",
30
+ version: 1,
31
+ kind: "query",
32
+ channels: ["api", "automation", "internal"],
33
+ surfaces: [surface],
34
+ permission: { require: "all", permissions: [actionPermissions.list] },
35
+ input: composeSchemaDefinitions([
36
+ workspaceSlugParamsValidator,
37
+ listCursorPaginationQueryValidator,
38
+ listSearchQueryValidator,
39
+ listParentFilterQueryValidator,
40
+ lookupIncludeQueryValidator,
41
+ ]),
42
+ output: null,
43
+ idempotency: "none",
44
+ audit: {
45
+ actionName: "crud.google_rewarded_provider_configs.list"
46
+ },
47
+ observability: {},
48
+ async execute(input, context, deps) {
49
+ const { workspaceSlug, ...query } = input || {};
50
+ return deps.googleRewardedProviderConfigsService.queryDocuments(query, {
51
+ context
52
+ });
53
+ }
54
+ },
55
+ {
56
+ id: "crud.google_rewarded_provider_configs.view",
57
+ version: 1,
58
+ kind: "query",
59
+ channels: ["api", "automation", "internal"],
60
+ surfaces: [surface],
61
+ permission: { require: "all", permissions: [actionPermissions.view] },
62
+ input: composeSchemaDefinitions([
63
+ workspaceSlugParamsValidator,
64
+ recordIdParamsValidator,
65
+ lookupIncludeQueryValidator,
66
+ ]),
67
+ output: null,
68
+ idempotency: "none",
69
+ audit: {
70
+ actionName: "crud.google_rewarded_provider_configs.view"
71
+ },
72
+ observability: {},
73
+ async execute(input, context, deps) {
74
+ return deps.googleRewardedProviderConfigsService.getDocumentById(input.recordId, {
75
+ context,
76
+ include: input.include
77
+ });
78
+ }
79
+ },
80
+ {
81
+ id: "crud.google_rewarded_provider_configs.create",
82
+ version: 1,
83
+ kind: "command",
84
+ channels: ["api", "automation", "internal"],
85
+ surfaces: [surface],
86
+ permission: { require: "all", permissions: [actionPermissions.create] },
87
+ input: composeSchemaDefinitions([
88
+ workspaceSlugParamsValidator,
89
+ resource.operations.create.body,
90
+ ], {
91
+ mode: "create"
92
+ }),
93
+ output: null,
94
+ idempotency: "optional",
95
+ audit: {
96
+ actionName: "crud.google_rewarded_provider_configs.create"
97
+ },
98
+ observability: {},
99
+ async execute(input, context, deps) {
100
+ const { workspaceSlug, ...payload } = input || {};
101
+ return deps.googleRewardedProviderConfigsService.createDocument(payload, {
102
+ context
103
+ });
104
+ }
105
+ },
106
+ {
107
+ id: "crud.google_rewarded_provider_configs.update",
108
+ version: 1,
109
+ kind: "command",
110
+ channels: ["api", "automation", "internal"],
111
+ surfaces: [surface],
112
+ permission: { require: "all", permissions: [actionPermissions.update] },
113
+ input: composeSchemaDefinitions([
114
+ workspaceSlugParamsValidator,
115
+ recordIdParamsValidator,
116
+ resource.operations.patch.body,
117
+ ]),
118
+ output: null,
119
+ idempotency: "optional",
120
+ audit: {
121
+ actionName: "crud.google_rewarded_provider_configs.update"
122
+ },
123
+ observability: {},
124
+ async execute(input, context, deps) {
125
+ const { workspaceSlug, recordId, ...patch } = input || {};
126
+ return deps.googleRewardedProviderConfigsService.patchDocumentById(recordId, patch, {
127
+ context
128
+ });
129
+ }
130
+ },
131
+ {
132
+ id: "crud.google_rewarded_provider_configs.delete",
133
+ version: 1,
134
+ kind: "command",
135
+ channels: ["api", "automation", "internal"],
136
+ surfaces: [surface],
137
+ permission: { require: "all", permissions: [actionPermissions.delete] },
138
+ input: composeSchemaDefinitions([
139
+ workspaceSlugParamsValidator,
140
+ recordIdParamsValidator,
141
+ ]),
142
+ output: null,
143
+ idempotency: "optional",
144
+ audit: {
145
+ actionName: "crud.google_rewarded_provider_configs.delete"
146
+ },
147
+ observability: {},
148
+ async execute(input, context, deps) {
149
+ return deps.googleRewardedProviderConfigsService.deleteDocumentById(input.recordId, {
150
+ context
151
+ });
152
+ }
153
+ }
154
+ ]);
155
+ }
156
+
157
+ export { createActions };