update flow and auth model

This commit is contained in:
Ada
2026-01-28 16:27:00 -08:00
parent 4d94888bb8
commit 146320052e
12 changed files with 1997 additions and 597 deletions

146
src/config/index.ts Normal file
View File

@@ -0,0 +1,146 @@
/**
* Application Configuration
*
* This file contains all configuration settings for the frontend application.
* Centralized configuration makes it easier to manage environment-specific settings.
*/
// =============================================================================
// Environment Configuration
// =============================================================================
/**
* Set to true to use mock data instead of real backend calls.
* Useful for development and testing without a running backend.
*/
export const NO_BACKEND_MODE = false;
/**
* Enable debug logging for API calls
*/
export const DEBUG_MODE = true;
// =============================================================================
// API Configuration
// =============================================================================
/**
* Base URL for the backend API server
*/
export const API_BASE_URL = 'http://localhost:8000';
/**
* API request timeout in milliseconds
*/
export const API_TIMEOUT = 30000;
/**
* API Endpoints
* All backend API routes are defined here for easy reference and maintenance.
*/
export const API_ENDPOINTS = {
// Authentication
AUTH: {
LOGIN: '/login',
REGISTER: '/register',
},
// Assets Management
ASSETS: {
GET: '/assets/get',
CREATE: '/assets/create',
CLAIM: '/assets/claim',
ASSIGN: '/assets/assign',
},
// AI Services
AI: {
PROXY: '/ai/proxy',
},
// Admin Operations
ADMIN: {
DECLARE_GUALE: '/admin/declare-guale',
},
} as const;
// =============================================================================
// Helper Functions
// =============================================================================
/**
* Build full API URL from endpoint
* @param endpoint - API endpoint path (e.g., '/login')
* @returns Full URL (e.g., 'http://192.168.56.103:8000/login')
*/
export function buildApiUrl(endpoint: string): string {
return `${API_BASE_URL}${endpoint}`;
}
/**
* Get default headers for API requests
* @param token - Optional JWT token for authenticated requests
* @returns Headers object
*/
export function getApiHeaders(token?: string): Record<string, string> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (token) {
headers['Authorization'] = `Bearer ${token}`;
}
return headers;
}
/**
* Log API debug information
* Only logs when DEBUG_MODE is enabled
*/
export function logApiDebug(label: string, data: unknown): void {
if (DEBUG_MODE) {
console.log(`[API Debug] ${label}:`, data);
}
}
// =============================================================================
// Mock User Configuration (for NO_BACKEND_MODE)
// =============================================================================
export const MOCK_CONFIG = {
USER: {
id: 999,
username: 'MockCaptain',
public_key: 'mock_public_key',
is_admin: true,
guale: false,
tier: 'premium',
tier_expires_at: '2026-12-31T23:59:59Z',
last_active_at: new Date().toISOString(),
},
ACCESS_TOKEN: 'mock_access_token',
RESPONSE_DELAY: 200, // milliseconds
} as const;
// =============================================================================
// AI Service Configuration
// =============================================================================
export const AI_CONFIG = {
/**
* Default system prompt for AI conversations
*/
DEFAULT_SYSTEM_PROMPT: 'You are a helpful journal assistant. Help the user reflect on their thoughts and feelings.',
/**
* Mock response delay in milliseconds (for NO_BACKEND_MODE)
*/
MOCK_RESPONSE_DELAY: 500,
} as const;
// =============================================================================
// Export Type Definitions
// =============================================================================
export type ApiEndpoint = typeof API_ENDPOINTS;

View File

