Files
zpan/server/entry-azure.ts
T
Jasper VanandBob d33800f23e feat: Azure Functions deployment target (v4, Node 22) (#330)
* feat: add Azure Functions deployment target (v4, Node 22)

- server/entry-azure.ts: Azure Functions v4 handler wrapping the Hono
  app via app.http(); uses createLibsqlPlatform for Turso and serves
  the SPA from ./dist via @hono/node-server/serve-static
- server/azure-host.json: runtime manifest (extensionBundle v4)
- deploy/azure-functions/main.bicep: idempotent Bicep template
  provisioning Storage Account, Consumption plan and Function App;
  BETTER_AUTH_SECRET handled separately by the workflow
- .github/workflows/deploy-azure.yml: 8-step workflow (secret check,
  checkout, Node setup, az login, Bicep deploy, build, db:migrate,
  func publish) with BETTER_AUTH_SECRET generate-if-missing logic
- package.json: build:azure script + @azure/functions dependency
- docs/deploy/azure-functions.md: setup guide covering SP JSON format,
  required secrets, and local emulation with func start

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* fix: address review issues in Azure Functions deploy

- Move BETTER_AUTH_SECRET and APP_URL setup to before func publish
  (bootstrap.ts throws on missing secret; any request between publish
  and the old secret-set step would have returned 500)
- Remove placeholder appUrl Bicep param; workflow sets APP_URL and
  BETTER_AUTH_URL via appsettings after Bicep, before publish
- Fix HttpRequest→Request body handling: construct a proper Web API
  Request with body cast and duplex option instead of double-casting
  HttpRequest, ensuring POST/PUT/PATCH body-reading routes work
- Add push: branches: [master] trigger + upstream guard to match other
  deploy workflow conventions; document the auto-deploy behaviour
- Update docs/deploy/azure-functions.md to reflect the push trigger

Agent-Profile: https://agent-kanban.dev/agents/a6bb038c4226a87f

* ci: re-trigger CI for review fixes

---------

Co-authored-by: Bob <aibob@mails.agent-kanban.dev>
2026-04-22 02:08:54 -04:00

44 lines
1.7 KiB
TypeScript

import type { HttpRequest, InvocationContext } from '@azure/functions'
import { app } from '@azure/functions'
import { serveStatic } from '@hono/node-server/serve-static'
import { Hono } from 'hono'
import { createBootstrap } from './bootstrap'
import { createLibsqlPlatform } from './platform/libsql'
const TURSO_DATABASE_URL = process.env.TURSO_DATABASE_URL
if (!TURSO_DATABASE_URL) {
throw new Error('TURSO_DATABASE_URL is required for the Azure Functions deployment.')
}
const platform = await createLibsqlPlatform({
TURSO_DATABASE_URL,
TURSO_AUTH_TOKEN: process.env.TURSO_AUTH_TOKEN,
})
const apiApp = await createBootstrap(platform)
const server = new Hono()
server.route('/', apiApp)
server.use('/*', serveStatic({ root: './dist' }))
server.get('/*', serveStatic({ root: './dist', path: 'index.html' }))
app.http('zpan', {
methods: ['DELETE', 'GET', 'HEAD', 'OPTIONS', 'PATCH', 'POST', 'PUT'],
authLevel: 'anonymous',
route: '{*path}',
handler: async (request: HttpRequest, _context: InvocationContext): Promise<Response> => {
// Construct a standards-compliant Request so Hono body-reading works correctly
// on POST/PUT/PATCH routes. Azure HttpRequest v4 has additional properties and
// a single-read stream; wrapping it avoids silent breakage on body-reading routes.
// The body cast and duplex option are required for Node 22's undici-based fetch.
const hasBody = request.method !== 'GET' && request.method !== 'HEAD'
const webReq = new Request(request.url, {
method: request.method,
headers: request.headers,
body: hasBody ? (request.body as BodyInit) : undefined,
...(hasBody ? { duplex: 'half' } : {}),
} as RequestInit)
return server.fetch(webReq)
},
})