mirror of
https://github.com/djdevin/recflare.git
synced 2026-10-25 23:18:51 -07:00
[studio] Complete editor uploads and queued cloud builds (#135)
* [studio] Complete editor uploads and queued cloud builds * [studio] Protect unpublished source and remove unused notification RPC
This commit is contained in:
+18
-4
@@ -353,6 +353,22 @@ const app = new Hono<App>()
|
||||
(c) => serveAsset(c, `avatar/${c.req.param('asset')}`)
|
||||
)
|
||||
|
||||
// Editable Unity source manifests. Baked bundles are served separately.
|
||||
.get(
|
||||
'/roomunityasset/:filename{.+}',
|
||||
describeRoute({
|
||||
tags: ['Assets'],
|
||||
summary: 'Download a Studio authoring manifest',
|
||||
parameters: [keyParam('filename', 'Uploaded JSON source manifest', true)],
|
||||
responses: assetResponses('The source manifest'),
|
||||
}),
|
||||
async (c) => {
|
||||
const name = c.req.param('filename')
|
||||
if (name.includes('..')) return c.body(null, 400)
|
||||
return serveAsset(c, `roomunityasset/${name}`)
|
||||
}
|
||||
)
|
||||
|
||||
// Rec Room Studio room bundles. The filename is the one a room save or
|
||||
// `GET …/unityasset` advertises — the R2 key itself (`studio/<roomId>/…`), as
|
||||
// `unity_asset.filename` records it. Only a recorded filename is served, so this
|
||||
@@ -426,16 +442,14 @@ async function serveUnityAsset(c: Context<App>) {
|
||||
const name = c.req.param('filename') ?? ''
|
||||
if (name.includes('..')) return c.body(null, 400)
|
||||
const key = await unityAssetR2Key(c.env.DB, name)
|
||||
if (!key) return c.notFound()
|
||||
return serveAsset(c, key)
|
||||
return serveAsset(c, key ?? `unityasset/${name}`)
|
||||
}
|
||||
|
||||
/** Build the Studio bundle HEAD response outside Hono's HEAD-to-GET fallback. */
|
||||
async function studioUnityAssetHead(env: StudioAssetEnv, name: string): Promise<Response> {
|
||||
if (name.includes('..')) return new Response(null, { status: 400 })
|
||||
const key = await unityAssetR2Key(env.DB, name)
|
||||
if (!key) return new Response(null, { status: 404 })
|
||||
const head = await env.CDN_ASSETS.head(key)
|
||||
const head = await env.CDN_ASSETS.head(key ?? `unityasset/${name}`)
|
||||
if (!head) return new Response(null, { status: 404 })
|
||||
|
||||
const headers = new Headers()
|
||||
|
||||
@@ -362,6 +362,7 @@ describe('cdn endpoints', () => {
|
||||
'GET /data/{id}',
|
||||
'GET /invention/{dataBlob}',
|
||||
'GET /room/{dataBlob}',
|
||||
'GET /roomunityasset/{filename}',
|
||||
'GET /sigs/{sigName}',
|
||||
'GET /unityasset/{filename}',
|
||||
'HEAD /unityasset/{filename}',
|
||||
@@ -377,3 +378,27 @@ describe('cdn endpoints', () => {
|
||||
expect(JSON.stringify(spec).includes('"$ref"')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
test('Studio authoring source manifests and source files remain distinct from baked bundles', async () => {
|
||||
await env.CDN_ASSETS.put(
|
||||
'roomunityasset/studio-source-fixture.json',
|
||||
'{"Version":3,"Files":[]}',
|
||||
{ httpMetadata: { contentType: 'application/json' } }
|
||||
)
|
||||
const source = await exports.default.fetch(`${ORIGIN}/roomunityasset/studio-source-fixture.json`)
|
||||
expect(source.status).toBe(200)
|
||||
expect(await source.json()).toEqual({ Version: 3, Files: [] })
|
||||
await env.CDN_ASSETS.put('unityasset/studio-source-fixture.txt', 'source data')
|
||||
const file = await exports.default.fetch(`${ORIGIN}/unityasset/studio-source-fixture.txt`)
|
||||
expect(await file.text()).toBe('source data')
|
||||
expect(
|
||||
(
|
||||
await exports.default.fetch(`${ORIGIN}/unityasset/studio-source-fixture.txt`, {
|
||||
method: 'HEAD',
|
||||
})
|
||||
).status
|
||||
).toBe(200)
|
||||
expect((await exports.default.fetch(`${ORIGIN}/roomunityasset/missing-source.json`)).status).toBe(
|
||||
404
|
||||
)
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user