feat: introduce mnie
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
FROM oven/bun:1 AS deps
|
||||
WORKDIR /app
|
||||
COPY package.json bun.lock tsconfig.json vite.config.ts ./
|
||||
COPY packages/client-sbi/package.json packages/client-sbi/package.json
|
||||
COPY packages/mnie-types/package.json packages/mnie-types/package.json
|
||||
COPY packages/mnie-sdk/package.json packages/mnie-sdk/package.json
|
||||
COPY packages/mnie-cli/package.json packages/mnie-cli/package.json
|
||||
COPY apps/mnie-app/package.json apps/mnie-app/package.json
|
||||
COPY apps/mnie-server/package.json apps/mnie-server/package.json
|
||||
COPY apps/mnie-ui/package.json apps/mnie-ui/package.json
|
||||
RUN bun install --frozen-lockfile
|
||||
|
||||
FROM deps AS build
|
||||
COPY . .
|
||||
RUN bun --filter @repo/mnie-app build
|
||||
|
||||
FROM oven/bun:1-slim AS runtime
|
||||
ENV NODE_ENV=production
|
||||
ENV MNIE_DATABASE_PATH=/app/data/mnie-app.sqlite
|
||||
ENV MNIE_KEYRING_BACKEND=sqlite
|
||||
ENV MNIE_KEYRING_SQLITE_PATH=/app/data/mnie-app.keyring.sqlite
|
||||
WORKDIR /app
|
||||
COPY --from=build /app/apps/mnie-app/dist ./apps/mnie-app/dist
|
||||
COPY --from=build /app/apps/mnie-ui/dist ./apps/mnie-ui/dist
|
||||
COPY --from=build /app/node_modules/.bun/@napi-rs+keyring@*/node_modules/@napi-rs/keyring ./node_modules/@napi-rs/keyring
|
||||
COPY --from=build /app/node_modules/.bun/@napi-rs+keyring-linux-x64-gnu@*/node_modules/@napi-rs/keyring-linux-x64-gnu ./node_modules/@napi-rs/keyring-linux-x64-gnu
|
||||
WORKDIR /app/apps/mnie-app/dist
|
||||
EXPOSE 8787
|
||||
CMD ["bun", "index.js"]
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"name": "@repo/mnie-app",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"clean": "rm -rf dist ../mnie-ui/dist",
|
||||
"dev": "bun --env-file=../../.env src/dev.ts",
|
||||
"build": "bun --filter @repo/mnie-ui build && bun build src/index.ts --target=bun --outdir=dist --external @napi-rs/keyring",
|
||||
"start": "bun --env-file=../../.env dist/index.js",
|
||||
"typecheck": "tsc"
|
||||
},
|
||||
"dependencies": {
|
||||
"@repo/mnie-server": "workspace:*",
|
||||
"hono": "^4.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/bun": "latest",
|
||||
"typescript": "^5"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
const uiPort = Number(process.env.MNIE_UI_DEV_PORT ?? 5173)
|
||||
const serverPort = Number(process.env.PORT ?? process.env.MNIE_SERVER_PORT ?? 8787)
|
||||
const uiOrigin = `http://127.0.0.1:${uiPort}`
|
||||
const serverEntry = new URL('../../mnie-server/src/index.ts', import.meta.url).pathname
|
||||
|
||||
const ui = Bun.spawn({
|
||||
cmd: ['bun', '--filter', '@repo/mnie-ui', 'dev'],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
stdin: 'inherit',
|
||||
env: process.env,
|
||||
})
|
||||
|
||||
const server = Bun.spawn({
|
||||
cmd: ['bun', '--watch', serverEntry],
|
||||
stdout: 'inherit',
|
||||
stderr: 'inherit',
|
||||
stdin: 'inherit',
|
||||
env: {
|
||||
...process.env,
|
||||
PORT: String(serverPort),
|
||||
MNIE_ORIGIN: uiOrigin,
|
||||
MNIE_CORS_ORIGIN: uiOrigin,
|
||||
},
|
||||
})
|
||||
|
||||
const shutdown = () => {
|
||||
ui.kill()
|
||||
server.kill()
|
||||
}
|
||||
|
||||
process.on('SIGINT', shutdown)
|
||||
process.on('SIGTERM', shutdown)
|
||||
|
||||
const appExit = await server.exited
|
||||
shutdown()
|
||||
process.exit(appExit)
|
||||
@@ -0,0 +1,66 @@
|
||||
import { existsSync } from 'node:fs'
|
||||
import { extname, isAbsolute, join, relative, resolve } from 'node:path'
|
||||
import { Hono } from 'hono'
|
||||
import { createServerApp } from '@repo/mnie-server/app'
|
||||
import { loadConfig } from '@repo/mnie-server/config'
|
||||
import { createDb } from '@repo/mnie-server/db'
|
||||
|
||||
const config = loadConfig()
|
||||
const db = createDb(config.databasePath)
|
||||
const api = createServerApp(db, config)
|
||||
const app = new Hono()
|
||||
const uiDist = resolve(import.meta.dir, '../../mnie-ui/dist')
|
||||
|
||||
const contentTypes: Record<string, string> = {
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.js': 'text/javascript; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.svg': 'image/svg+xml',
|
||||
'.ico': 'image/x-icon',
|
||||
}
|
||||
|
||||
app.route('/api', api.app)
|
||||
app.route('/', api.app)
|
||||
|
||||
const isInsideUiDist = (path: string) => {
|
||||
const pathFromUiDist = relative(uiDist, path)
|
||||
return pathFromUiDist === '' || (!pathFromUiDist.startsWith('..') && !isAbsolute(pathFromUiDist))
|
||||
}
|
||||
|
||||
const serveUi = async (request: Request) => {
|
||||
const url = new URL(request.url)
|
||||
let pathname: string
|
||||
try {
|
||||
pathname = decodeURIComponent(url.pathname)
|
||||
} catch {
|
||||
return new Response('Bad Request', { status: 400 })
|
||||
}
|
||||
const relativePath = pathname === '/' ? 'index.html' : pathname.slice(1)
|
||||
const filePath = resolve(uiDist, relativePath)
|
||||
if (!isInsideUiDist(filePath)) return new Response('Not Found', { status: 404 })
|
||||
const resolvedPath = existsSync(filePath) ? filePath : join(uiDist, 'index.html')
|
||||
const file = Bun.file(resolvedPath)
|
||||
const headers = new Headers()
|
||||
headers.set('content-type', contentTypes[extname(resolvedPath)] ?? 'application/octet-stream')
|
||||
return new Response(file, { headers })
|
||||
}
|
||||
|
||||
const server = Bun.serve({
|
||||
port: config.port,
|
||||
fetch(request, server) {
|
||||
const url = new URL(request.url)
|
||||
if (
|
||||
url.pathname.startsWith('/api/') ||
|
||||
url.pathname.startsWith('/.well-known/') ||
|
||||
url.pathname.startsWith('/oauth/') ||
|
||||
['/authorize', '/token', '/register', '/revoke'].includes(url.pathname)
|
||||
) {
|
||||
return app.fetch(request, { server })
|
||||
}
|
||||
return serveUi(request)
|
||||
},
|
||||
websocket: api.websocket,
|
||||
})
|
||||
|
||||
console.log(`mnie listening on http://localhost:${server.port}`)
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"types": ["bun"],
|
||||
"rootDir": "src",
|
||||
"outDir": "dist",
|
||||
"noEmit": true
|
||||
},
|
||||
"include": ["src/**/*.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user