Implement CLI commands and admin API endpoints

- Add bootstrap command with direct DB access for initial setup
- Implement auth login/logout/status CLI commands
- Implement user create/confirm-email CLI commands
- Implement org create/list/add-site CLI commands
- Add admin.orgs.* and admin.users.* API endpoints
- Add password hashing utility with scrypt
- Add token hashing and authentication utility
- Add superuser runtime checks for admin endpoints
- Wrap multi-step operations in transactions
- Fix config file permissions (0o600) for security
- Remove token display from status command
- Add return statements to void handlers
- Add reviq CLI command to devenv

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 15:30:10 +08:00
parent 30ee35b25c
commit 410b937f9f
20 changed files with 1267 additions and 85 deletions

View File

@@ -0,0 +1,61 @@
/**
* API client utilities for CLI commands
*/
import { readConfig } from "./config.js";
export interface ApiClientError {
code: string;
message: string;
}
/**
* Create an API client with the stored credentials
* Throws an error if not logged in
*/
export const createApiClient = async () => {
const config = await readConfig();
if (!config) {
throw new Error(
"Not logged in. Run 'reviq bootstrap' or 'reviq auth login' first.",
);
}
return {
/**
* Call an oRPC procedure
*/
call: async <T>(path: string, input?: unknown): Promise<T> => {
const url = `${config.apiUrl}/api/v1/rpc/${path}`;
const response = await fetch(url, {
method: "POST",
headers: {
"Content-Type": "application/json",
"X-API-Key": config.token,
},
body: input !== undefined ? JSON.stringify(input) : undefined,
});
if (!response.ok) {
const text = await response.text();
let errorMessage = `API error: ${String(response.status)} ${response.statusText}`;
try {
const error = JSON.parse(text) as ApiClientError;
if (error.message) {
errorMessage = error.message;
}
} catch {
if (text) {
errorMessage = text;
}
}
throw new Error(errorMessage);
}
return response.json() as Promise<T>;
},
config,
};
};
export type ApiClient = Awaited<ReturnType<typeof createApiClient>>;