|
1 | | -import { serve } from '@hono/node-server'; |
2 | | -import { serveStatic } from '@hono/node-server/serve-static'; |
3 | | -import { Hono } from 'hono'; |
4 | | -import { cors } from 'hono/cors'; |
5 | | -import { logger } from 'hono/logger'; |
6 | | -import { ObjectStackRuntimeProtocol } from '@objectstack/runtime'; |
7 | | -import { RuntimePlugin, IKernel } from '@objectstack/types'; |
8 | | - |
9 | | -export interface HonoServerOptions { |
10 | | - port?: number; |
11 | | - staticRoot?: string; |
12 | | - cors?: boolean; |
13 | | - logger?: boolean; |
14 | | -} |
15 | | - |
16 | | -/** |
17 | | - * Hono Server Runtime Plugin |
18 | | - * |
19 | | - * Exposes the ObjectStack Kernel via standard HTTP Protocol using Hono. |
20 | | - * Can be used for Production (Standalone) or Development. |
21 | | - */ |
22 | | -export class HonoServerPlugin implements RuntimePlugin { |
23 | | - name = 'com.objectstack.server.hono'; |
24 | | - |
25 | | - private options: HonoServerOptions; |
26 | | - |
27 | | - constructor(options: HonoServerOptions = {}) { |
28 | | - this.options = { |
29 | | - port: 3000, |
30 | | - cors: true, |
31 | | - logger: true, |
32 | | - ...options |
33 | | - }; |
34 | | - } |
35 | | - |
36 | | - async onStart(ctx: { engine: IKernel }) { |
37 | | - const app = new Hono(); |
38 | | - // TODO: Protocol needs access to actual Kernel instance or we make Protocol an interface too |
39 | | - const protocol = new ObjectStackRuntimeProtocol(ctx.engine as any); |
40 | | - |
41 | | - // 1. Middlewares |
42 | | - if (this.options.logger) app.use('*', logger()); |
43 | | - if (this.options.cors) app.use('*', cors()); |
44 | | - |
45 | | - // 2. Wiring Protocol (Automatic) |
46 | | - // Discovery |
47 | | - app.get('/api/v1', (c) => c.json(protocol.getDiscovery())); |
48 | | - |
49 | | - // Meta Protocol |
50 | | - app.get('/api/v1/meta', (c) => c.json(protocol.getMetaTypes())); |
51 | | - app.get('/api/v1/meta/:type', (c) => c.json(protocol.getMetaItems(c.req.param('type')))); |
52 | | - app.get('/api/v1/meta/:type/:name', (c) => { |
53 | | - try { |
54 | | - return c.json(protocol.getMetaItem(c.req.param('type'), c.req.param('name'))); |
55 | | - } catch(e:any) { |
56 | | - return c.json({error: e.message}, 404); |
57 | | - } |
58 | | - }); |
59 | | - |
60 | | - // Data Protocol |
61 | | - app.get('/api/v1/data/:object', async (c) => { |
62 | | - try { return c.json(await protocol.findData(c.req.param('object'), c.req.query())); } |
63 | | - catch(e:any) { return c.json({error:e.message}, 400); } |
64 | | - }); |
65 | | - app.get('/api/v1/data/:object/:id', async (c) => { |
66 | | - try { return c.json(await protocol.getData(c.req.param('object'), c.req.param('id'))); } |
67 | | - catch(e:any) { return c.json({error:e.message}, 404); } |
68 | | - }); |
69 | | - app.post('/api/v1/data/:object', async (c) => { |
70 | | - try { return c.json(await protocol.createData(c.req.param('object'), await c.req.json()), 201); } |
71 | | - catch(e:any) { return c.json({error:e.message}, 400); } |
72 | | - }); |
73 | | - app.patch('/api/v1/data/:object/:id', async (c) => { |
74 | | - try { return c.json(await protocol.updateData(c.req.param('object'), c.req.param('id'), await c.req.json())); } |
75 | | - catch(e:any) { return c.json({error:e.message}, 400); } |
76 | | - }); |
77 | | - app.delete('/api/v1/data/:object/:id', async (c) => { |
78 | | - try { return c.json(await protocol.deleteData(c.req.param('object'), c.req.param('id'))); } |
79 | | - catch(e:any) { return c.json({error:e.message}, 400); } |
80 | | - }); |
81 | | - |
82 | | - // UI Protocol |
83 | | - // @ts-ignore |
84 | | - app.get('/api/v1/ui/view/:object', (c) => { |
85 | | - try { |
86 | | - const viewType = (c.req.query('type') as 'list' | 'form') || 'list'; |
87 | | - return c.json(protocol.getUiView(c.req.param('object'), viewType)); |
88 | | - } |
89 | | - catch(e:any) { return c.json({error:e.message}, 404); } |
90 | | - }); |
91 | | - |
92 | | - // 3. Static Files (Optional) |
93 | | - if (this.options.staticRoot) { |
94 | | - app.get('/', serveStatic({ root: this.options.staticRoot, path: 'index.html' })); |
95 | | - app.get('/*', serveStatic({ root: this.options.staticRoot })); |
96 | | - } |
97 | | - |
98 | | - console.log(''); |
99 | | - console.log(`🌍 ObjectStack Server (Hono) running at: http://localhost:${this.options.port}`); |
100 | | - console.log(''); |
101 | | - |
102 | | - serve({ fetch: app.fetch, port: this.options.port }); |
103 | | - } |
104 | | -} |
| 1 | +export * from './hono-plugin'; |
0 commit comments