Files
zpan/vite.config.ts
T
saltbo e700fd4977 feat(about): add changelog drawer, latest-version check, and commit hash
Maintain a CHANGELOG.md (Keep a Changelog format) at the repo root and surface
it on the admin About page:

- The page now shows the running build's short commit hash next to the version,
  linked to the GitHub commit. Commit is injected at build time via a new
  resolveAppCommit() (ZPAN_APP_COMMIT -> WORKERS_CI_COMMIT_SHA -> git rev-parse),
  wired through vite/tsup defines, the node entry, Docker, and CI.
- A new admin-only GET /api/system/changelog endpoint fetches CHANGELOG.md from
  master on GitHub, caches it, parses the latest released version, and reports
  whether an update is available (semver compare against the running version).
- The About page renders a "latest version" row with an update-available badge
  and a side drawer that displays the changelog markdown.

Tests cover the semver compare, changelog parse/fetch caching, the API wrapper,
and the route (admin-gated, parsed payload).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-09 13:38:29 -04:00

69 lines
2.0 KiB
TypeScript

import path from 'node:path'
import { cloudflare } from '@cloudflare/vite-plugin'
import tailwindcss from '@tailwindcss/vite'
import { TanStackRouterVite } from '@tanstack/router-plugin/vite'
import react from '@vitejs/plugin-react-swc'
import { defineConfig } from 'vite'
import { resolveAppCommit, resolveAppVersion } from './scripts/app-version.mjs'
const appPort = Number(process.env.E2E_APP_PORT ?? 5185)
const apiPort = Number(process.env.E2E_API_PORT ?? 8222)
const appVersion = resolveAppVersion()
const appCommit = resolveAppCommit()
export default defineConfig(({ mode }) => ({
define: {
'globalThis.__ZPAN_APP_VERSION__': JSON.stringify(appVersion),
'globalThis.__ZPAN_APP_COMMIT__': JSON.stringify(appCommit),
},
build: {
outDir: 'dist',
emptyOutDir: true,
},
environments:
mode === 'node'
? undefined
: {
zpan: {
optimizeDeps: {
exclude: ['@aws-sdk/client-s3', '@aws-sdk/s3-request-presigner'],
},
resolve: {
conditions: ['browser', 'workerd', 'worker', 'module', 'development|production'],
mainFields: ['browser', 'module', 'jsnext:main', 'jsnext'],
},
},
},
plugins: [
TanStackRouterVite({
routesDirectory: './src/routes',
generatedRouteTree: './src/routeTree.gen.ts',
routeFileIgnorePattern: '.test.ts',
}),
react(),
tailwindcss(),
...(mode === 'node' ? [] : [cloudflare()]),
],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@shared': path.resolve(__dirname, './shared'),
'@server': path.resolve(__dirname, './server'),
},
},
server: {
port: appPort,
allowedHosts: process.env.E2E_BASE_URL ? true : undefined,
...(mode === 'node'
? {
proxy: {
'/api': {
target: `http://localhost:${apiPort}`,
changeOrigin: true,
},
},
}
: {}),
},
}))