@@ -1,66 +1,175 @@
import React, { createContext, useContext, useState, ReactNode } from 'react';
/**
* AuthContext - Authentication State Management
*
* Provides authentication state and methods throughout the app.
* Uses AsyncStorage for persistent login state.
*/
import React, { createContext, useContext, useState, useEffect, ReactNode } from 'react';
import AsyncStorage from '@react-native-async-storage/async-storage';
import { User, LoginRequest, RegisterRequest } from '../types';
import { authService } from '../services/auth.service';
import { Alert } from 'react-native';
// =============================================================================
// Type Definitions
// =============================================================================
interface AuthContextType {
user: User | null;
token: string | null;
isLoading: boolean;
signIn: (credentials: LoginRequest) => Promise<void>;
signUp: (data: RegisterRequest) => Promise<void>;
signOut: () => void;
user: User | null;
token: string | null;
isLoading: boolean;
isInitializing: boolean;
signIn: (credentials: LoginRequest) => Promise<void>;
signUp: (data: RegisterRequest) => Promise<void>;
signOut: () => void;
}
// Storage keys
const STORAGE_KEYS = {
TOKEN: '@auth_token',
USER: '@auth_user',
};
// =============================================================================
// Context
// =============================================================================
const AuthContext = createContext<AuthContextType | undefined>(undefined);
// =============================================================================
// Provider Component
// =============================================================================
export function AuthProvider({ children }: { children: ReactNode }) {
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [user, setUser] = useState<User | null>(null);
const [token, setToken] = useState<string | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [isInitializing, setIsInitializing] = useState(true);
const signIn = async (credentials: LoginRequest) => {
setIsLoading(true);
try {
const response = await authService.login(credentials);
setToken(response.access_token);
setUser(response.user);
} catch (error) {
throw error;
} finally {
setIsLoading(false);
}
};
// Load saved auth state on app start
useEffect(() => {
loadStoredAuth();
}, []);
const signUp = async (data: RegisterRequest) => {
setIsLoading(true);
try {
await authService.register(data);
// After successful registration, sign in automatically
await signIn({ username: data.username, password: data.password });
} catch (error) {
throw error;
} finally {
setIsLoading(false);
}
};
/**
* Load stored authentication from AsyncStorage
*/
const loadStoredAuth = async () => {
try {
const [storedToken, storedUser] = await Promise.all([
AsyncStorage.getItem(STORAGE_KEYS.TOKEN),
AsyncStorage.getItem(STORAGE_KEYS.USER),
]);
const signOut = () => {
setUser(null);
setToken(null);
};
if (storedToken && storedUser) {
setToken(storedToken);
setUser(JSON.parse(storedUser));
console.log('[Auth] Restored session for user:', JSON.parse(storedUser).username);
}
} catch (error) {
console.error('[Auth] Failed to load stored auth:', error);
} finally {
setIsInitializing(false);
}
};
return (
<AuthContext.Provider value={{ user, token, isLoading, signIn, signUp, signOut }}>
{children}
</AuthContext.Provider>
);
/**
* Save authentication to AsyncStorage
*/
const saveAuth = async (authToken: string, authUser: User) => {
try {
await Promise.all([
AsyncStorage.setItem(STORAGE_KEYS.TOKEN, authToken),
AsyncStorage.setItem(STORAGE_KEYS.USER, JSON.stringify(authUser)),
]);
console.log('[Auth] Session saved for user:', authUser.username);
} catch (error) {
console.error('[Auth] Failed to save auth:', error);
}
};
/**
* Clear authentication from AsyncStorage
*/
const clearAuth = async () => {
try {
await Promise.all([
AsyncStorage.removeItem(STORAGE_KEYS.TOKEN),
AsyncStorage.removeItem(STORAGE_KEYS.USER),
]);
console.log('[Auth] Session cleared');
} catch (error) {
console.error('[Auth] Failed to clear auth:', error);
}
};
/**
* Sign in with username and password
*/
const signIn = async (credentials: LoginRequest) => {
setIsLoading(true);
try {
const response = await authService.login(credentials);
setToken(response.access_token);
setUser(response.user);
await saveAuth(response.access_token, response.user);
} catch (error) {
throw error;
} finally {
setIsLoading(false);
}
};
/**
* Sign up and automatically sign in
*/
const signUp = async (data: RegisterRequest) => {
setIsLoading(true);
try {
await authService.register(data);
// After successful registration, sign in automatically
await signIn({ username: data.username, password: data.password });
} catch (error) {
throw error;
} finally {
setIsLoading(false);
}
};
/**
* Sign out and clear stored auth
*/
const signOut = () => {
setUser(null);
setToken(null);
clearAuth();
};
return (
<AuthContext.Provider
value={{
user,
token,
isLoading,
isInitializing,
signIn,
signUp,
signOut
}}
>
{children}
</AuthContext.Provider>
);
}
// =============================================================================
// Hook
// =============================================================================
export function useAuth() {
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
const context = useContext(AuthContext);
if (context === undefined) {
throw new Error('useAuth must be used within an AuthProvider');
}
return context;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,79 @@
/**
* Admin Service
*
* Handles admin-only API operations.
*/
import {
NO_BACKEND_MODE,
API_ENDPOINTS,
MOCK_CONFIG,
buildApiUrl,
getApiHeaders,
logApiDebug,
} from '../config';
// =============================================================================
// Type Definitions
// =============================================================================
export interface DeclareGualeRequest {
username: string;
}
export interface DeclareGualeResponse {
message: string;
username: string;
guale: boolean;
}
// =============================================================================
// Admin Service
// =============================================================================
export const adminService = {
/**
* Declare a user as deceased (guale)
* Admin only operation
* @param request - Username to declare as deceased
* @param token - JWT token for authentication (must be admin)
* @returns Success response
*/
async declareGuale(request: DeclareGualeRequest, token: string): Promise<DeclareGualeResponse> {
if (NO_BACKEND_MODE) {
logApiDebug('Declare Guale', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
message: `User ${request.username} has been declared as deceased`,
username: request.username,
guale: true,
});
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.ADMIN.DECLARE_GUALE);
logApiDebug('Declare Guale URL', url);
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify(request),
});
logApiDebug('Declare Guale Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Failed to declare user as deceased');
}
return await response.json();
} catch (error) {
console.error('Declare guale error:', error);
throw error;
}
},
};

