mirror of
https://github.com/Routstr/routstrd.git
synced 2026-07-30 15:46:14 +00:00
feat: add authUrl config for routing management commands to auth proxy
Adds an authUrl field to RoutstrdConfig so CLI management commands (npubs, clients, usage) can be directed to the routstrd-auth proxy instead of the daemon. This fixes 'routstrd npubs list' failing with 'Only POST is supported' when the auth proxy and daemon run on separate ports (e.g., Cloudron setup: proxy on 8008, daemon on 8009). Changes: - config.ts: add authUrl to RoutstrdConfig - daemon-client.ts: add getAuthBaseUrl(), callAuth(), refactor callDaemon - cli.ts: npubs/usage commands use callAuth, remote command accepts --auth-url - clients.ts: clients commands use callAuth
This commit is contained in:
+25
-8
@@ -3,6 +3,7 @@ import { startDaemon } from "./start-daemon";
|
||||
import {
|
||||
handleDaemonCommand,
|
||||
callDaemon,
|
||||
callAuth,
|
||||
ensureDaemonRunning,
|
||||
isDaemonRunning,
|
||||
loadConfig,
|
||||
@@ -334,7 +335,8 @@ program
|
||||
program
|
||||
.command("remote <url>")
|
||||
.description("Configure a remote daemon URL")
|
||||
.action(async (url: string) => {
|
||||
.option("--auth-url <authUrl>", "URL of the auth proxy for management commands (npubs, clients, usage)")
|
||||
.action(async (url: string, options: { authUrl?: string }) => {
|
||||
try {
|
||||
new URL(url);
|
||||
} catch {
|
||||
@@ -342,12 +344,24 @@ program
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (options.authUrl) {
|
||||
try {
|
||||
new URL(options.authUrl);
|
||||
} catch {
|
||||
console.error(`Invalid auth URL: ${options.authUrl}`);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!existsSync(CONFIG_DIR)) {
|
||||
mkdirSync(CONFIG_DIR, { recursive: true });
|
||||
}
|
||||
|
||||
const config = await loadConfig();
|
||||
const updates: Partial<RoutstrdConfig> = { daemonUrl: url };
|
||||
if (options.authUrl) {
|
||||
updates.authUrl = options.authUrl;
|
||||
}
|
||||
let generatedNpub: string | undefined;
|
||||
|
||||
if (!config.nsec) {
|
||||
@@ -366,6 +380,9 @@ program
|
||||
await Bun.write(CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
|
||||
|
||||
console.log(`Remote daemon URL set to: ${url}`);
|
||||
if (options.authUrl) {
|
||||
console.log(`Auth proxy URL set to: ${options.authUrl}`);
|
||||
}
|
||||
if (generatedNpub) {
|
||||
console.log(
|
||||
`\nA new Nostr identity has been generated for remote authentication.`,
|
||||
@@ -666,7 +683,7 @@ program
|
||||
? Math.min(requested, 1000)
|
||||
: 10;
|
||||
|
||||
const result = await callDaemon(`/usage?limit=${limit}`);
|
||||
const result = await callAuth(`/usage?limit=${limit}`);
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
@@ -882,7 +899,7 @@ npubsCmd
|
||||
await ensureDaemonRunning();
|
||||
const config = await loadConfig();
|
||||
const userNpub = getUserNpub(config);
|
||||
const result = await callDaemon("/npubs");
|
||||
const result = await callAuth("/npubs");
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
@@ -925,7 +942,7 @@ npubsCmd
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
const result = await callDaemon("/npubs");
|
||||
const result = await callAuth("/npubs");
|
||||
if (result.error) {
|
||||
console.log(result.error);
|
||||
process.exit(1);
|
||||
@@ -943,7 +960,7 @@ npubsCmd
|
||||
console.error("Failed to normalize user npub.");
|
||||
process.exit(1);
|
||||
}
|
||||
const addResult = await callDaemon("/npubs", {
|
||||
const addResult = await callAuth("/npubs", {
|
||||
method: "POST",
|
||||
body: { npub: npubFromPubkey(normalized) },
|
||||
});
|
||||
@@ -977,7 +994,7 @@ npubsCmd
|
||||
process.exit(1);
|
||||
}
|
||||
const body: Record<string, string> = { npub: npubFromPubkey(normalized), role: options.role };
|
||||
const result = await callDaemon("/npubs", {
|
||||
const result = await callAuth("/npubs", {
|
||||
method: "POST",
|
||||
body,
|
||||
});
|
||||
@@ -1010,7 +1027,7 @@ npubsCmd
|
||||
console.error("Invalid role. Expected 'admin' or 'user'.");
|
||||
process.exit(1);
|
||||
}
|
||||
const result = await callDaemon("/npubs", {
|
||||
const result = await callAuth("/npubs", {
|
||||
method: "PATCH",
|
||||
body: { npub: npubFromPubkey(normalized), role: options.role },
|
||||
});
|
||||
@@ -1039,7 +1056,7 @@ npubsCmd
|
||||
console.error("Invalid npub value. Use npub1... or 64-char hex pubkey.");
|
||||
process.exit(1);
|
||||
}
|
||||
const result = await callDaemon(
|
||||
const result = await callAuth(
|
||||
`/npubs/${encodeURIComponent(npubFromPubkey(normalized))}`,
|
||||
{
|
||||
method: "DELETE",
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import {
|
||||
callAuth,
|
||||
callDaemon,
|
||||
loadConfig,
|
||||
getDaemonBaseUrl,
|
||||
@@ -56,7 +57,7 @@ export function getClientsFromStore(store: { getState(): any }): ClientEntry[] {
|
||||
* Use this when running remotely (CLI in remote mode).
|
||||
*/
|
||||
export async function getClientsList(): Promise<ClientEntry[]> {
|
||||
const result = await callDaemon("/clients");
|
||||
const result = await callAuth("/clients");
|
||||
const clients = (
|
||||
result.output as
|
||||
| {
|
||||
@@ -107,7 +108,7 @@ export async function addDaemonClient(
|
||||
return { client, created: false };
|
||||
}
|
||||
|
||||
const result = await callDaemon("/clients/add", {
|
||||
const result = await callAuth("/clients/add", {
|
||||
method: "POST",
|
||||
body: { name, id: derivedId },
|
||||
});
|
||||
@@ -159,7 +160,7 @@ export async function listClientsAction(): Promise<void> {
|
||||
export async function deleteClientAction(id: string): Promise<void> {
|
||||
await ensureDaemonRunning();
|
||||
|
||||
const result = await callDaemon("/clients/delete", {
|
||||
const result = await callAuth("/clients/delete", {
|
||||
method: "POST",
|
||||
body: { id },
|
||||
});
|
||||
|
||||
@@ -35,6 +35,9 @@ export interface RoutstrdConfig {
|
||||
cocodPath: string | null;
|
||||
mode?: "xcashu" | "apikeys";
|
||||
daemonUrl?: string;
|
||||
/** URL of the auth proxy (routstrd-auth) for management endpoints (npubs, clients, usage).
|
||||
* Defaults to daemonUrl or localhost:{port} if not set. */
|
||||
authUrl?: string;
|
||||
nsec?: string;
|
||||
/** Nostr hex pubkey for routstr review/model events (kind 38425/38423). */
|
||||
routstrPubkey?: string;
|
||||
|
||||
@@ -35,13 +35,20 @@ export function getDaemonBaseUrl(config: RoutstrdConfig): string {
|
||||
);
|
||||
}
|
||||
|
||||
export async function callDaemon(
|
||||
export function getAuthBaseUrl(config: RoutstrdConfig): string {
|
||||
if (config.authUrl) {
|
||||
return config.authUrl.replace(/\/$/, "");
|
||||
}
|
||||
return getDaemonBaseUrl(config);
|
||||
}
|
||||
|
||||
async function _callUrl(
|
||||
baseUrl: string,
|
||||
path: string,
|
||||
options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
|
||||
options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object },
|
||||
config: RoutstrdConfig,
|
||||
): Promise<CommandResponse> {
|
||||
const { method = "GET", body } = options;
|
||||
const config = await loadConfig();
|
||||
const baseUrl = getDaemonBaseUrl(config);
|
||||
const url = `${baseUrl}${path}`;
|
||||
|
||||
const bodyString = body ? JSON.stringify(body) : undefined;
|
||||
@@ -50,7 +57,7 @@ export async function callDaemon(
|
||||
: undefined;
|
||||
|
||||
let authorization: string | undefined;
|
||||
if (config.daemonUrl && config.nsec) {
|
||||
if ((config.daemonUrl || config.authUrl) && config.nsec) {
|
||||
const secretKey = parseSecretKey(config.nsec);
|
||||
authorization = await createNIP98Authorization(
|
||||
secretKey,
|
||||
@@ -77,6 +84,26 @@ export async function callDaemon(
|
||||
return response.json() as Promise<CommandResponse>;
|
||||
}
|
||||
|
||||
export async function callDaemon(
|
||||
path: string,
|
||||
options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
|
||||
): Promise<CommandResponse> {
|
||||
const config = await loadConfig();
|
||||
const baseUrl = getDaemonBaseUrl(config);
|
||||
return _callUrl(baseUrl, path, options, config);
|
||||
}
|
||||
|
||||
/** Like callDaemon but sends requests to the auth proxy URL instead.
|
||||
* Falls back to the daemon URL if no authUrl is configured. */
|
||||
export async function callAuth(
|
||||
path: string,
|
||||
options: { method?: "GET" | "POST" | "PATCH" | "DELETE"; body?: object } = {},
|
||||
): Promise<CommandResponse> {
|
||||
const config = await loadConfig();
|
||||
const baseUrl = getAuthBaseUrl(config);
|
||||
return _callUrl(baseUrl, path, options, config);
|
||||
}
|
||||
|
||||
export async function isDaemonRunning(): Promise<boolean> {
|
||||
try {
|
||||
const config = await loadConfig();
|
||||
|
||||
Reference in New Issue
Block a user