Initial commit: deardiary project setup

This commit is contained in:
lotherk
2026-03-26 19:57:20 +00:00
commit 3f9bc1f484
73 changed files with 8627 additions and 0 deletions

172
frontend/src/lib/api.ts Normal file
View File

@@ -0,0 +1,172 @@
const API_BASE = '/api/v1';
interface ApiResponse<T> {
data: T | null;
error: { code: string; message: string } | null;
}
class ApiClient {
private apiKey: string | null = null;
setApiKey(key: string) {
this.apiKey = key;
localStorage.setItem('apiKey', key);
}
getApiKey(): string | null {
if (this.apiKey) return this.apiKey;
this.apiKey = localStorage.getItem('apiKey');
return this.apiKey;
}
clearApiKey() {
this.apiKey = null;
localStorage.removeItem('apiKey');
}
private async request<T>(
method: string,
path: string,
body?: unknown
): Promise<ApiResponse<T>> {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
};
if (this.getApiKey()) {
headers['Authorization'] = `Bearer ${this.getApiKey()}`;
}
const response = await fetch(`${API_BASE}${path}`, {
method,
headers,
body: body ? JSON.stringify(body) : undefined,
});
return response.json() as Promise<ApiResponse<T>>;
}
async register(email: string, password: string) {
return this.request<{ user: { id: string; email: string } }>('POST', '/auth/register', { email, password });
}
async login(email: string, password: string) {
return this.request<{ token: string; userId: string }>('POST', '/auth/login', { email, password });
}
async createApiKey(name: string, token: string) {
const headers: Record<string, string> = {
'Content-Type': 'application/json',
'Authorization': `Bearer ${token}`,
};
const response = await fetch(`${API_BASE}/auth/api-key`, {
method: 'POST',
headers,
body: JSON.stringify({ name }),
});
return response.json() as Promise<ApiResponse<{ apiKey: string }>>;
}
async getDays() {
return this.request<Array<{ date: string; entryCount: number; hasJournal: boolean }>>('GET', '/days');
}
async getDay(date: string) {
return this.request<{ date: string; entries: Entry[]; journal: Journal | null }>('GET', `/days/${date}`);
}
async deleteDay(date: string) {
return this.request<{ deleted: boolean }>('DELETE', `/days/${date}`);
}
async createEntry(date: string, type: string, content: string, metadata?: object) {
return this.request<Entry>('POST', '/entries', { date, type, content, metadata });
}
async updateEntry(id: string, content: string, metadata?: object) {
return this.request<Entry>('PUT', `/entries/${id}`, { content, metadata });
}
async deleteEntry(id: string) {
return this.request<{ deleted: boolean }>('DELETE', `/entries/${id}`);
}
async uploadPhoto(entryId: string, file: File) {
const formData = new FormData();
formData.append('file', file);
const headers: Record<string, string> = {};
if (this.getApiKey()) {
headers['Authorization'] = `Bearer ${this.getApiKey()}`;
}
const response = await fetch(`${API_BASE}/entries/${entryId}/photo`, {
method: 'POST',
headers,
body: formData,
});
return response.json() as Promise<ApiResponse<{ mediaPath: string }>>;
}
async uploadVoice(entryId: string, file: File) {
const formData = new FormData();
formData.append('file', file);
const headers: Record<string, string> = {};
if (this.getApiKey()) {
headers['Authorization'] = `Bearer ${this.getApiKey()}`;
}
const response = await fetch(`${API_BASE}/entries/${entryId}/voice`, {
method: 'POST',
headers,
body: formData,
});
return response.json() as Promise<ApiResponse<{ mediaPath: string }>>;
}
async generateJournal(date: string) {
return this.request<Journal>('POST', `/journal/generate/${date}`);
}
async getJournal(date: string) {
return this.request<Journal>('GET', `/journal/${date}`);
}
async getSettings() {
return this.request<Settings>('GET', '/settings');
}
async updateSettings(settings: Partial<Settings>) {
return this.request<Settings>('PUT', '/settings', settings);
}
}
export interface Entry {
id: string;
date: string;
type: 'text' | 'voice' | 'photo' | 'health' | 'location';
content: string;
mediaPath?: string;
metadata?: string;
createdAt: string;
}
export interface Journal {
id: string;
date: string;
content: string;
entryCount: number;
generatedAt: string;
}
export interface Settings {
aiProvider: 'openai' | 'anthropic' | 'ollama' | 'lmstudio';
aiApiKey?: string;
aiModel: string;
aiBaseUrl?: string;
journalPrompt: string;
language: string;
}
export const api = new ApiClient();