|
13 | 13 | // limitations under the License. |
14 | 14 | // |
15 | 15 |
|
16 | | -import { type IRequest, Router, error, html } from 'itty-router' |
17 | | -import { |
18 | | - deleteBlob as handleBlobDelete, |
19 | | - handleBlobGet, |
20 | | - handleBlobHead, |
21 | | - postBlobFormData as handleUploadFormData |
22 | | -} from './blob' |
| 16 | +import { WorkerEntrypoint } from 'cloudflare:workers' |
| 17 | +import { type IRequestStrict, type RequestHandler, Router, error, html } from 'itty-router' |
| 18 | + |
| 19 | +import { handleBlobDelete, handleBlobGet, handleBlobHead, handleUploadFormData } from './blob' |
23 | 20 | import { cors } from './cors' |
24 | | -import { getImage as handleImageGet } from './image' |
25 | | -import { getVideoMeta as handleVideoMetaGet } from './video' |
| 21 | +import { handleImageGet } from './image' |
| 22 | +import { handleVideoMetaGet } from './video' |
26 | 23 | import { handleSignAbort, handleSignComplete, handleSignCreate } from './sign' |
| 24 | +import { type BlobRequest, type WorkspaceRequest } from './types' |
27 | 25 |
|
28 | 26 | const { preflight, corsify } = cors({ |
29 | 27 | maxAge: 86400 |
30 | 28 | }) |
31 | 29 |
|
32 | | -export default { |
33 | | - async fetch (request, env, ctx): Promise<Response> { |
34 | | - const router = Router<IRequest>({ |
35 | | - before: [preflight], |
36 | | - finally: [corsify] |
37 | | - }) |
| 30 | +const router = Router<IRequestStrict, [Env, ExecutionContext], Response>({ |
| 31 | + before: [preflight], |
| 32 | + finally: [corsify] |
| 33 | +}) |
| 34 | + |
| 35 | +const withWorkspace: RequestHandler<WorkspaceRequest> = (request: WorkspaceRequest) => { |
| 36 | + if (request.params.workspace === undefined || request.params.workspace === '') { |
| 37 | + return error(400, 'Missing workspace') |
| 38 | + } |
| 39 | + request.workspace = decodeURIComponent(request.params.workspace) |
| 40 | +} |
| 41 | + |
| 42 | +const withBlob: RequestHandler<BlobRequest> = (request: BlobRequest) => { |
| 43 | + if (request.params.name === undefined || request.params.name === '') { |
| 44 | + return error(400, 'Missing blob name') |
| 45 | + } |
| 46 | + request.workspace = decodeURIComponent(request.params.name) |
| 47 | +} |
| 48 | + |
| 49 | +router |
| 50 | + .get('/blob/:workspace/:name', withBlob, handleBlobGet) |
| 51 | + .head('/blob/:workspace/:name', withBlob, handleBlobHead) |
| 52 | + .delete('/blob/:workspace/:name', withBlob, handleBlobDelete) |
| 53 | + // Image |
| 54 | + .get('/image/:transform/:workspace/:name', withBlob, handleImageGet) |
| 55 | + // Video |
| 56 | + .get('/video/:workspace/:name/meta', withBlob, handleVideoMetaGet) |
| 57 | + // Form Data |
| 58 | + .post('/upload/form-data/:workspace', withWorkspace, handleUploadFormData) |
| 59 | + // Signed URL |
| 60 | + .post('/upload/signed-url/:workspace/:name', withBlob, handleSignCreate) |
| 61 | + .put('/upload/signed-url/:workspace/:name', withBlob, handleSignComplete) |
| 62 | + .delete('/upload/signed-url/:workspace/:name', withBlob, handleSignAbort) |
| 63 | + .all('/', () => |
| 64 | + html( |
| 65 | + `Huly® Datalake™ <a href="https://huly.io">https://huly.io</a> |
| 66 | + © 2024 <a href="https://hulylabs.com">Huly Labs</a>` |
| 67 | + ) |
| 68 | + ) |
| 69 | + .all('*', () => error(404)) |
| 70 | + |
| 71 | +export default class DatalakeWorker extends WorkerEntrypoint<Env> { |
| 72 | + async fetch (request: Request): Promise<Response> { |
| 73 | + return await router.fetch(request, this.env, this.ctx).catch(error) |
| 74 | + } |
| 75 | + |
| 76 | + async getBlob (workspace: string, name: string): Promise<ArrayBuffer> { |
| 77 | + const request = new Request(`https://datalake/blob/${workspace}/${name}`) |
| 78 | + const response = await router.fetch(request) |
| 79 | + |
| 80 | + if (!response.ok) { |
| 81 | + console.error({ error: 'datalake error: ' + response.statusText, workspace, name }) |
| 82 | + throw new Error(`Failed to fetch blob: ${response.statusText}`) |
| 83 | + } |
| 84 | + |
| 85 | + return await response.arrayBuffer() |
| 86 | + } |
| 87 | + |
| 88 | + async putBlob (workspace: string, name: string, data: ArrayBuffer | Blob | string, type: string): Promise<void> { |
| 89 | + const request = new Request(`https://datalake/upload/form-data/${workspace}`) |
| 90 | + |
| 91 | + const body = new FormData() |
| 92 | + const blob = new Blob([data], { type }) |
| 93 | + body.set('file', blob, name) |
38 | 94 |
|
39 | | - router |
40 | | - .get('/blob/:workspace/:name', ({ params }) => handleBlobGet(request, env, ctx, params.workspace, params.name)) |
41 | | - .head('/blob/:workspace/:name', ({ params }) => handleBlobHead(request, env, ctx, params.workspace, params.name)) |
42 | | - .delete('/blob/:workspace/:name', ({ params }) => handleBlobDelete(env, params.workspace, params.name)) |
43 | | - // Image |
44 | | - .get('/image/:transform/:workspace/:name', ({ params }) => |
45 | | - handleImageGet(request, params.workspace, params.name, params.transform) |
46 | | - ) |
47 | | - // Video |
48 | | - .get('/video/:workspace/:name/meta', ({ params }) => |
49 | | - handleVideoMetaGet(request, env, ctx, params.workspace, params.name) |
50 | | - ) |
51 | | - // Form Data |
52 | | - .post('/upload/form-data/:workspace', ({ params }) => handleUploadFormData(request, env, params.workspace)) |
53 | | - // Signed URL |
54 | | - .post('/upload/signed-url/:workspace/:name', ({ params }) => |
55 | | - handleSignCreate(request, env, ctx, params.workspace, params.name) |
56 | | - ) |
57 | | - .put('/upload/signed-url/:workspace/:name', ({ params }) => |
58 | | - handleSignComplete(request, env, ctx, params.workspace, params.name) |
59 | | - ) |
60 | | - .delete('/upload/signed-url/:workspace/:name', ({ params }) => |
61 | | - handleSignAbort(request, env, ctx, params.workspace, params.name) |
62 | | - ) |
63 | | - .all('/', () => |
64 | | - html( |
65 | | - `Huly® Datalake™ <a href="https://huly.io">https://huly.io</a> |
66 | | - © 2024 <a href="https://hulylabs.com">Huly Labs</a>` |
67 | | - ) |
68 | | - ) |
69 | | - .all('*', () => error(404)) |
| 95 | + const response = await router.fetch(request, { method: 'POST', body }) |
70 | 96 |
|
71 | | - return await router.fetch(request).catch(error) |
| 97 | + if (!response.ok) { |
| 98 | + console.error({ error: 'datalake error: ' + response.statusText, workspace, name }) |
| 99 | + throw new Error(`Failed to fetch blob: ${response.statusText}`) |
| 100 | + } |
72 | 101 | } |
73 | | -} satisfies ExportedHandler<Env> |
| 102 | +} |
0 commit comments