feat: add Layer 2 - WhatsApp Core logic, API Gateway models/auth, Frontend core

WhatsApp Core:
- SessionManager with Baileys integration for multi-account support
- Express server with REST API and Socket.IO for real-time events
- Session lifecycle management (create, disconnect, delete)
- Message sending with support for text, image, document, audio, video

API Gateway:
- Database models: User, WhatsAppAccount, Contact, Conversation, Message
- JWT authentication with access/refresh tokens
- Auth endpoints: login, refresh, register, me
- Pydantic schemas for request/response validation

Frontend:
- React 18 app structure with routing
- Zustand auth store with persistence
- API client with automatic token handling
- Base CSS and TypeScript declarations

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Claude AI
2026-01-29 09:55:10 +00:00
parent 31d68bc118
commit 7042aa2061
19 changed files with 827 additions and 0 deletions

View File

@@ -0,0 +1,79 @@
const API_BASE_URL = import.meta.env.VITE_API_URL || '/api';
interface RequestOptions extends RequestInit {
params?: Record<string, string>;
}
class ApiClient {
private baseUrl: string;
constructor(baseUrl: string) {
this.baseUrl = baseUrl;
}
private getAuthHeaders(): Record<string, string> {
const token = localStorage.getItem('access_token');
return token ? { Authorization: `Bearer ${token}` } : {};
}
private async request<T>(endpoint: string, options: RequestOptions = {}): Promise<T> {
const { params, ...fetchOptions } = options;
let url = `${this.baseUrl}${endpoint}`;
if (params) {
const searchParams = new URLSearchParams(params);
url += `?${searchParams.toString()}`;
}
const headers: Record<string, string> = {
'Content-Type': 'application/json',
...this.getAuthHeaders(),
...(options.headers as Record<string, string>),
};
const response = await fetch(url, {
...fetchOptions,
headers,
});
if (response.status === 401) {
localStorage.removeItem('access_token');
localStorage.removeItem('refresh_token');
window.location.href = '/login';
throw new Error('Unauthorized');
}
if (!response.ok) {
const error = await response.json().catch(() => ({ detail: 'Request failed' }));
throw new Error(error.detail || 'Request failed');
}
return response.json();
}
get<T>(endpoint: string, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'GET' });
}
post<T>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, {
...options,
method: 'POST',
body: JSON.stringify(data),
});
}
put<T>(endpoint: string, data?: unknown, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, {
...options,
method: 'PUT',
body: JSON.stringify(data),
});
}
delete<T>(endpoint: string, options?: RequestOptions): Promise<T> {
return this.request<T>(endpoint, { ...options, method: 'DELETE' });
}
}
export const apiClient = new ApiClient(API_BASE_URL);