capgo and adk autoupdates. card tags
This commit is contained in:
@@ -1,39 +1,94 @@
|
||||
import { createReadStream, existsSync, statSync } from 'node:fs';
|
||||
import type { IncomingMessage, ServerResponse } from 'node:http';
|
||||
import path from 'node:path';
|
||||
import react from '@vitejs/plugin-react';
|
||||
import { defineConfig, loadEnv, type Plugin, type ProxyOptions } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
import { buildVersionFromDate } from './src/buildVersion.js';
|
||||
|
||||
// Web build: public at https://dev.aggtaa.com/m/, the reverse proxy strips "/m" (X-Forwarded-Prefix).
|
||||
// Capacitor build (--mode capacitor): served from the app bundle root.
|
||||
//
|
||||
// Env (mobile-client/.env or process env):
|
||||
// PORT dev/preview server port (default 10003)
|
||||
// ALLOWED_HOSTS comma-separated public host names (default dev.aggtaa.com)
|
||||
// BACKEND_URL local dev only: server-be address (default http://127.0.0.1:10002)
|
||||
// PORT dev/preview server port (default 10003)
|
||||
// ALLOWED_HOSTS comma-separated public host names (default dev.aggtaa.com)
|
||||
// BACKEND_URL local dev only: server-be address (default http://127.0.0.1:10002)
|
||||
// CARDY_UPDATE_URL where the Android app looks for live updates (default https://dev.aggtaa.com/m/android)
|
||||
// CARDY_BUILD_VERSION override the build version (default: from the build time)
|
||||
const WEB_BASE = '/m/';
|
||||
/** Published Android updates (scripts/publish-android-update.mjs, publish-android-apk.mjs), served at /m/android/. */
|
||||
const UPDATES_DIR = path.resolve('updates/android');
|
||||
const UPDATES_PATH = `${WEB_BASE}android/`;
|
||||
|
||||
type Next = () => void;
|
||||
|
||||
/** The proxy strips /m; put it back so Vite's base-aware dev and preview servers find the files. */
|
||||
function restoreStrippedBase(): Plugin {
|
||||
const rewrite = (req: { url?: string }, _res: unknown, next: () => void) => {
|
||||
const url = req.url ?? '/';
|
||||
if (!url.startsWith(WEB_BASE) && !url.startsWith('/api/') && url !== '/api') req.url = WEB_BASE.slice(0, -1) + url;
|
||||
next();
|
||||
function restoreStrippedBase(req: IncomingMessage, _res: ServerResponse, next: Next) {
|
||||
const url = req.url ?? '/';
|
||||
if (!url.startsWith(WEB_BASE) && !url.startsWith('/api/') && url !== '/api') req.url = WEB_BASE.slice(0, -1) + url;
|
||||
next();
|
||||
}
|
||||
|
||||
/** version.json and bundle zips for the Android app; CORS is open because the app runs on https://localhost. */
|
||||
function serveAndroidUpdates(req: IncomingMessage, res: ServerResponse, next: Next) {
|
||||
const url = (req.url ?? '').split('?')[0] ?? '';
|
||||
if (!url.startsWith(UPDATES_PATH) || (req.method !== 'GET' && req.method !== 'HEAD')) return next();
|
||||
const name = decodeURIComponent(url.slice(UPDATES_PATH.length));
|
||||
const file = path.join(UPDATES_DIR, name);
|
||||
res.setHeader('Access-Control-Allow-Origin', '*');
|
||||
if (!/^[A-Za-z0-9._-]+$/.test(name) || !existsSync(file)) {
|
||||
res.statusCode = 404;
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
const isJson = name.endsWith('.json');
|
||||
res.setHeader(
|
||||
'Content-Type',
|
||||
isJson ? 'application/json' : name.endsWith('.apk') ? 'application/vnd.android.package-archive' : 'application/zip',
|
||||
);
|
||||
if (!isJson) res.setHeader('Content-Length', String(statSync(file).size));
|
||||
// Bundle files are named by version and never change.
|
||||
res.setHeader('Cache-Control', isJson ? 'no-cache' : 'public, max-age=31536000, immutable');
|
||||
if (req.method === 'HEAD') {
|
||||
res.end();
|
||||
return;
|
||||
}
|
||||
createReadStream(file).pipe(res);
|
||||
}
|
||||
|
||||
function cardyServer(): Plugin {
|
||||
const install = (use: (handler: (req: IncomingMessage, res: ServerResponse, next: Next) => void) => void) => {
|
||||
use(restoreStrippedBase);
|
||||
use(serveAndroidUpdates);
|
||||
};
|
||||
return {
|
||||
name: 'cardy-restore-stripped-base',
|
||||
configureServer: (server) => void server.middlewares.use(rewrite),
|
||||
configurePreviewServer: (server) => void server.middlewares.use(rewrite),
|
||||
name: 'cardy-server',
|
||||
configureServer: (server) => install((h) => server.middlewares.use(h)),
|
||||
configurePreviewServer: (server) => install((h) => server.middlewares.use(h)),
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig(({ mode }) => {
|
||||
/** Writes cardy-version.json next to the build: read by the update publishing script. */
|
||||
function emitVersionFile(version: string): Plugin {
|
||||
return {
|
||||
name: 'cardy-version-file',
|
||||
apply: 'build',
|
||||
generateBundle() {
|
||||
this.emitFile({ type: 'asset', fileName: 'cardy-version.json', source: JSON.stringify({ version }) });
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export default defineConfig(({ mode, command }) => {
|
||||
const env = loadEnv(mode, process.cwd(), '');
|
||||
const capacitor = mode === 'capacitor';
|
||||
const buildVersion = process.env.CARDY_BUILD_VERSION || (command === 'build' ? buildVersionFromDate(new Date()) : 'dev');
|
||||
|
||||
// Local development without the reverse proxy: /api/* -> server-be with /api stripped.
|
||||
const proxy: Record<string, ProxyOptions> = {
|
||||
'/api': {
|
||||
target: env.BACKEND_URL || 'http://127.0.0.1:10002',
|
||||
rewrite: (path) => path.replace(/^\/api/, '') || '/',
|
||||
rewrite: (p) => p.replace(/^\/api/, '') || '/',
|
||||
headers: { 'X-Forwarded-Prefix': '/api' },
|
||||
},
|
||||
};
|
||||
@@ -47,9 +102,14 @@ export default defineConfig(({ mode }) => {
|
||||
|
||||
return {
|
||||
base: capacitor ? './' : WEB_BASE,
|
||||
define: {
|
||||
__BUILD_VERSION__: JSON.stringify(buildVersion),
|
||||
__UPDATE_URL__: JSON.stringify((env.CARDY_UPDATE_URL || 'https://dev.aggtaa.com/m/android').replace(/\/+$/, '')),
|
||||
},
|
||||
plugins: [
|
||||
react(),
|
||||
restoreStrippedBase(),
|
||||
cardyServer(),
|
||||
...(capacitor ? [emitVersionFile(buildVersion)] : []),
|
||||
VitePWA({
|
||||
disable: capacitor,
|
||||
registerType: 'autoUpdate',
|
||||
@@ -75,7 +135,7 @@ export default defineConfig(({ mode }) => {
|
||||
// bwip-js and the zxing wasm module are large but needed offline.
|
||||
maximumFileSizeToCacheInBytes: 8 * 1024 * 1024,
|
||||
navigateFallback: 'index.html',
|
||||
navigateFallbackDenylist: [/^\/api(\/|$)/],
|
||||
navigateFallbackDenylist: [/^\/api(\/|$)/, /\/android\//],
|
||||
},
|
||||
}),
|
||||
],
|
||||
|
||||
Reference in New Issue
Block a user