mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
feat: merge the webmail side of the desktop app
# Conflicts: # lib/desktop/types.ts # locales/ar/common.json # locales/ca/common.json # locales/cs/common.json # locales/da/common.json # locales/de/common.json # locales/en/common.json # locales/es/common.json # locales/et/common.json # locales/fa/common.json # locales/fr/common.json # locales/he/common.json # locales/hu/common.json # locales/it/common.json # locales/ja/common.json # locales/ko/common.json # locales/lv/common.json # locales/mn/common.json # locales/nb/common.json # locales/nl/common.json # locales/pl/common.json # locales/pt/common.json # locales/ro/common.json # locales/ru/common.json # locales/sk/common.json # locales/th/common.json # locales/tr/common.json # locales/uk/common.json # locales/vi/common.json # locales/zh-TW/common.json # locales/zh/common.json # stores/account-store.ts
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,19 @@
|
||||
// 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';
|
||||
|
||||
/**
|
||||
* A read of the flag that survived compiling: then setting it at runtime would
|
||||
* work. Only the read: Next also writes the bare name into server.js with the
|
||||
* config's `env` block, as a JSON key with its compiled value.
|
||||
*/
|
||||
export const DESKTOP_FLAG_READ = /process\.env(\.NEXT_PUBLIC_BULWARK_DESKTOP\b|\[\s*["'`]NEXT_PUBLIC_BULWARK_DESKTOP["'`]\s*\])/;
|
||||
|
||||
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_FLAG_READ, 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,35 @@ 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')) {
|
||||
const text = readFileSync(full, 'utf8');
|
||||
if (text.includes(DESKTOP_TRUST_MARKER) || DESKTOP_FLAG_READ.test(text)) {
|
||||
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