243
src/services/ai.service.ts Normal file
View File

@@ -0,0 +1,243 @@
/**
* AI Service
*
* Handles communication with the AI proxy endpoint for chat completions.
*/
import {
NO_BACKEND_MODE,
API_ENDPOINTS,
AI_CONFIG,
buildApiUrl,
getApiHeaders,
logApiDebug,
} from '../config';
// =============================================================================
// Type Definitions
// =============================================================================
export interface AIMessage {
role: 'system' | 'user' | 'assistant';
content: string;
}
export interface AIRequest {
messages: AIMessage[];
model?: string;
}
export interface AIResponse {
id: string;
object: string;
created: number;
model: string;
choices: Array<{
index: number;
message: AIMessage;
finish_reason: string;
}>;
usage: {
prompt_tokens: number;
completion_tokens: number;
total_tokens: number;
};
}
// =============================================================================
// Mock Response Generator
// =============================================================================
const createMockResponse = (userMessage: string): AIResponse => {
return {
id: `mock-${Date.now()}`,
object: 'chat.completion',
created: Math.floor(Date.now() / 1000),
model: 'mock-model',
choices: [
{
index: 0,
message: {
role: 'assistant',
content: `I received your message: "${userMessage}". This is a mock response since the backend is not connected.`,
},
finish_reason: 'stop',
},
],
usage: {
prompt_tokens: 10,
completion_tokens: 20,
total_tokens: 30,
},
};
};
// =============================================================================
// AI Service
// =============================================================================
export const aiService = {
/**
* Send chat messages to the AI proxy
* @param messages - Array of chat messages
* @param token - JWT token for authentication
* @returns AI response
*/
async chat(messages: AIMessage[], token?: string): Promise<AIResponse> {
if (NO_BACKEND_MODE) {
logApiDebug('AI Chat', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
const lastUserMessage = messages.filter((m) => m.role === 'user').pop();
resolve(createMockResponse(lastUserMessage?.content || 'Hello'));
}, AI_CONFIG.MOCK_RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.AI.PROXY);
logApiDebug('AI Request', {
url,
hasToken: !!token,
messageCount: messages.length,
});
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify({ messages } as AIRequest),
});
logApiDebug('AI Response Status', response.status);
if (!response.ok) {
const errorText = await response.text();
logApiDebug('AI Error Response', errorText);
let errorDetail = 'AI request failed';
try {
const errorData = JSON.parse(errorText);
errorDetail = errorData.detail || errorDetail;
} catch {
errorDetail = errorText || errorDetail;
}
throw new Error(`${response.status}: ${errorDetail}`);
}
const data = await response.json();
logApiDebug('AI Success', {
id: data.id,
model: data.model,
choicesCount: data.choices?.length,
});
return data;
} catch (error) {
console.error('AI proxy error:', error);
throw error;
}
},
/**
* Simple helper for single message chat
* @param content - User message content
* @param token - JWT token for authentication
* @returns AI response text
*/
async sendMessage(content: string, token?: string): Promise<string> {
const messages: AIMessage[] = [
{
role: 'system',
content: AI_CONFIG.DEFAULT_SYSTEM_PROMPT,
},
{
role: 'user',
content,
},
];
const response = await this.chat(messages, token);
return response.choices[0]?.message?.content || 'No response';
},
/**
* Send a message with an image to AI for analysis
* @param content - User message content
* @param imageBase64 - Base64 encoded image data
* @param token - JWT token for authentication
* @returns AI response text
*/
async sendMessageWithImage(content: string, imageBase64: string, token?: string): Promise<string> {
if (NO_BACKEND_MODE) {
logApiDebug('AI Image Analysis', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve('This is a mock image analysis response. The image appears to show an interesting scene. In production, this would be analyzed by Gemini AI.');
}, AI_CONFIG.MOCK_RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.AI.PROXY);
logApiDebug('AI Image Request', {
url,
hasToken: !!token,
hasImage: !!imageBase64,
});
// Gemini vision format - using multimodal content
const messages = [
{
role: 'user',
content: [
{
type: 'text',
text: content,
},
{
type: 'image_url',
image_url: {
url: `data:image/jpeg;base64,${imageBase64}`,
},
},
],
},
];
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify({ messages }),
});
logApiDebug('AI Image Response Status', response.status);
if (!response.ok) {
const errorText = await response.text();
logApiDebug('AI Image Error Response', errorText);
let errorDetail = 'AI image request failed';
try {
const errorData = JSON.parse(errorText);
errorDetail = errorData.detail || errorDetail;
} catch {
errorDetail = errorText || errorDetail;
}
throw new Error(`${response.status}: ${errorDetail}`);
}
const data = await response.json();
logApiDebug('AI Image Success', {
id: data.id,
model: data.model,
});
return data.choices[0]?.message?.content || 'No response';
} catch (error) {
console.error('AI image proxy error:', error);
throw error;
}
},
};

