| |
| |
|
|
| import { validateApiBaseUrl } from "../config"; |
|
|
| const BASE = validateApiBaseUrl(); |
|
|
| interface RequestOptions { |
| method?: string; |
| body?: any; |
| timeout?: number; |
| headers?: Record<string, string>; |
| } |
|
|
| class ApiError extends Error { |
| status: number; |
| constructor(message: string, status: number) { |
| super(message); |
| this.name = "ApiError"; |
| this.status = status; |
| } |
| } |
|
|
| async function request<T>(path: string, options: RequestOptions = {}): Promise<T> { |
| const { method = "GET", body, timeout = 30000, headers } = options; |
| const controller = new AbortController(); |
| const timer = setTimeout(() => controller.abort(), timeout); |
|
|
| try { |
| const token = localStorage.getItem("workspace_auth_token"); |
| const fetchOptions: RequestInit = { |
| method, |
| headers: { |
| "Content-Type": "application/json", |
| ...(token ? { "Authorization": `Bearer ${token}` } : {}), |
| ...(headers || {}) |
| }, |
| signal: controller.signal, |
| }; |
| if (body && method !== "GET") { |
| fetchOptions.body = JSON.stringify(body); |
| } |
|
|
| const response = await fetch(`${BASE}${path}`, fetchOptions); |
|
|
| if (!response.ok) { |
| const errDetail = await response.text().catch(() => ""); |
| throw new ApiError( |
| errDetail || `HTTP ${response.status}: ${response.statusText}`, |
| response.status |
| ); |
| } |
|
|
| return (await response.json()) as T; |
| } finally { |
| clearTimeout(timer); |
| } |
| } |
|
|
| |
|
|
| export interface AIProcessResponse { |
| result: string; |
| provider: string; |
| model?: string; |
| tokensConsumed?: number; |
| } |
|
|
| export interface AIChatResponse { |
| result: string; |
| provider: string; |
| tokensConsumed?: number; |
| } |
|
|
| export function aiProcess( |
| text: string, |
| action: string, |
| targetLang?: string, |
| customOpenRouterKey?: string, |
| customModel?: string |
| ): Promise<AIProcessResponse> { |
| return request<AIProcessResponse>("/api/ai/process", { |
| method: "POST", |
| body: { text, action, targetLang, customOpenRouterKey, customModel }, |
| timeout: 60000, |
| }); |
| } |
|
|
| export function aiChat( |
| documentContent: string, |
| history: { sender: string; text: string }[], |
| message: string, |
| customOpenRouterKey?: string, |
| customModel?: string |
| ): Promise<AIChatResponse> { |
| return request<AIChatResponse>("/api/ai/chat", { |
| method: "POST", |
| body: { documentContent, history, message, customOpenRouterKey, customModel }, |
| timeout: 60000, |
| }); |
| } |
|
|
| |
|
|
| export interface SyncResponse { |
| status: string; |
| timestamp: string; |
| newVersion: number; |
| content: string; |
| logs: string[]; |
| activeCollaborators: number; |
| } |
|
|
| export function syncDocument( |
| docId: string, |
| content: string, |
| version: number, |
| teamChangesEnabled: boolean, |
| title?: string |
| ): Promise<SyncResponse> { |
| return request<SyncResponse>("/api/sync", { |
| method: "POST", |
| body: { docId, content, version, teamChangesEnabled, title }, |
| }); |
| } |
|
|
| export interface ServerDocument { |
| id: string; |
| title: string; |
| content: string; |
| version: number; |
| lastSaved: string; |
| } |
|
|
| export function getDocuments(): Promise<{ documents: ServerDocument[] }> { |
| return request("/api/documents"); |
| } |
|
|
| export function getDocument(docId: string): Promise<ServerDocument> { |
| return request<ServerDocument>(`/api/documents/${docId}`); |
| } |
|
|
| export function deleteDocument(docId: string): Promise<{ status: string }> { |
| return request(`/api/documents/${docId}`, { method: "DELETE" }); |
| } |
|
|
| |
|
|
| export interface PreferencesSyncResponse { |
| status: string; |
| timestamp: string; |
| preferences: Record<string, any>; |
| } |
|
|
| export function syncPreferences( |
| preferences: Record<string, any> |
| ): Promise<PreferencesSyncResponse> { |
| return request<PreferencesSyncResponse>("/api/preferences/sync", { |
| method: "POST", |
| body: { preferences }, |
| }); |
| } |
|
|
| export function getPreferences(): Promise<{ preferences: Record<string, any> }> { |
| return request("/api/preferences"); |
| } |
|
|
| |
|
|
| export interface ConvertResponse { |
| job_id: string; |
| status: string; |
| } |
|
|
| export interface ConvertStatusResponse { |
| job_id: string; |
| filename?: string; |
| status: string; |
| markdown?: string; |
| error?: string; |
| created_at?: string; |
| } |
|
|
| export async function uploadAndConvert( |
| file: File |
| ): Promise<ConvertResponse> { |
| const formData = new FormData(); |
| formData.append("file", file); |
|
|
| const response = await fetch(`${BASE}/api/convert`, { |
| method: "POST", |
| body: formData, |
| }); |
|
|
| if (!response.ok) { |
| const errText = await response.text().catch(() => ""); |
| throw new ApiError(errText || `Upload failed: HTTP ${response.status}`, response.status); |
| } |
|
|
| return response.json(); |
| } |
|
|
| export function getConvertStatus(jobId: string): Promise<ConvertStatusResponse> { |
| return request<ConvertStatusResponse>(`/api/convert/status/${jobId}`); |
| } |
|
|
| |
|
|
| export interface SpellcheckResponse { |
| misspelled: string[]; |
| } |
|
|
| export function spellcheck(words: string[]): Promise<SpellcheckResponse> { |
| return request<SpellcheckResponse>("/api/spellcheck", { |
| method: "POST", |
| body: { words }, |
| }); |
| } |
|
|
| |
|
|
| export interface AdminStatsResponse { |
| total_jobs: number; |
| completed_jobs: number; |
| failed_jobs: number; |
| pending_jobs: number; |
| total_users: number; |
| guest_users: number; |
| registered_users: number; |
| total_documents: number; |
| total_payments: number; |
| preferences_count: number; |
| system_uptime: string; |
| configs: Record<string, string>; |
| recent_jobs: any[]; |
| } |
|
|
| export function adminVerify(password: string): Promise<{ status: string; token: string }> { |
| return request("/api/admin/verify", { method: "POST", body: { password } }); |
| } |
|
|
| export function getAdminStats(token: string): Promise<AdminStatsResponse> { |
| return request("/api/admin/stats", { headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function setAdminConfig(configs: Record<string, string>, token: string): Promise<{ status: string; message: string }> { |
| return request("/api/admin/config", { method: "POST", body: { configs }, headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function purgeJobs(token: string): Promise<{ status: string; message: string }> { |
| return request("/api/admin/purge/jobs", { method: "POST", headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function purgeGuests(token: string): Promise<{ status: string; message: string }> { |
| return request("/api/admin/purge/guests", { method: "POST", headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function getAdminUsers(token: string, page: number = 1): Promise<{ total: number; users: any[] }> { |
| return request(`/api/admin/users?page=${page}`, { headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function updateAdminUser(userId: number, data: { tier?: string; display_name?: string }, token: string): Promise<{ status: string }> { |
| return request(`/api/admin/users/${userId}`, { method: "PATCH", body: data, headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function deleteAdminUser(userId: number, token: string): Promise<{ status: string }> { |
| return request(`/api/admin/users/${userId}`, { method: "DELETE", headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function getAdminDocuments(token: string, page: number = 1): Promise<{ total: number; documents: any[] }> { |
| return request(`/api/admin/documents?page=${page}`, { headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function deleteAdminDocument(docId: string, token: string): Promise<{ status: string }> { |
| return request(`/api/admin/documents/${docId}`, { method: "DELETE", headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function getAdminPayments(token: string, page: number = 1): Promise<{ total: number; payments: any[] }> { |
| return request(`/api/admin/payments?page=${page}`, { headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function getAdminAdmins(token: string): Promise<{ admins: any[] }> { |
| return request("/api/admin/admins", { headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function addAdmin(email: string, token: string): Promise<{ status: string; message: string }> { |
| return request("/api/admin/admins/add", { method: "POST", body: { email }, headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function removeAdmin(userId: number, token: string): Promise<{ status: string }> { |
| return request(`/api/admin/admins/${userId}`, { method: "DELETE", headers: { "Authorization": `Bearer ${token}` } }); |
| } |
|
|
| export function getApiDocs(): Promise<any> { |
| return request("/api/docs"); |
| } |
|
|
| |
|
|
| export interface BillingConfig { |
| active_provider: string; |
| supported_providers: string[]; |
| } |
|
|
| export interface CheckoutResponse { |
| status: string; |
| url: string; |
| provider: string; |
| } |
|
|
| export interface PaymentStatusResponse { |
| status: string; |
| tier: string; |
| provider?: string; |
| timestamp?: string; |
| } |
|
|
| export function getBillingConfig(): Promise<BillingConfig> { |
| return request<BillingConfig>("/api/billing/config"); |
| } |
|
|
| export function createCheckoutSession( |
| email: string, |
| amount: number, |
| provider?: string, |
| successUrl?: string, |
| cancelUrl?: string |
| ): Promise<CheckoutResponse> { |
| return request<CheckoutResponse>("/api/billing/create-checkout-session", { |
| method: "POST", |
| body: { email, amount, provider, success_url: successUrl, cancel_url: cancelUrl } |
| }); |
| } |
|
|
| export function getPaymentStatus(email: string): Promise<PaymentStatusResponse> { |
| return request<PaymentStatusResponse>(`/api/billing/status/${email}`); |
| } |
|
|
| |
|
|
| export interface AuthResponse { |
| status: string; |
| token: string; |
| user: { |
| id: number; |
| email: string | null; |
| display_name: string; |
| is_guest: boolean; |
| tier: "free" | "pro" | "enterprise"; |
| has_passkey: boolean; |
| oauth_provider: string | null; |
| }; |
| } |
|
|
| |
| export function passkeyRegisterStart(displayName: string): Promise<{ options: any; challenge_key: string }> { |
| return request("/api/auth/passkey/register/start", { |
| method: "POST", |
| body: { display_name: displayName } |
| }); |
| } |
|
|
| export function passkeyRegisterFinish(challengeKey: string, credential: any): Promise<AuthResponse> { |
| return request("/api/auth/passkey/register/finish", { |
| method: "POST", |
| body: { challenge_key: challengeKey, credential } |
| }); |
| } |
|
|
| |
| export function passkeyLoginStart(): Promise<{ options: any; challenge_key: string }> { |
| return request("/api/auth/passkey/login/start", { method: "POST" }); |
| } |
|
|
| export function passkeyLoginFinish(challengeKey: string, credential: any): Promise<AuthResponse> { |
| return request("/api/auth/passkey/login/finish", { |
| method: "POST", |
| body: { challenge_key: challengeKey, credential } |
| }); |
| } |
|
|
| |
| export function oauthStart(provider: string, redirectUri: string): Promise<{ url: string; provider: string }> { |
| return request("/api/auth/oauth/start", { |
| method: "POST", |
| body: { provider, redirect_uri: redirectUri } |
| }); |
| } |
|
|
| export function oauthCallback(provider: string, code: string, redirectUri: string): Promise<AuthResponse> { |
| return request("/api/auth/oauth/callback", { |
| method: "POST", |
| body: { provider, code, redirect_uri: redirectUri } |
| }); |
| } |
|
|
| |
| export function authGuest(deviceUuid: string): Promise<AuthResponse> { |
| return request<AuthResponse>("/api/auth/guest", { |
| method: "POST", |
| body: { device_uuid: deviceUuid } |
| }); |
| } |
|
|
| export function getMe(token: string): Promise<AuthResponse> { |
| return request<AuthResponse>("/api/auth/me", { |
| headers: { "Authorization": `Bearer ${token}` } |
| }); |
| } |
|
|
| export { ApiError }; |