Files
zpan/server/app.ts
T

183 lines
7.4 KiB
TypeScript

import { Hono } from 'hono'
import { cors } from 'hono/cors'
import type { Auth } from './auth'
import { authMiddleware } from './middleware/auth'
import { imageHostingDomain } from './middleware/image-hosting-domain'
import { accessLog } from './middleware/logger'
import type { Env } from './middleware/platform'
import { platformMiddleware } from './middleware/platform'
import { downloaderOpenAPIDocument } from './openapi/downloader'
import type { Platform } from './platform/interface'
import { adminAnnouncements, announcements } from './routes/announcements'
import { adminAudit } from './routes/audit'
import { adminAuthProviders, publicAuthProviders } from './routes/auth-providers'
import backgroundJobs from './routes/background-jobs'
import { brandingAdmin, publicBranding } from './routes/branding'
import { cloudStore, cloudStoreWebhooks } from './routes/cloud-store'
import downloadTasks from './routes/download-tasks'
import downloaders, { downloaderSelfRoute } from './routes/downloaders'
import emailConfig from './routes/email-config'
import ihost from './routes/ihost'
import ihostConfig from './routes/ihost-config'
import internal from './routes/internal'
import { adminInviteCodes, publicInviteCodes } from './routes/invite-codes'
import licensing from './routes/licensing'
import licensingAdmin from './routes/licensing-admin'
import { me } from './routes/me'
import { notifications } from './routes/notifications'
import objects from './routes/objects'
import profile from './routes/profile'
import { adminQuotas, userQuotas } from './routes/quotas'
import redirect from './routes/redirect'
import { authedShares, publicShares } from './routes/shares'
import { adminSiteInvitations, publicSiteInvitations } from './routes/site-invitations'
import storages from './routes/storages'
import system from './routes/system'
import { publicTeams, teams } from './routes/teams'
import trash from './routes/trash'
import users from './routes/users'
import webdav from './routes/webdav'
export function createApp(platform: Platform, auth: Auth) {
const app = new Hono<Env>()
const corsOrigins = getCorsOrigins(platform)
app.use('/*', platformMiddleware(platform, auth))
app.use('/*', imageHostingDomain)
app.use('/api/*', accessLog)
app.use('/dav', accessLog)
app.use('/dav/*', accessLog)
app.use(
'/api/*',
cors({
origin: (origin) => (origin && corsOrigins.has(origin) ? origin : null),
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
credentials: true,
}),
)
app.on(['POST', 'GET'], '/api/auth/*', async (c) => {
const a = c.get('auth')
return a.handler(c.req.raw)
})
app.get('/api/openapi/downloader.json', (c) => c.json(downloaderOpenAPIDocument()))
app.all('/dav', (c) => c.redirect('/dav/', 308))
app.route('/dav', webdav)
// Public routes — no auth required; mount before authMiddleware.
// /api/shares/:token endpoints are covered by run_worker_first=["/api/*"] in wrangler.toml.
// /r/* is listed separately in run_worker_first.
// /s/:token is intentionally left for the SPA landing page.
app.route('/api/shares', publicShares)
app.route('/r', redirect)
app.route('/api/profiles', profile)
app.route('/api/teams', publicTeams)
app.route('/api/auth-providers', publicAuthProviders)
app.route('/api/licensing', licensing)
app.route('/api/branding', publicBranding)
app.route('/api/site-invitations', publicSiteInvitations)
app.route('/api/store', cloudStoreWebhooks)
app.route('/api/internal', internal)
app.use('/api/*', authMiddleware)
app.route('/api/me', me)
app.route('/api/announcements', announcements)
// Mount routes separately to avoid deep type chain accumulation.
// Each .route() call is independent — TypeScript doesn't stack types.
app.route('/api/objects', objects)
app.route('/api/shares', authedShares)
app.route('/api/trash', trash)
app.route('/api/teams', teams)
app.route('/api/admin/storages', storages)
app.route('/api/admin/users', users)
app.route('/api/admin/email-config', emailConfig)
app.route('/api/admin/invite-codes', adminInviteCodes)
app.route('/api/invite-codes', publicInviteCodes)
app.route('/api/admin/site-invitations', adminSiteInvitations)
app.route('/api/admin/quotas', adminQuotas)
app.route('/api/quotas', userQuotas)
app.route('/api/store', cloudStore)
app.route('/api/system', system)
app.route('/api/admin/auth-providers', adminAuthProviders)
app.route('/api/notifications', notifications)
app.route('/api/background-jobs', backgroundJobs)
app.route('/api/download-tasks', downloadTasks)
app.route('/api/downloader', downloaderSelfRoute)
app.route('/api/ihost', ihost)
app.route('/api/ihost/config', ihostConfig)
app.route('/api/licensing', licensingAdmin)
app.route('/api/admin/branding', brandingAdmin)
app.route('/api/admin/announcements', adminAnnouncements)
app.route('/api/admin/audit', adminAudit)
app.route('/api/admin/downloaders', downloaders)
app.get('/api/health', (c) => c.json({ status: 'ok' }))
return app
}
function getCorsOrigins(platform: Platform): Set<string> {
const origins = new Set<string>()
const addOrigin = (value: string | undefined) => {
if (!value) return
try {
origins.add(new URL(value).origin)
} catch {
origins.add(value)
}
}
addOrigin(platform.getEnv('BETTER_AUTH_URL'))
for (const origin of platform.getEnv('TRUSTED_ORIGINS')?.split(',') ?? []) {
addOrigin(origin.trim())
}
return origins
}
export type AppType = ReturnType<typeof createApp>
// Sub-router types for RPC clients — avoids combined AppType OOM
export type ObjectsRoute = typeof objects
export type PublicSharesRoute = typeof publicShares
export type AuthedSharesRoute = typeof authedShares
export type TrashRoute = typeof trash
export type StoragesRoute = typeof storages
export type UsersRoute = typeof users
export type AdminQuotasRoute = typeof adminQuotas
export type UserQuotasRoute = typeof userQuotas
export type SystemRoute = typeof system
export type EmailConfigRoute = typeof emailConfig
export type AdminInviteCodesRoute = typeof adminInviteCodes
export type PublicInviteCodesRoute = typeof publicInviteCodes
export type AdminSiteInvitationsRoute = typeof adminSiteInvitations
export type PublicSiteInvitationsRoute = typeof publicSiteInvitations
export type AuthProvidersRoute = typeof publicAuthProviders
export type AdminAuthProvidersRoute = typeof adminAuthProviders
export type ProfileRoute = typeof profile
export type CloudStoreRoute = typeof cloudStore
export type CloudStoreWebhooksRoute = typeof cloudStoreWebhooks
export type TeamsRoute = typeof teams
export type PublicTeamsRoute = typeof publicTeams
export type NotificationsRoute = typeof notifications
export type BackgroundJobsRoute = typeof backgroundJobs
export type DownloadTasksRoute = typeof downloadTasks
export type DownloadersRoute = typeof downloaders
export type DownloaderSelfRoute = typeof downloaderSelfRoute
export type IhostRoute = typeof ihost
export type IhostConfigRoute = typeof ihostConfig
export type MeRoute = typeof me
export type AnnouncementsRoute = typeof announcements
export type AdminAnnouncementsRoute = typeof adminAnnouncements
export type LicensingRoute = typeof licensing
export type LicensingAdminRoute = typeof licensingAdmin
export type PublicBrandingRoute = typeof publicBranding
export type BrandingAdminRoute = typeof brandingAdmin
export type AdminAuditRoute = typeof adminAudit