playcademy 0.13.8 → 0.13.10

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.
@@ -0,0 +1,178 @@
1
+ /**
2
+ * Sample Bucket Storage API Route
3
+ *
4
+ * This route will be available at: https://<your-game-slug>.playcademy.gg/api/sample/bucket
5
+ */
6
+
7
+ /**
8
+ * File metadata for bucket storage
9
+ */
10
+ interface FileInfo {
11
+ /** File key in bucket */
12
+ key: string
13
+ /** File size in bytes */
14
+ size: number
15
+ /** Upload timestamp */
16
+ uploaded: string
17
+ }
18
+
19
+ /**
20
+ * GET /api/sample/bucket
21
+ *
22
+ * List or retrieve files from bucket storage
23
+ */
24
+ export async function GET(c: Context): Promise<Response> {
25
+ try {
26
+ const fileKey = c.req.query('key')
27
+
28
+ // If key provided, get specific file
29
+ if (fileKey) {
30
+ const object = await c.env.BUCKET.get(fileKey)
31
+
32
+ if (!object) {
33
+ return c.json(
34
+ {
35
+ success: false,
36
+ error: 'File not found',
37
+ },
38
+ 404,
39
+ )
40
+ }
41
+
42
+ // Return file with appropriate headers
43
+ return new Response(object.body, {
44
+ headers: {
45
+ 'Content-Type': object.httpMetadata?.contentType || 'application/octet-stream',
46
+ 'Content-Length': object.size.toString(),
47
+ 'Cache-Control': 'public, max-age=3600',
48
+ },
49
+ })
50
+ }
51
+
52
+ // Otherwise, list files
53
+ const prefix = c.req.query('prefix') || ''
54
+ const listed = await c.env.BUCKET.list({ prefix })
55
+
56
+ const files: FileInfo[] = listed.objects.map(obj => ({
57
+ key: obj.key,
58
+ size: obj.size,
59
+ uploaded: obj.uploaded.toISOString(),
60
+ }))
61
+
62
+ return c.json({
63
+ success: true,
64
+ data: files,
65
+ truncated: listed.truncated,
66
+ })
67
+ } catch (error) {
68
+ return c.json(
69
+ {
70
+ success: false,
71
+ error: 'Failed to access bucket storage',
72
+ details: error instanceof Error ? error.message : String(error),
73
+ },
74
+ 500,
75
+ )
76
+ }
77
+ }
78
+
79
+ /**
80
+ * PUT /api/sample/bucket
81
+ *
82
+ * Upload a file to bucket storage
83
+ */
84
+ export async function PUT(c: Context): Promise<Response> {
85
+ try {
86
+ const fileKey = c.req.query('key')
87
+
88
+ if (!fileKey) {
89
+ return c.json(
90
+ {
91
+ success: false,
92
+ error: 'File key is required',
93
+ },
94
+ 400,
95
+ )
96
+ }
97
+
98
+ // Get file from request body
99
+ const body = await c.req.arrayBuffer()
100
+
101
+ if (!body || body.byteLength === 0) {
102
+ return c.json(
103
+ {
104
+ success: false,
105
+ error: 'File body is required',
106
+ },
107
+ 400,
108
+ )
109
+ }
110
+
111
+ // Get content type from header or default to binary
112
+ const contentType = c.req.header('Content-Type') || 'application/octet-stream'
113
+
114
+ // Upload to bucket
115
+ await c.env.BUCKET.put(fileKey, body, {
116
+ httpMetadata: {
117
+ contentType,
118
+ },
119
+ })
120
+
121
+ return c.json({
122
+ success: true,
123
+ data: {
124
+ key: fileKey,
125
+ size: body.byteLength,
126
+ uploaded: new Date().toISOString(),
127
+ },
128
+ message: 'File uploaded successfully',
129
+ })
130
+ } catch (error) {
131
+ return c.json(
132
+ {
133
+ success: false,
134
+ error: 'Failed to upload file',
135
+ details: error instanceof Error ? error.message : String(error),
136
+ },
137
+ 500,
138
+ )
139
+ }
140
+ }
141
+
142
+ /**
143
+ * DELETE /api/sample/bucket
144
+ *
145
+ * Delete a file from bucket storage
146
+ */
147
+ export async function DELETE(c: Context): Promise<Response> {
148
+ try {
149
+ const fileKey = c.req.query('key')
150
+
151
+ if (!fileKey) {
152
+ return c.json(
153
+ {
154
+ success: false,
155
+ error: 'File key is required',
156
+ },
157
+ 400,
158
+ )
159
+ }
160
+
161
+ // Delete from bucket
162
+ await c.env.BUCKET.delete(fileKey)
163
+
164
+ return c.json({
165
+ success: true,
166
+ message: 'File deleted successfully',
167
+ })
168
+ } catch (error) {
169
+ return c.json(
170
+ {
171
+ success: false,
172
+ error: 'Failed to delete file',
173
+ details: error instanceof Error ? error.message : String(error),
174
+ },
175
+ 500,
176
+ )
177
+ }
178
+ }
@@ -4,8 +4,6 @@
4
4
  * This route will be available at: https://<your-game-slug>.playcademy.gg/api/sample/custom