View File

@@ -0,0 +1,243 @@
/**
* Assets Service
*
* Handles all asset-related API operations including CRUD and inheritance.
*/
import {
NO_BACKEND_MODE,
API_ENDPOINTS,
MOCK_CONFIG,
buildApiUrl,
getApiHeaders,
logApiDebug,
} from '../config';
// =============================================================================
// Type Definitions
// =============================================================================
export interface Asset {
id: number;
title: string;
author_id: number;
private_key_shard: string;
content_outer_encrypted: string;
}
export interface AssetCreate {
title: string;
private_key_shard: string;
content_inner_encrypted: string;
}
export interface AssetClaim {
asset_id: number;
private_key_shard: string;
}
export interface AssetClaimResponse {
asset_id: number;
title: string;
decrypted_content: string;
server_shard_key: string;
}
export interface AssetAssign {
asset_id: number;
heir_name: string;
}
// =============================================================================
// Mock Data
// =============================================================================
const MOCK_ASSETS: Asset[] = [
{
id: 1,
title: 'Mock Asset 1',
author_id: MOCK_CONFIG.USER.id,
private_key_shard: 'mock_shard_1',
content_outer_encrypted: 'mock_encrypted_content_1',
},
{
id: 2,
title: 'Mock Asset 2',
author_id: MOCK_CONFIG.USER.id,
private_key_shard: 'mock_shard_2',
content_outer_encrypted: 'mock_encrypted_content_2',
},
];
// =============================================================================
// Assets Service
// =============================================================================
export const assetsService = {
/**
* Get all assets for the current user
* @param token - JWT token for authentication
* @returns Array of user's assets
*/
async getMyAssets(token: string): Promise<Asset[]> {
if (NO_BACKEND_MODE) {
logApiDebug('Get Assets', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => resolve(MOCK_ASSETS), MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.ASSETS.GET);
logApiDebug('Get Assets URL', url);
try {
const response = await fetch(url, {
method: 'GET',
headers: getApiHeaders(token),
});
logApiDebug('Get Assets Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Failed to fetch assets');
}
return await response.json();
} catch (error) {
console.error('Get assets error:', error);
throw error;
}
},
/**
* Create a new asset
* @param asset - Asset creation data
* @param token - JWT token for authentication
* @returns Created asset
*/
async createAsset(asset: AssetCreate, token: string): Promise<Asset> {
if (NO_BACKEND_MODE) {
logApiDebug('Create Asset', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
id: Date.now(),
title: asset.title,
author_id: MOCK_CONFIG.USER.id,
private_key_shard: asset.private_key_shard,
content_outer_encrypted: asset.content_inner_encrypted,
});
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.ASSETS.CREATE);
logApiDebug('Create Asset URL', url);
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify(asset),
});
logApiDebug('Create Asset Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Failed to create asset');
}
return await response.json();
} catch (error) {
console.error('Create asset error:', error);
throw error;
}
},
/**
* Claim an inherited asset
* @param claim - Asset claim data
* @param token - JWT token for authentication
* @returns Claimed asset with decrypted content
*/
async claimAsset(claim: AssetClaim, token: string): Promise<AssetClaimResponse> {
if (NO_BACKEND_MODE) {
logApiDebug('Claim Asset', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
asset_id: claim.asset_id,
title: 'Mock Claimed Asset',
decrypted_content: 'This is the decrypted content of the claimed asset.',
server_shard_key: 'mock_server_shard',
});
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.ASSETS.CLAIM);
logApiDebug('Claim Asset URL', url);
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify(claim),
});
logApiDebug('Claim Asset Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Failed to claim asset');
}
return await response.json();
} catch (error) {
console.error('Claim asset error:', error);
throw error;
}
},
/**
* Assign an asset to an heir
* @param assignment - Asset assignment data
* @param token - JWT token for authentication
* @returns Success message
*/
async assignAsset(assignment: AssetAssign, token: string): Promise<{ message: string }> {
if (NO_BACKEND_MODE) {
logApiDebug('Assign Asset', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({ message: `Asset assigned to ${assignment.heir_name}` });
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.ASSETS.ASSIGN);
logApiDebug('Assign Asset URL', url);
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(token),
body: JSON.stringify(assignment),
});
logApiDebug('Assign Asset Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Failed to assign asset');
}
return await response.json();
} catch (error) {
console.error('Assign asset error:', error);
throw error;
}
},
};

