playcademy 0.11.3 → 0.11.5

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.
@@ -1,87 +0,0 @@
1
- import { verifyGameToken } from '@playcademy/sdk/server'
2
-
3
- import type { Context } from 'hono'
4
- import type { PlaycademyConfig, XPAwardMetadata } from '@playcademy/sdk/server'
5
- import type { HonoEnv } from '../../../types'
6
-
7
- /**
8
- * TimeBack integration - Award XP to student
9
- * Route: POST /api/integrations/timeback/award-xp
10
- * Auto-generated when integrations.timeback is configured
11
- *
12
- * Flow:
13
- * 1. Game frontend calls this route on deployed backend ({slug}.playcademy.gg/api/integrations/timeback/award-xp)
14
- * 2. This route calls Playcademy platform API via SDK (hub.playcademy.net/api/timeback/award-xp)
15
- * 3. Platform API sends Caliper events to TimeBack
16
- *
17
- * This acts as a secure proxy - game developers don't need TimeBack credentials or
18
- * their own backend infrastructure. The SDK handles config enrichment and metadata.
19
- */
20
-
21
- function getConfig(c: Context<HonoEnv>): PlaycademyConfig {
22
- const config = c.get('config')
23
- const timebackConfig = config?.integrations?.timeback
24
- if (!timebackConfig) throw new Error('TimeBack integration not found')
25
- return config
26
- }
27
-
28
- function enrichMetadata(
29
- metadata: XPAwardMetadata,
30
- config: PlaycademyConfig,
31
- c: Context<HonoEnv>,
32
- ): XPAwardMetadata {
33
- const appName = metadata.appName || config?.name
34
- const subject =
35
- metadata.subject ||
36
- config?.integrations?.timeback?.course?.defaultSubject ||
37
- config?.integrations?.timeback?.course?.subjects?.[0]
38
- const sensorUrl = metadata.sensorUrl || new URL(c.req.url).origin
39
-
40
- if (!appName) throw new Error('App name is required')
41
- if (!subject) throw new Error('Subject is required')
42
- if (!sensorUrl) throw new Error('Sensor URL is required')
43
-
44
- return { ...metadata, appName, subject, sensorUrl }
45
- }
46
-
47
- export async function POST(c: Context<HonoEnv>): Promise<Response> {
48
- try {
49
- // 1. Verify game token from frontend (calls platform API for verification)
50
- const token = c.req.header('Authorization')?.replace('Bearer ', '')
51
- if (!token) return c.json({ error: 'Unauthorized' }, 401)
52
-
53
- const { user } = await verifyGameToken(token)
54
-
55
- // 2. Ensure user has TimeBack integration
56
- if (!user.timeback_id) {
57
- return c.json({ error: 'User does not have TimeBack integration' }, 400)
58
- }
59
-
60
- // 3. Parse request body
61
- const { xpAmount, metadata } = await c.req.json()
62
-
63
- // 4. Get config and enrich metadata with required Caliper fields
64
- const config = getConfig(c)
65
-
66
- // 5. Enrich metadata with required Caliper fields
67
- const enrichedMetadata = enrichMetadata(metadata, config, c)
68
-
69
- // 6. Get SDK client from context (initialized once per Worker, reused across requests)
70
- const sdk = c.get('sdk')
71
-
72
- // 7. Award XP to student (SDK handles enrichment & Caliper events)
73
- const result = await sdk.timeback.awardXP(user.timeback_id, xpAmount, enrichedMetadata)
74
-
75
- return c.json(result)
76
- } catch (error) {
77
- console.error('[TimeBack Award XP] Error:', error)
78
- return c.json(
79
- {
80
- error: 'Failed to award XP',
81
- message: error instanceof Error ? error.message : String(error),
82
- stack: error instanceof Error ? error.stack : undefined,
83
- },
84
- 500,
85
- )
86
- }
87
- }
@@ -1,89 +0,0 @@
1
- import { verifyGameToken } from '@playcademy/sdk/server'
2
-
3
- import type { Context } from 'hono'
4
- import type { PlaycademyConfig, ProgressData } from '@playcademy/sdk/server'
5
- import type { HonoEnv } from '../../../types'
6
-
7
- /**
8
- * TimeBack integration - Record student progress
9
- * Route: POST /api/integrations/timeback/progress
10
- * Auto-generated when integrations.timeback is configured
11
- *
12
- * Flow:
13
- * 1. Game frontend calls this route on deployed backend ({slug}.playcademy.gg/api/integrations/timeback/progress)
14
- * 2. This route calls Playcademy platform API via SDK (hub.playcademy.net/api/timeback/progress)
15
- * 3. Platform API sends Caliper events to TimeBack
16
- *
17
- * This acts as a secure proxy - game developers don't need TimeBack credentials or
18
- * their own backend infrastructure. The SDK handles config enrichment and metadata.
19
- */
20
-
21
- function getConfig(c: Context<HonoEnv>): PlaycademyConfig {
22
- const config = c.get('config')
23
- const timebackConfig = config?.integrations?.timeback
24
- if (!timebackConfig) throw new Error('TimeBack integration not found')
25
- return config
26
- }
27
-
28
- function enrichProgressData(
29
- progressData: ProgressData,
30
- config: PlaycademyConfig,
31
- c: Context<HonoEnv>,
32
- ): ProgressData {
33
- const appName = progressData.appName || config?.name
34
- const subject =
35
- progressData.subject ||
36
- config?.integrations?.timeback?.course?.defaultSubject ||
37
- config?.integrations?.timeback?.course?.subjects?.[0]
38
- const sensorUrl = progressData.sensorUrl || new URL(c.req.url).origin
39
-
40
- if (!appName) throw new Error('App name is required')
41
- if (!subject) throw new Error('Subject is required')
42
- if (!sensorUrl) throw new Error('Sensor URL is required')
43
-
44
- return { ...progressData, appName, subject, sensorUrl }
45
- }
46
-
47
- export async function POST(c: Context<HonoEnv>): Promise<Response> {
48
- try {
49
- // 1. Verify game token from frontend (calls platform API for verification)
50
- const token = c.req.header('Authorization')?.replace('Bearer ', '')
51
- if (!token) {
52
- return c.json({ error: 'Unauthorized' }, 401)
53
- }
54
-
55
- const { user } = await verifyGameToken(token)
56
-
57
- // 2. Ensure user has TimeBack integration
58
- if (!user.timeback_id) {
59
- return c.json({ error: 'User does not have TimeBack integration' }, 400)
60
- }
61
-
62
- // 3. Parse request body
63
- const { progressData } = await c.req.json()
64
-
65
- // 4. Get config and enrich progress data with required Caliper fields
66
- const config = getConfig(c)
67
-
68
- // 5. Enrich progress data with required Caliper fields
69
- const enrichedProgressData = enrichProgressData(progressData, config, c)
70
-
71
- // 5. Get SDK client from context (initialized once per Worker, reused across requests)
72
- const sdk = c.get('sdk')
73
-
74
- // 6. Record progress to TimeBack (SDK handles enrichment & Caliper events)
75
- const result = await sdk.timeback.recordProgress(user.timeback_id, enrichedProgressData)
76
-
77
- return c.json(result)
78
- } catch (error) {
79
- console.error('[TimeBack Progress] Error:', error)
80
- return c.json(
81
- {
82
- error: 'Failed to record progress',
83
- message: error instanceof Error ? error.message : String(error),
84
- stack: error instanceof Error ? error.stack : undefined,
85
- },
86
- 500,
87
- )
88
- }
89
- }