|
1 | 1 | import { Hono } from 'hono' |
2 | | -import { createStorageFromEnv } from './storage/index.js' |
3 | | -import type { StorageAdapter } from './storage/index.js' |
4 | | -import { LocalStorageAdapter } from './storage/index.js' |
| 2 | +import type { AppContext } from './types/app.js'; |
| 3 | +import { createStorageFromEnv, LocalStorageAdapter, type StorageAdapter } from './storage/index.js' |
5 | 4 | import extensionsRouter from './routes/extensions.js' |
6 | | -import { getMimeType } from './utils/mime.js' |
7 | | - |
8 | | -const storage = createStorageFromEnv() |
9 | | - |
10 | | -type AppContext = { |
11 | | - Variables: { |
12 | | - storage: StorageAdapter |
13 | | - } |
14 | | -} |
| 5 | +import localStorageRouter from './routes/storage.js' |
15 | 6 |
|
16 | 7 | const app = new Hono<AppContext>() |
| 8 | +const storage = createStorageFromEnv(); |
17 | 9 |
|
18 | 10 | app.use('*', async (c, next) => { |
19 | 11 | c.set('storage', storage) |
20 | 12 | await next() |
21 | 13 | }) |
22 | 14 |
|
23 | 15 | app.get('/', (c) => { |
24 | | - return c.json({ message: 'Vicinae Extension Store API TEST' }) |
| 16 | + return c.json({ message: 'Vicinae Backend' }) |
25 | 17 | }) |
26 | 18 |
|
27 | | -if (storage instanceof LocalStorageAdapter) { |
28 | | - app.get('/storage/*', async (c) => { |
29 | | - try { |
30 | | - const path = c.req.path.replace('/storage/', ''); |
31 | | - const file = await storage.get(path); |
32 | | - |
33 | | - const contentType = getMimeType(path); |
34 | | - const filename = path.split('/').pop(); |
35 | | - |
36 | | - // For images and markdown, use inline display instead of attachment |
37 | | - const isInline = contentType.startsWith('image/') || contentType === 'text/markdown'; |
38 | | - |
39 | | - // Compute ETag from file buffer for cache validation |
40 | | - const crypto = await import('crypto'); |
41 | | - const hash = crypto.createHash('md5').update(file).digest('hex'); |
42 | | - const etag = `"${hash}"`; |
43 | | - |
44 | | - // Check if client has cached version |
45 | | - const ifNoneMatch = c.req.header('if-none-match'); |
46 | | - if (ifNoneMatch === etag) { |
47 | | - return new Response(null, { status: 304 }); |
48 | | - } |
| 19 | +app.route('/', extensionsRouter) |
49 | 20 |
|
50 | | - return new Response(file, { |
51 | | - headers: { |
52 | | - 'Content-Type': contentType, |
53 | | - 'Content-Disposition': isInline |
54 | | - ? `inline; filename="${filename}"` |
55 | | - : `attachment; filename="${filename}"`, |
56 | | - // Cache for 1 year since files are content-addressed (path includes version) |
57 | | - 'Cache-Control': 'public, max-age=31536000, immutable', |
58 | | - 'ETag': etag, |
59 | | - }, |
60 | | - }); |
61 | | - } catch (error) { |
62 | | - return c.json({ error: 'File not found' }, 404); |
63 | | - } |
64 | | - }); |
| 21 | +if (storage instanceof LocalStorageAdapter) { |
| 22 | + app.route('/', localStorageRouter); |
65 | 23 | } |
66 | 24 |
|
67 | | -app.route('/', extensionsRouter) |
68 | | - |
69 | 25 | export default app |
0 commit comments