View File

@@ -1,80 +1,87 @@
import { LoginRequest, LoginResponse, RegisterRequest, User } from '../types';
const PLATFORM_URL = 'http://192.168.56.103:8000';
const no_backend_mode = true;
const MOCK_USER: User = {
id: 999,
username: 'MockCaptain',
public_key: 'mock_public_key',
is_admin: true,
guale: false,
tier: 'premium',
tier_expires_at: '2026-12-31T23:59:59Z',
last_active_at: new Date().toISOString(),
};
import {
NO_BACKEND_MODE,
API_ENDPOINTS,
MOCK_CONFIG,
buildApiUrl,
getApiHeaders,
logApiDebug,
} from '../config';
export const authService = {
async login(credentials: LoginRequest): Promise<LoginResponse> {
if (no_backend_mode) {
console.log('No-Backend Mode: Simulating login...');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
access_token: 'mock_access_token',
token_type: 'bearer',
user: { ...MOCK_USER, username: credentials.username },
});
}, 200);
});
}
try {
const response = await fetch(`${PLATFORM_URL}/login`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(credentials),
});
async login(credentials: LoginRequest): Promise<LoginResponse> {
if (NO_BACKEND_MODE) {
logApiDebug('Login', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({
access_token: MOCK_CONFIG.ACCESS_TOKEN,
token_type: 'bearer',
user: { ...MOCK_CONFIG.USER, username: credentials.username } as User,
});
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
if (!response.ok) {
throw new Error('Login failed');
}
const url = buildApiUrl(API_ENDPOINTS.AUTH.LOGIN);
logApiDebug('Login URL', url);
return await response.json();
} catch (error) {
console.error('Login error:', error);
throw error;
}
},
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(),
body: JSON.stringify(credentials),
});
async register(data: RegisterRequest): Promise<User> {
if (no_backend_mode) {
console.log('No-Backend Mode: Simulating registration...');
return new Promise((resolve) => {
setTimeout(() => {
resolve({ ...MOCK_USER, username: data.username });
}, 200);
});
}
try {
const response = await fetch(`${PLATFORM_URL}/register`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(data),
});
logApiDebug('Login Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail?.[0]?.msg || 'Registration failed');
}
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail || 'Login failed');
}
return await response.json();
} catch (error) {
console.error('Registration error:', error);
throw error;
}
},
const data = await response.json();
logApiDebug('Login Success', { username: data.user?.username });
return data;
} catch (error) {
console.error('Login error:', error);
throw error;
}
},
async register(data: RegisterRequest): Promise<User> {
if (NO_BACKEND_MODE) {
logApiDebug('Register', 'Using mock mode');
return new Promise((resolve) => {
setTimeout(() => {
resolve({ ...MOCK_CONFIG.USER, username: data.username } as User);
}, MOCK_CONFIG.RESPONSE_DELAY);
});
}
const url = buildApiUrl(API_ENDPOINTS.AUTH.REGISTER);
logApiDebug('Register URL', url);
try {
const response = await fetch(url, {
method: 'POST',
headers: getApiHeaders(),
body: JSON.stringify(data),
});
logApiDebug('Register Response Status', response.status);
if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(errorData.detail?.[0]?.msg || errorData.detail || 'Registration failed');
}
const result = await response.json();
logApiDebug('Register Success', { username: result.username });
return result;
} catch (error) {
console.error('Registration error:', error);
throw error;
}
},
};

25
src/services/index.ts Normal file
View File

@@ -0,0 +1,25 @@
/**
* Services Index
*
* Central export for all API services.
* Import services from here for cleaner imports.
*
* Usage:
* import { authService, aiService, assetsService, adminService } from '../services';
*/
export { authService } from './auth.service';
export { aiService, type AIMessage, type AIRequest, type AIResponse } from './ai.service';
export {
assetsService,
type Asset,
type AssetCreate,
type AssetClaim,
type AssetClaimResponse,
type AssetAssign
} from './assets.service';
export {
adminService,
type DeclareGualeRequest,
type DeclareGualeResponse
} from './admin.service';