206 lines
4.3 KiB
TypeScript
206 lines
4.3 KiB
TypeScript
/**
|
|
* API Service Layer
|
|
* Handles all communication with the backend API
|
|
*/
|
|
|
|
const API_BASE_URL = import.meta.env.VITE_API_URL || 'http://localhost:8001'
|
|
|
|
type ApiOptions = {
|
|
method?: 'GET' | 'POST' | 'PUT' | 'DELETE'
|
|
body?: unknown
|
|
token?: string | null
|
|
}
|
|
|
|
async function apiCall<T>(
|
|
endpoint: string,
|
|
options: ApiOptions = {}
|
|
): Promise<T> {
|
|
const { method = 'GET', body, token } = options
|
|
|
|
const headers: HeadersInit = {
|
|
'Content-Type': 'application/json',
|
|
}
|
|
|
|
if (token) {
|
|
headers['Authorization'] = `Bearer ${token}`
|
|
}
|
|
|
|
const config: RequestInit = {
|
|
method,
|
|
headers,
|
|
credentials: 'include',
|
|
}
|
|
|
|
if (body) {
|
|
config.body = JSON.stringify(body)
|
|
}
|
|
|
|
const response = await fetch(`${API_BASE_URL}${endpoint}`, config)
|
|
|
|
if (!response.ok) {
|
|
const error = await response.json().catch(() => ({}))
|
|
throw new Error(error.detail || `API error: ${response.statusText}`)
|
|
}
|
|
|
|
return response.json() as Promise<T>
|
|
}
|
|
|
|
// ============================================
|
|
// USER ENDPOINTS
|
|
// ============================================
|
|
|
|
export async function registerUser(
|
|
userData: {
|
|
email: string
|
|
displayName?: string
|
|
photoURL?: string
|
|
},
|
|
token: string
|
|
) {
|
|
return apiCall('/api/users/register', {
|
|
method: 'POST',
|
|
body: userData,
|
|
token,
|
|
})
|
|
}
|
|
|
|
export async function getUserByEmail(email: string, token: string) {
|
|
return apiCall(`/api/users/by-email/${email}`, { token })
|
|
}
|
|
|
|
export async function updateUserProfile(
|
|
userId: string,
|
|
updates: { displayName?: string; photoURL?: string; theme?: string },
|
|
token: string
|
|
) {
|
|
return apiCall(`/api/users/update/${userId}`, {
|
|
method: 'PUT',
|
|
body: updates,
|
|
token,
|
|
})
|
|
}
|
|
|
|
export async function deleteUser(userId: string, token: string) {
|
|
return apiCall<{ message: string; user_deleted: number; entries_deleted: number }>(
|
|
`/api/users/${userId}`,
|
|
{
|
|
method: 'DELETE',
|
|
token,
|
|
}
|
|
)
|
|
}
|
|
|
|
// ============================================
|
|
// ENTRY ENDPOINTS
|
|
// ============================================
|
|
|
|
export interface EncryptionMetadata {
|
|
encrypted: boolean
|
|
ciphertext?: string // Base64-encoded encrypted content
|
|
nonce?: string // Base64-encoded nonce
|
|
algorithm?: string // e.g., "XSalsa20-Poly1305"
|
|
}
|
|
|
|
export interface JournalEntryCreate {
|
|
title?: string // Optional if encrypted
|
|
content?: string // Optional if encrypted
|
|
mood?: string
|
|
tags?: string[]
|
|
isPublic?: boolean
|
|
encryption?: EncryptionMetadata
|
|
}
|
|
|
|
export interface JournalEntry extends JournalEntryCreate {
|
|
id: string
|
|
userId: string
|
|
createdAt: string
|
|
updatedAt: string
|
|
entryDate?: string
|
|
encryption?: EncryptionMetadata
|
|
}
|
|
|
|
export async function createEntry(
|
|
userId: string,
|
|
entryData: JournalEntryCreate,
|
|
token: string
|
|
) {
|
|
return apiCall<{ id: string; message: string }>(
|
|
`/api/entries/${userId}`,
|
|
{
|
|
method: 'POST',
|
|
body: entryData,
|
|
token,
|
|
}
|
|
)
|
|
}
|
|
|
|
export async function getUserEntries(
|
|
userId: string,
|
|
token: string,
|
|
limit = 50,
|
|
skip = 0
|
|
) {
|
|
return apiCall<{ entries: JournalEntry[]; total: number }>(
|
|
`/api/entries/${userId}?limit=${limit}&skip=${skip}`,
|
|
{ token }
|
|
)
|
|
}
|
|
|
|
export async function getEntry(
|
|
userId: string,
|
|
entryId: string,
|
|
token: string
|
|
) {
|
|
return apiCall<JournalEntry>(`/api/entries/${userId}/${entryId}`, {
|
|
token,
|
|
})
|
|
}
|
|
|
|
export async function updateEntry(
|
|
userId: string,
|
|
entryId: string,
|
|
updates: Partial<JournalEntryCreate>,
|
|
token: string
|
|
) {
|
|
return apiCall(`/api/entries/${userId}/${entryId}`, {
|
|
method: 'PUT',
|
|
body: updates,
|
|
token,
|
|
})
|
|
}
|
|
|
|
export async function deleteEntry(
|
|
userId: string,
|
|
entryId: string,
|
|
token: string
|
|
) {
|
|
return apiCall(`/api/entries/${userId}/${entryId}`, {
|
|
method: 'DELETE',
|
|
token,
|
|
})
|
|
}
|
|
|
|
export async function getEntriesByDate(
|
|
userId: string,
|
|
startDate: string,
|
|
endDate: string,
|
|
token: string
|
|
) {
|
|
return apiCall<JournalEntry[]>(
|
|
`/api/entries/${userId}/date-range?startDate=${startDate}&endDate=${endDate}`,
|
|
{ token }
|
|
)
|
|
}
|
|
// ============================================
|
|
// TIMEZONE CONVERSION ENDPOINTS
|
|
// ============================================
|
|
|
|
export async function convertUTCToIST(utcTimestamp: string) {
|
|
return apiCall<{ utc: string; ist: string }>(
|
|
`/api/entries/convert-timestamp/utc-to-ist`,
|
|
{
|
|
method: 'POST',
|
|
body: { timestamp: utcTimestamp },
|
|
}
|
|
)
|
|
} |