import { env as privateEnv } from '$env/dynamic/private'; import { env as publicEnv } from '$env/dynamic/public'; // Convert .cloud URL to .site URL for HTTP actions function getHttpUrl(): string { const baseUrl = publicEnv.PUBLIC_CONVEX_URL ?? privateEnv.PUBLIC_CONVEX_URL; if (!baseUrl) { throw new Error('PUBLIC_CONVEX_URL is not set'); } return baseUrl.replace('.convex.cloud', '.convex.site'); } // Generic fetch helper for Convex HTTP endpoints (server-side only) async function convexFetch(path: string, body: Record = {}): Promise { const url = `${getHttpUrl()}${path}`; const response = await fetch(url, { method: 'POST', headers: { 'Content-Type': 'application/json', 'X-Convex-Secret': privateEnv.CONVEX_API_SECRET || '' }, body: JSON.stringify(body) }); if (!response.ok) { const error = await response.text(); throw new Error(`Convex HTTP error: ${response.status} - ${error}`); } return response.json(); } // ============ USERS ============ export const users = { getByNickname: (nickname: string) => convexFetch('/api/users/getByNickname', { nickname }), getById: (id: string) => convexFetch('/api/users/getById', { id }), create: (nickname: string, password: string) => convexFetch<{ userId: string }>('/api/users/create', { nickname, password }), updateStatus: (userId: string, status: string) => convexFetch('/api/users/updateStatus', { userId, status }), list: () => convexFetch('/api/users/list', {}), setAllOffline: () => convexFetch<{ updated: number }>('/api/users/setAllOffline', {}), updateLastReadMentionTimestamp: (userId: string, timestamp: number) => convexFetch<{ success: boolean }>('/api/users/updateLastReadMentionTimestamp', { userId, timestamp }) }; // ============ SESSIONS ============ export const sessions = { create: (tokenHash: string, userId: string, expiresAt: number) => convexFetch<{ id: string; tokenHash: string }>('/api/sessions/create', { tokenHash, userId, expiresAt }), validate: (tokenHash: string) => convexFetch<{ session: ConvexSession | null; user: ConvexUser | null }>( '/api/sessions/validate', { tokenHash } ), remove: (tokenHash: string) => convexFetch<{ success: boolean }>('/api/sessions/remove', { tokenHash }) }; // ============ CHAT ROOMS ============ export const chatRooms = { getOrCreateDefault: () => convexFetch('/api/chatRooms/getOrCreateDefault', {}), getById: (id: string) => convexFetch('/api/chatRooms/getById', { id }) }; // ============ MESSAGES ============ export const messages = { getByRoom: (roomId: string, limit?: number, beforeTimestamp?: number) => convexFetch<{ messages: ConvexMessage[]; hasMore: boolean }>('/api/messages/getByRoom', { roomId, limit, beforeTimestamp }), send: ( chatRoomId: string, senderId: string, content: string, type?: string, styleData?: string, hasFormatting?: boolean ) => convexFetch('/api/messages/send', { chatRoomId, senderId, content, type, styleData, hasFormatting }) }; // ============ TYPES ============ export interface ConvexUser { _id: string; _creationTime: number; password: string; nickname: string; status: string; avatarUrl?: string; createdAt: number; lastSeen?: number; lastReadMentionTimestamp?: number; } export interface ConvexSession { _id: string; _creationTime: number; tokenHash: string; userId: string; expiresAt: number; createdAt: number; } export interface ConvexMessage { _id: string; _creationTime: number; chatRoomId: string; senderId: string; content: string; type: string; timestamp: number; styleData?: string; hasFormatting?: boolean; } export interface ConvexChatRoom { _id: string; _creationTime: number; name?: string; type: string; createdAt: number; topic?: string; memberIds?: string[]; createdBy?: string; } export interface ConvexTextPreferences { _id: string; _creationTime: number; userId: string; defaultStyle: { fontFamily: string; fontSize: number; color?: string; gradient?: string[]; bold: boolean; italic: boolean; underline: boolean; strikethrough: boolean; }; allowFormatting: boolean; maxMessageLength: number; createdAt: number; updatedAt: number; } // ============ TEXT PREFERENCES ============ export const textPreferences = { get: (userId: string) => convexFetch('/api/textPreferences/get', { userId }), save: ( userId: string, defaultStyle: { fontFamily: string; fontSize: number; color?: string; gradient?: string[]; bold: boolean; italic: boolean; underline: boolean; strikethrough: boolean; }, allowFormatting: boolean, maxMessageLength: number ) => convexFetch<{ success: boolean; id: string }>('/api/textPreferences/save', { userId, defaultStyle, allowFormatting, maxMessageLength }) }; export default { users, sessions, messages, chatRooms, textPreferences };