Add admin CLI command and auth guard, use oRPC client
CLI changes: - Use official oRPC client instead of manual HTTP requests - Add admin complete-login command for dev workflow - Remove type assertions, use proper ContractRouterClient typing - Add @orpc/client and @orpc/contract dependencies API changes: - Use oRPC cookie helpers from @orpc/server/helpers - Improve admin complete-login error messages (expired, already completed) Dashboard changes: - Add AuthGuard component to redirect unauthenticated users to /auth/login - Update confirm page with correct CLI command and copy button - Remove duplicate auth redirect from dashboard layout Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,18 +2,19 @@
|
||||
* API client utilities for CLI commands
|
||||
*/
|
||||
|
||||
import type { ContractRouterClient } from "@orpc/contract";
|
||||
import type { contract } from "@reviq/api-contract";
|
||||
import { createORPCClient } from "@orpc/client";
|
||||
import { RPCLink } from "@orpc/client/fetch";
|
||||
import { readConfig } from "./config.js";
|
||||
|
||||
export interface ApiClientError {
|
||||
code: string;
|
||||
message: string;
|
||||
}
|
||||
export type ApiClient = ContractRouterClient<typeof contract>;
|
||||
|
||||
/**
|
||||
* Create an API client with the stored credentials
|
||||
* Create an oRPC API client with the stored credentials
|
||||
* Throws an error if not logged in
|
||||
*/
|
||||
export const createApiClient = async () => {
|
||||
export const createApiClient = async (): Promise<ApiClient> => {
|
||||
const config = await readConfig();
|
||||
if (!config) {
|
||||
throw new Error(
|
||||
@@ -21,41 +22,13 @@ export const createApiClient = async () => {
|
||||
);
|
||||
}
|
||||
|
||||
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>;
|
||||
const link = new RPCLink({
|
||||
url: `${config.apiUrl}/api/v1/rpc`,
|
||||
headers: {
|
||||
"X-API-Key": config.token,
|
||||
},
|
||||
config,
|
||||
};
|
||||
};
|
||||
});
|
||||
|
||||
export type ApiClient = Awaited<ReturnType<typeof createApiClient>>;
|
||||
// Cast to ApiClient for type-safe API calls
|
||||
return createORPCClient(link) as unknown as ApiClient;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user