Merge branch 'main' of github.com-red:Routstr/routstrd into coco-integration

# Conflicts:
#	src/cli.ts
This commit is contained in:
redshift
2026-07-30 20:59:46 +01:00
5 changed files with 68 additions and 25 deletions
+1 -1
View File
@@ -12,7 +12,7 @@
"monitor": "bun src/index.ts monitor", "monitor": "bun src/index.ts monitor",
"lint": "tsc --noEmit", "lint": "tsc --noEmit",
"test": "bun test", "test": "bun test",
"build": "mkdir -p dist/daemon && bun build src/index.ts --target=bun --outfile=dist/index.js && bun build src/daemon/index.ts --target=bun --outfile=dist/daemon/index.js", "build": "bun build src/index.ts --target=bun --outfile=dist/index.js --external better-sqlite3 && bun build src/daemon/index.ts --target=bun --outfile=dist/daemon/index.js --external better-sqlite3",
"prepublishOnly": "bun run build" "prepublishOnly": "bun run build"
}, },
"devDependencies": { "devDependencies": {
+50 -17
View File
@@ -15,7 +15,7 @@ import {
deleteClientAction, deleteClientAction,
addClientAction, addClientAction,
} from "./utils/clients"; } from "./utils/clients";
import { chmodSync, existsSync, mkdirSync, writeFileSync } from "fs"; import { chmodSync, existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "fs";
import { execSync } from "child_process"; import { execSync } from "child_process";
import { import {
CONFIG_DIR, CONFIG_DIR,
@@ -1955,6 +1955,40 @@ function getLogFileForDate(date: Date = new Date()): string {
return `${LOGS_DIR}/${year}-${month}-${day}.log`; return `${LOGS_DIR}/${year}-${month}-${day}.log`;
} }
function readLastLines(file: string, lines: number): string {
const content = readFileSync(file, "utf8");
const allLines = content.replace(/\r\n/g, "\n").split("\n");
if (allLines.at(-1) === "") allLines.pop();
return allLines.slice(-lines).join("\n");
}
async function followLogFile(file: string, lines: number): Promise<void> {
const initial = readLastLines(file, lines);
if (initial) {
console.log(initial);
}
let position = statSync(file).size;
while (true) {
await new Promise((resolve) => setTimeout(resolve, 1000));
if (!existsSync(file)) {
continue;
}
const size = statSync(file).size;
if (size < position) {
position = 0;
}
if (size === position) {
continue;
}
const text = await Bun.file(file).slice(position, size).text();
process.stdout.write(text);
position = size;
}
}
program program
.command("logs") .command("logs")
.description("View daemon logs") .description("View daemon logs")
@@ -1980,24 +2014,23 @@ program
}); });
if (options.follow) { if (options.follow) {
const proc = Bun.spawn(["tail", "-n", String(lines), "-f", todayFile], { if (existsSync(todayFile)) {
stdout: "inherit", await followLogFile(todayFile, lines);
stderr: "inherit", } else {
stdin: "inherit", console.log("No log file for today to follow.");
}); }
return;
const exitCode = await proc.exited;
process.exit(exitCode);
} }
const proc = Bun.spawn(["tail", "-n", String(lines), ...logFiles], { for (const file of logFiles) {
stdout: "inherit", if (logFiles.length > 1) {
stderr: "inherit", console.log(`==> ${file} <==`);
stdin: "inherit", }
}); const output = readLastLines(file, lines);
if (output) {
const exitCode = await proc.exited; console.log(output);
process.exit(exitCode); }
}
}); });
export function cli(args: string[]) { export function cli(args: string[]) {
+2 -2
View File
@@ -1,5 +1,5 @@
import { createServer } from "http"; import { createServer } from "http";
import { existsSync } from "fs"; import { existsSync, unlinkSync } from "fs";
import { import {
ProviderManager, ProviderManager,
createStorageAdapterFromStore, createStorageAdapterFromStore,
@@ -182,7 +182,7 @@ async function main(): Promise<void> {
try { try {
if (existsSync(SOCKET_PATH)) { if (existsSync(SOCKET_PATH)) {
Bun.spawn(["rm", SOCKET_PATH]); unlinkSync(SOCKET_PATH);
} }
} catch { } catch {
// Ignore // Ignore
+8 -2
View File
@@ -1,5 +1,6 @@
import { existsSync } from "fs"; import { existsSync } from "fs";
import { createHash } from "crypto"; import { createHash } from "crypto";
import { isAbsolute } from "path";
import { logger } from "../../utils/logger"; import { logger } from "../../utils/logger";
import { withCrossProcessLock } from "../../utils/process-lock"; import { withCrossProcessLock } from "../../utils/process-lock";
import type { HistoryEntry } from "@cashu/coco-core"; import type { HistoryEntry } from "@cashu/coco-core";
@@ -101,13 +102,18 @@ export async function isCocodInstalled(
): Promise<boolean> { ): Promise<boolean> {
const executable = resolveCocodExecutable(cocodPath); const executable = resolveCocodExecutable(cocodPath);
if (executable.includes("/")) { if (
isAbsolute(executable) ||
executable.includes("/") ||
executable.includes("\\")
) {
return existsSync(executable); return existsSync(executable);
} }
try { try {
const command = process.platform === "win32" ? "where.exe" : "which";
const proc = Bun.spawn({ const proc = Bun.spawn({
cmd: ["which", executable], cmd: [command, executable],
stdout: "ignore", stdout: "ignore",
stderr: "ignore", stderr: "ignore",
}); });
+7 -3
View File
@@ -8,6 +8,8 @@ import {
import { logger } from "./utils/logger"; import { logger } from "./utils/logger";
import { CONFIG_DIR, LOGS_DIR } from "./utils/config"; import { CONFIG_DIR, LOGS_DIR } from "./utils/config";
import { withCrossProcessLock } from "./utils/process-lock"; import { withCrossProcessLock } from "./utils/process-lock";
import { fileURLToPath } from "url";
import { existsSync } from "fs";
const DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`; const DAEMON_STARTUP_LOCK_PATH = `${CONFIG_DIR}/routstrd-startup.lock`;
const DEBUG_LOG_PATH = `${CONFIG_DIR}/debug.log`; const DEBUG_LOG_PATH = `${CONFIG_DIR}/debug.log`;
@@ -146,15 +148,17 @@ async function startDaemonUnlocked(
args.push("--provider", options.provider); args.push("--provider", options.provider);
} }
const daemonScript = new URL("./daemon/index.js", import.meta.url).pathname; let daemonScript = fileURLToPath(new URL("./daemon/index.js", import.meta.url));
const shellCmd = `bun run "${daemonScript}" ${args.map((a) => `'${a}'`).join(" ")}`; if (!existsSync(daemonScript)) {
daemonScript = fileURLToPath(new URL("./daemon/index.ts", import.meta.url));
}
const debugLogOffset = existsSync(DEBUG_LOG_PATH) const debugLogOffset = existsSync(DEBUG_LOG_PATH)
? fileSize(DEBUG_LOG_PATH) ? fileSize(DEBUG_LOG_PATH)
: 0; : 0;
const debugLogFd = openSync(DEBUG_LOG_PATH, "a"); const debugLogFd = openSync(DEBUG_LOG_PATH, "a");
const proc = Bun.spawn(["sh", "-c", shellCmd], { const proc = Bun.spawn(["bun", daemonScript, ...args], {
stdout: debugLogFd, stdout: debugLogFd,
stderr: debugLogFd, stderr: debugLogFd,
stdin: "ignore", stdin: "ignore",