mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
feat: add a desktop build of the webmail that no server build can switch on
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
#!/usr/bin/env node
|
||||
// The webmail build the desktop app embeds: `npm run build:desktop`. It's the
|
||||
// regular standalone build with NEXT_PUBLIC_BULWARK_DESKTOP compiled in, using
|
||||
// webpack (a third of Turbopack's server size, and its hashed external links
|
||||
// don't relocate). No other build sets the flag: repos/desktop's
|
||||
// scripts/fetch-webmail.mjs refuses a tree without it.
|
||||
|
||||
import { spawnSync } from 'node:child_process';
|
||||
|
||||
const env = { ...process.env, NEXT_PUBLIC_BULWARK_DESKTOP: '1', NEXT_TELEMETRY_DISABLED: '1' };
|
||||
const isWindows = process.platform === 'win32';
|
||||
|
||||
for (const [command, ...args] of [
|
||||
['node', 'scripts/build-pst-worker.mjs'],
|
||||
['node', 'scripts/build-store-worker.mjs'],
|
||||
['node', 'scripts/build-plugin-sandbox.mjs'],
|
||||
['npx', 'next', 'build', '--webpack'],
|
||||
['node', 'scripts/standalone-guard.mjs'],
|
||||
]) {
|
||||
const run = spawnSync(command, args, { env, stdio: 'inherit', shell: isWindows });
|
||||
if (run.status !== 0) process.exit(run.status ?? 1);
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
// The value next.config.ts inlines as NEXT_PUBLIC_BULWARK_DESKTOP.
|
||||
//
|
||||
// Always defined, so every build compiles `process.env.NEXT_PUBLIC_BULWARK_DESKTOP`
|
||||
// to a literal. Left undefined, Next would leave the read in the server bundle,
|
||||
// and setting the variable when starting a server build would turn the flag on.
|
||||
|
||||
/** The marker scripts/standalone-guard.mjs looks for in a non-desktop build. */
|
||||
export const DESKTOP_TRUST_MARKER = 'bulwark.desktop.trustedJmapOrigins';
|
||||
|
||||
export function desktopBuildFlag(env = process.env) {
|
||||
return env.NEXT_PUBLIC_BULWARK_DESKTOP === '1' ? '1' : '';
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
import { existsSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { pathToFileURL } from 'node:url';
|
||||
import { DESKTOP_TRUST_MARKER, desktopBuildFlag } from './desktop-build-flag.mjs';
|
||||
|
||||
/** Top-level project entries the running server needs. Traces drop the rest. */
|
||||
const TRACE_ALLOWED = new Set(['node_modules', '.next', 'public', 'locales', 'package.json', 'VERSION']);
|
||||
@@ -141,10 +142,32 @@ export function removeCopiedEnvFiles(standaloneDir) {
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Server bundles that still hold the desktop app's trust path. A server build
|
||||
* compiles it away (lib/desktop/flag.ts); finding it means the flag leaked into
|
||||
* this build or stopped being inlined, and a visitor could reach private hosts.
|
||||
*/
|
||||
export function findDesktopTrustPath(standaloneDir) {
|
||||
const found = [];
|
||||
const visit = (dir) => {
|
||||
for (const entry of readdirSync(dir, { withFileTypes: true })) {
|
||||
const full = path.join(dir, entry.name);
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name !== 'node_modules') visit(full);
|
||||
} else if (entry.name.endsWith('.js') && readFileSync(full, 'utf8').includes(DESKTOP_TRUST_MARKER)) {
|
||||
found.push(`${toPosix(path.relative(standaloneDir, full))} (desktop trust path in a server build)`);
|
||||
}
|
||||
}
|
||||
};
|
||||
visit(standaloneDir);
|
||||
return found.sort();
|
||||
}
|
||||
|
||||
/** Cleans and checks a standalone tree. Returns what is still wrong with it. */
|
||||
export function guardStandalone(standaloneDir) {
|
||||
export function guardStandalone(standaloneDir, { desktopBuild = desktopBuildFlag() === '1' } = {}) {
|
||||
removeCopiedEnvFiles(standaloneDir);
|
||||
return findForbidden(standaloneDir);
|
||||
const forbidden = findForbidden(standaloneDir);
|
||||
return desktopBuild ? forbidden : [...forbidden, ...findDesktopTrustPath(standaloneDir)];
|
||||
}
|
||||
|
||||
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
||||
|
||||
Reference in New Issue
Block a user