chore: add proper-lockfile for safe database read/write operations and implement retry logic for file access
This commit is contained in:
parent
3059df4014
commit
8759545260
15 changed files with 648 additions and 57 deletions
25
src/app/api/9remote/install/route.js
Normal file
25
src/app/api/9remote/install/route.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { exec } from "child_process";
|
||||
import { join, dirname } from "path";
|
||||
|
||||
// Use npm from the same Node.js that runs Next.js — ensures 9remote
|
||||
// lands in the correct global bin (nvm or system, whichever is active)
|
||||
const npmBin = join(dirname(process.execPath), "npm");
|
||||
|
||||
function installPackage() {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(`"${npmBin}" install -g 9remote`, { windowsHide: true }, (err, stdout, stderr) => {
|
||||
if (err) reject(new Error(stderr || err.message));
|
||||
else resolve(stdout);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
await installPackage();
|
||||
return NextResponse.json({ ok: true });
|
||||
} catch (error) {
|
||||
return NextResponse.json({ ok: false, error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
43
src/app/api/9remote/start/route.js
Normal file
43
src/app/api/9remote/start/route.js
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { spawn } from "child_process";
|
||||
import { join, dirname } from "path";
|
||||
import os from "os";
|
||||
import { setRemoteProcess } from "@/lib/9remoteManager";
|
||||
|
||||
const bin9remote = join(dirname(process.execPath), "9remote");
|
||||
|
||||
export async function POST() {
|
||||
try {
|
||||
const nodeDir = dirname(process.execPath);
|
||||
const existingPath = process.env.PATH || "";
|
||||
const path = existingPath.includes(nodeDir)
|
||||
? existingPath
|
||||
: `${nodeDir}:${existingPath}`;
|
||||
|
||||
const env = {
|
||||
HOME: os.homedir(),
|
||||
PATH: path,
|
||||
USER: process.env.USER || process.env.LOGNAME,
|
||||
LANG: process.env.LANG || "en_US.UTF-8",
|
||||
TERM: process.env.TERM || "xterm-256color",
|
||||
TMPDIR: process.env.TMPDIR || os.tmpdir(),
|
||||
SHELL: process.env.SHELL,
|
||||
};
|
||||
const home = os.homedir();
|
||||
|
||||
// Spawn without detached - process will be child of Next.js and receive SIGTERM
|
||||
const child = spawn(bin9remote, ["ui", "--start"], {
|
||||
cwd: home,
|
||||
stdio: "ignore",
|
||||
env,
|
||||
windowsHide: process.platform === "win32",
|
||||
});
|
||||
|
||||
// Store child process for manual cleanup if needed
|
||||
setRemoteProcess(child);
|
||||
|
||||
return NextResponse.json({ ok: true });
|
||||
} catch (error) {
|
||||
return NextResponse.json({ ok: false, error: error.message }, { status: 500 });
|
||||
}
|
||||
}
|
||||
25
src/app/api/9remote/status/route.js
Normal file
25
src/app/api/9remote/status/route.js
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
import { NextResponse } from "next/server";
|
||||
import { existsSync } from "fs";
|
||||
import { join, dirname } from "path";
|
||||
|
||||
const bin9remote = join(dirname(process.execPath), "9remote");
|
||||
const AGENT_URL = "http://localhost:2208";
|
||||
|
||||
async function isRunning() {
|
||||
try {
|
||||
const res = await fetch(`${AGENT_URL}/api/health`, {
|
||||
signal: AbortSignal.timeout(1500),
|
||||
});
|
||||
return res.ok;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export async function GET() {
|
||||
const running = await isRunning();
|
||||
if (running) return NextResponse.json({ installed: true, running: true });
|
||||
|
||||
const installed = existsSync(bin9remote);
|
||||
return NextResponse.json({ installed, running: false });
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue