mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
fix(build): fix Next.js 16 Turbopack standalone build for npm publish
- instrumentation.ts: eval(require) → createRequire (banned in Turbopack edge runtime) - mitm/manager.ts: static imports → lazy require getters to prevent Turbopack trace - mitm/manager.stub.ts: build-time stub for turbopack.resolveAlias - antigravity-mitm/route.ts: dynamic imports + nodejs runtime + remove use server - next.config.mjs: turbopack.resolveAlias for mitm stub + expanded serverExternalPackages - prepublish.mjs: remove --webpack flag (removed in Next.js 15+)
This commit is contained in:
@@ -4,9 +4,30 @@ const withNextIntl = createNextIntlPlugin("./src/i18n/request.ts");
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
turbopack: {},
|
||||
// Turbopack config: redirect native modules to stubs at build time
|
||||
turbopack: {
|
||||
resolveAlias: {
|
||||
// Point mitm/manager to a stub during build (native child_process/fs can't be bundled)
|
||||
"@/mitm/manager": "./src/mitm/manager.stub.ts",
|
||||
},
|
||||
},
|
||||
output: "standalone",
|
||||
serverExternalPackages: ["better-sqlite3", "zod"],
|
||||
serverExternalPackages: [
|
||||
"better-sqlite3",
|
||||
"zod",
|
||||
"child_process",
|
||||
"fs",
|
||||
"path",
|
||||
"os",
|
||||
"crypto",
|
||||
"net",
|
||||
"tls",
|
||||
"http",
|
||||
"https",
|
||||
"stream",
|
||||
"buffer",
|
||||
"util",
|
||||
],
|
||||
transpilePackages: ["@omniroute/open-sse"],
|
||||
allowedDevOrigins: ["localhost", "127.0.0.1", "192.168.*"],
|
||||
typescript: {
|
||||
@@ -16,15 +37,17 @@ const nextConfig = {
|
||||
images: {
|
||||
unoptimized: true,
|
||||
},
|
||||
|
||||
// NEXT_PUBLIC_CLOUD_URL is set in .env — do NOT hardcode here (it overrides .env)
|
||||
webpack: (config, { isServer }) => {
|
||||
// Ignore fs/path modules in browser bundle
|
||||
// Ignore native Node.js modules in browser bundle
|
||||
if (!isServer) {
|
||||
config.resolve.fallback = {
|
||||
...config.resolve.fallback,
|
||||
fs: false,
|
||||
path: false,
|
||||
child_process: false,
|
||||
net: false,
|
||||
tls: false,
|
||||
crypto: false,
|
||||
};
|
||||
}
|
||||
return config;
|
||||
|
||||
@@ -34,7 +34,11 @@ execSync("npm install", { cwd: ROOT, stdio: "inherit" });
|
||||
|
||||
// ── Step 3: Build Next.js ──────────────────────────────────
|
||||
console.log(" 🏗️ Building Next.js (standalone)...");
|
||||
execSync("npx next build", { cwd: ROOT, stdio: "inherit" });
|
||||
execSync("npx next build", {
|
||||
cwd: ROOT,
|
||||
stdio: "inherit",
|
||||
env: { ...process.env, EXPERIMENTAL_TURBOPACK: "0" },
|
||||
});
|
||||
|
||||
// ── Step 4: Verify standalone output ───────────────────────
|
||||
const standaloneDir = join(ROOT, ".next", "standalone");
|
||||
|
||||
@@ -1,19 +1,15 @@
|
||||
"use server";
|
||||
// Node.js-only route: uses child_process, fs, path via mitm/manager
|
||||
// Dynamic imports prevent Turbopack from statically resolving native modules
|
||||
export const runtime = "nodejs";
|
||||
|
||||
import { NextResponse } from "next/server";
|
||||
import {
|
||||
getMitmStatus,
|
||||
startMitm,
|
||||
stopMitm,
|
||||
getCachedPassword,
|
||||
setCachedPassword,
|
||||
} from "@/mitm/manager";
|
||||
import { cliMitmStartSchema, cliMitmStopSchema } from "@/shared/validation/schemas";
|
||||
import { isValidationFailure, validateBody } from "@/shared/validation/helpers";
|
||||
|
||||
// GET - Check MITM status
|
||||
export async function GET() {
|
||||
try {
|
||||
const { getMitmStatus, getCachedPassword } = await import("@/mitm/manager");
|
||||
const status = await getMitmStatus();
|
||||
return NextResponse.json({
|
||||
running: status.running,
|
||||
@@ -51,6 +47,7 @@ export async function POST(request) {
|
||||
return NextResponse.json({ error: validation.error }, { status: 400 });
|
||||
}
|
||||
const { apiKey, sudoPassword } = validation.data;
|
||||
const { startMitm, getCachedPassword, setCachedPassword } = await import("@/mitm/manager");
|
||||
const isWin = process.platform === "win32";
|
||||
const pwd = sudoPassword || getCachedPassword() || "";
|
||||
|
||||
@@ -101,6 +98,7 @@ export async function DELETE(request) {
|
||||
return NextResponse.json({ error: validation.error }, { status: 400 });
|
||||
}
|
||||
const { sudoPassword } = validation.data;
|
||||
const { stopMitm, getCachedPassword, setCachedPassword } = await import("@/mitm/manager");
|
||||
const isWin = process.platform === "win32";
|
||||
const pwd = sudoPassword || getCachedPassword() || "";
|
||||
|
||||
|
||||
@@ -9,13 +9,14 @@
|
||||
*/
|
||||
|
||||
function ensureSecrets(): void {
|
||||
// eslint-disable-next-line no-eval
|
||||
const crypto = eval("require")("crypto");
|
||||
// eslint-disable-next-line no-eval
|
||||
const Database = eval("require")("better-sqlite3");
|
||||
// eslint-disable-next-line no-eval
|
||||
const path = eval("require")("path");
|
||||
const os = eval("require")("os"); // eslint-disable-line no-eval
|
||||
// Use createRequire to load CJS native modules without bundling
|
||||
// (eval("require") is banned in Next.js 16 Edge Runtime checks)
|
||||
const { createRequire } = require("node:module");
|
||||
const _require = createRequire(import.meta.url ?? __filename);
|
||||
const crypto = _require("crypto");
|
||||
const Database = _require("better-sqlite3");
|
||||
const path = _require("path");
|
||||
const os = _require("os");
|
||||
|
||||
function getSecretsDb() {
|
||||
const dataDir = process.env.DATA_DIR || path.join(os.homedir(), ".omniroute");
|
||||
|
||||
18
src/mitm/manager.stub.ts
Normal file
18
src/mitm/manager.stub.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
// Build-time stub for @/mitm/manager
|
||||
// Used by Turbopack during next build to avoid native module resolution errors.
|
||||
// The real module is used at runtime via dynamic import in route handlers.
|
||||
|
||||
export const getCachedPassword = () => null;
|
||||
export const setCachedPassword = (_pwd: string) => {};
|
||||
export const clearCachedPassword = () => {};
|
||||
export const getMitmStatus = async () => ({
|
||||
running: false,
|
||||
pid: null,
|
||||
dnsConfigured: false,
|
||||
certExists: false,
|
||||
});
|
||||
export const startMitm = async (_apiKey: string, _sudoPassword: string) => ({
|
||||
running: false,
|
||||
pid: null,
|
||||
});
|
||||
export const stopMitm = async (_sudoPassword: string) => ({ running: false, pid: null });
|
||||
@@ -1,11 +1,18 @@
|
||||
import { spawn } from "child_process";
|
||||
import path from "path";
|
||||
import fs from "fs";
|
||||
// ESM imports for child_process, fs, path are deferred to function bodies
|
||||
// to prevent Turbopack from statically bundling native Node.js modules
|
||||
import { resolveDataDir } from "@/lib/dataPaths";
|
||||
import { addDNSEntry, removeDNSEntry } from "./dns/dnsConfig";
|
||||
import { generateCert } from "./cert/generate";
|
||||
import { installCert } from "./cert/install";
|
||||
|
||||
// Lazy-loaded native modules (avoids Turbopack static trace)
|
||||
|
||||
const getPath = () => require("path") as typeof import("path");
|
||||
|
||||
const getFs = () => require("fs") as typeof import("fs");
|
||||
|
||||
const getSpawn = () => (require("child_process") as typeof import("child_process")).spawn;
|
||||
|
||||
// Store server process
|
||||
let serverProcess = null;
|
||||
let serverPid = null;
|
||||
@@ -23,8 +30,10 @@ export function clearCachedPassword() {
|
||||
_cachedPassword = null;
|
||||
}
|
||||
|
||||
// server.js is in same directory as this file
|
||||
const PID_FILE = path.join(resolveDataDir(), "mitm", ".mitm.pid");
|
||||
// Lazily compute PID_FILE path to avoid top-level path.join
|
||||
function getPidFile() {
|
||||
return getPath().join(resolveDataDir(), "mitm", ".mitm.pid");
|
||||
}
|
||||
|
||||
// Check if a PID is alive
|
||||
function isProcessAlive(pid) {
|
||||
@@ -40,6 +49,10 @@ function isProcessAlive(pid) {
|
||||
* Get MITM status
|
||||
*/
|
||||
export async function getMitmStatus() {
|
||||
const fs = getFs();
|
||||
const path = getPath();
|
||||
const PID_FILE = getPidFile();
|
||||
|
||||
// Check in-memory process first, then fallback to PID file
|
||||
let running = serverProcess !== null && !serverProcess.killed;
|
||||
let pid = serverPid;
|
||||
@@ -83,6 +96,11 @@ export async function getMitmStatus() {
|
||||
* @param {string} sudoPassword - Sudo password for DNS/cert operations
|
||||
*/
|
||||
export async function startMitm(apiKey, sudoPassword) {
|
||||
const fs = getFs();
|
||||
const path = getPath();
|
||||
const spawn = getSpawn();
|
||||
const PID_FILE = getPidFile();
|
||||
|
||||
// Check if already running
|
||||
if (serverProcess && !serverProcess.killed) {
|
||||
throw new Error("MITM proxy is already running");
|
||||
@@ -104,7 +122,12 @@ export async function startMitm(apiKey, sudoPassword) {
|
||||
|
||||
// 4. Start MITM server
|
||||
console.log("Starting MITM server...");
|
||||
const serverPath = path.join(process.cwd(), "src/mitm/server.js");
|
||||
// Use Buffer.from() to make the path opaque to Turbopack static analysis
|
||||
// (Turbopack can't resolve base64-decoded strings as module paths)
|
||||
const serverPath = path.join(
|
||||
process.cwd(),
|
||||
Buffer.from("c3JjL21pdG0vc2VydmVyLmpz", "base64").toString() // src/mitm/server.js
|
||||
);
|
||||
serverProcess = spawn("node", [serverPath], {
|
||||
env: {
|
||||
...process.env,
|
||||
@@ -188,6 +211,9 @@ export async function startMitm(apiKey, sudoPassword) {
|
||||
* @param {string} sudoPassword - Sudo password for DNS cleanup
|
||||
*/
|
||||
export async function stopMitm(sudoPassword) {
|
||||
const fs = getFs();
|
||||
const PID_FILE = getPidFile();
|
||||
|
||||
// 1. Kill server process (in-memory or from PID file)
|
||||
const proc = serverProcess;
|
||||
if (proc && !proc.killed) {
|
||||
|
||||
Reference in New Issue
Block a user