5
5
  */
6
6
 
7
- import type { Context } from 'hono'
8
-
9
7
  /**
10
8
  * GET /api/sample/custom
11
9
  *
@@ -9,8 +9,6 @@ import { desc } from 'drizzle-orm'
9
9
 
10
10
  import { getDb, schema } from '../../../db'
11
11
 
12
- import type { Context } from 'hono'
13
-
14
12
  /**
15
13
  * GET /api/sample/database
16
14
  *
@@ -70,12 +68,16 @@ export async function POST(c: Context): Promise<Response> {
70
68
  .insert(schema.users)
71
69
  .values({
72
70
  name: 'Demo Player',
73
- email: 'demo@example.com',
71
+ updatedAt: new Date().toISOString(),
74
72
  createdAt: new Date().toISOString(),
75
73
  })
76
74
  .returning()
77
75
  }
78
76
 
77
+ if (!user) {
78
+ return c.json({ success: false, error: 'Failed to create user' }, 500)
79
+ }
80
+
79
81
  const [newScore] = await db
80
82
  .insert(schema.scores)
81
83
  .values({
@@ -4,8 +4,6 @@
4
4
  * This route will be available at: https://<your-game-slug>.playcademy.gg/api/sample/kv
5
5
  */
6
6
 
7
- import type { Context } from 'hono'
8
-
9
7
  /**
10
8
  * Player state stored in KV
11
9
  */
@@ -8,8 +8,6 @@ import { desc } from 'drizzle-orm'
8
8
 
9
9
  import { getDb, schema } from '../../db'
10
10
 
11
- import type { Context } from 'hono'
12
-
13
11
  /**
14
12
  * Request body for score submission
15
13
  */
@@ -34,7 +34,6 @@
34
34
  *
35
35
  * This route will be available at: https://<your-game-slug>.playcademy.gg/api/hello
36
36
  */
37
- import type { Context } from 'hono'
38
37
 
39
38
  /**
40
39
  * GET /api/hello
@@ -10,8 +10,8 @@ yarn-error.log*
10
10
  pnpm-debug.log*
11
11
  lerna-debug.log*
12
12
 
13
- # environment
14
- .env
15
-
16
13
  # os
17
14
  .DS_Store
15
+
16
+ # playcademy
17
+ playcademy-env.d.ts
@@ -0,0 +1,16 @@
1
+ /// <reference types="./.playcademy/node_modules/@cloudflare/workers-types" />
2
+
3
+ import type { Context as HonoContext } from 'hono'
4
+
5
+ declare global {
6
+ interface PlaycademyEnv {
7
+ PLAYCADEMY_API_KEY: string
8
+ GAME_ID: string
9
+ PLAYCADEMY_BASE_URL: string{{BINDINGS}}
10
+ }
11
+
12
+ type Context = HonoContext<{ Bindings: PlaycademyEnv }>
13
+ }
14
+
15
+ export {}
16
+
@@ -1,3 +1,7 @@
1
1
  *.zip
2
2
  db
3
3
  kv
4
+ bucket
5
+ node_modules/
6
+ package.json
7
+ *.lock
package/dist/utils.d.ts CHANGED
@@ -52,6 +52,8 @@ interface IntegrationsConfig {
52
52
  database?: DatabaseIntegration | boolean;
53
53
  /** Key-Value storage (optional) */
54
54
  kv?: boolean;
55
+ /** Bucket storage (optional) */
56
+ bucket?: boolean;
55
57
  }
56
58
  /**
57
59
  * Unified Playcademy configuration