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.
- package/dist/constants.d.ts +2 -0
- package/dist/constants.js +3 -1
- package/dist/db.js +3 -1
- package/dist/edge-play/src/types.ts +10 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +926 -211
- package/dist/templates/api/sample-bucket.ts.template +178 -0
- package/dist/templates/api/sample-custom.ts.template +0 -2
- package/dist/templates/api/sample-database.ts.template +5 -3
- package/dist/templates/api/sample-kv.ts.template +0 -2
- package/dist/templates/api/sample-route-with-db.ts.template +0 -2
- package/dist/templates/api/sample-route.ts.template +0 -1
- package/dist/templates/gitignore.template +3 -3
- package/dist/templates/playcademy-env.d.ts.template +16 -0
- package/dist/templates/playcademy-gitignore.template +4 -0
- package/dist/utils.d.ts +2 -0
- package/dist/utils.js +202 -50
- package/package.json +1 -1
|
@@ -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
|
+
}
|
|
@@ -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
|
-
|
|
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({
|
|
@@ -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
|
+
|
package/dist/utils.d.ts
CHANGED