Files
zpan/server/middleware/require-feature.ts
T
Jasper Van fbec74747e refactor(usecases): consolidate licensing into one file, merge tiny usecases, drop dead code (#434)
Group all license application logic into a single usecases/licensing.ts
(certificate/token verification, binding-state, cloud refresh, license-gated
policy) and collapse small single-purpose usecases that belonged together.

- delete license-entitlement.ts: a write-only cache nobody read (loadEntitlement
  had zero live consumers); remove its invalidate* call-sites
- merge licensing-refresh-runner -> license-refresh, then fold
  license-certificate + license-refresh + license-policy + binding-state into
  one licensing.ts (internal cert<-state<-refresh<-policy edges become in-file)
- merge team-count + signup-mode -> license-policy (then into licensing.ts)
- merge trash-retention -> purge (manual purge + scheduled retention sweep)

Tests follow the source: the runner tests are rewritten against a fake
LicensingCloud port (the old module-spy on performRefresh can't survive a
same-module call), and the team-limit test seeds a real pro license instead of
mocking the licensing module. Net 486+/861-. typecheck clean; unit+integration
green (158 files / 3797 tests).

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 16:42:47 -04:00

27 lines
1.2 KiB
TypeScript

import type { ProFeature } from '@shared/types'
import type { Context } from 'hono'
import { createMiddleware } from 'hono/factory'
import { ZPAN_CLOUD_URL_DEFAULT } from '../../shared/constants'
import { hasFeature } from '../domain/licensing'
import { loadBindingState, normalizeHost } from '../usecases/licensing'
import { getSitePublicOrigin } from '../usecases/site-public-origin'
import type { Env } from './platform'
async function configuredPublicHost(c: Context<Env>): Promise<string | null> {
const origin = await getSitePublicOrigin(c.get('deps'))
return origin ? new URL(origin).host : null
}
export function requireFeature(name: ProFeature) {
return createMiddleware<Env>(async (c, next) => {
const cloudBaseUrl = c.get('platform').getEnv('ZPAN_CLOUD_URL') ?? ZPAN_CLOUD_URL_DEFAULT
const currentHost =
(await configuredPublicHost(c)) ?? normalizeHost(c.req.header('host')) ?? new URL(c.req.url).host
const state = await loadBindingState(c.get('deps'), { currentHost, cloudBaseUrl })
if (!hasFeature(name, state)) {
return c.json({ error: 'feature_not_available', feature: name, upgrade_url: '/settings/billing' }, 402)
}
await next()
})
}