Files
gh-christianlouis-inboxconv…/frontend/src/lib/api.ts
T
copilot-swe-agent[bot] 2e3b56098a fix: wizard grey screen, missing form fields, and implement settings page
- Fix Tailwind v4 modal overlay: bg-opacity-75 removed, use bg-gray-500/75
- Restructure AddMailAccountModal with flexbox + relative z-10
- Add missing required fields to form: forward_to, delivery_method,
  delete_after_forward; auto-sync email_address from username
- Fix DashboardLayout mobile overlay opacity (same Tailwind v4 issue)
- Make email_address/forward_to required in MailAccountCreate interface
- Add userApi.updateProfile() method
- Implement Settings page: profile, account info, security sections
- Update CHANGELOG.md and docs/TODO.md

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
Agent-Logs-Url: https://github.com/christianlouis/pop_puller_to_gmail/sessions/d3a9e3bc-5637-45c5-b674-8ef436953614
2026-03-25 09:32:56 +00:00

249 lines
6.8 KiB
TypeScript

import axios from "axios";
const api = axios.create({
baseURL: `/api/v1`,
headers: {
"Content-Type": "application/json",
},
});
// Attach auth token to every request
api.interceptors.request.use((config) => {
if (typeof window !== "undefined") {
const token = localStorage.getItem("access_token");
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
}
return config;
});
// Handle 401 responses by clearing auth state
api.interceptors.response.use(
(response) => response,
(error) => {
if (error.response?.status === 401 && typeof window !== "undefined") {
localStorage.removeItem("access_token");
localStorage.removeItem("user");
window.location.href = "/login";
}
return Promise.reject(error);
}
);
// ── Types ───────────────────────────────────────────────────────────────
export interface User {
id: number;
email: string;
full_name: string | null;
is_active: boolean;
subscription_tier: string;
subscription_status: string;
created_at: string;
google_id?: string | null;
oauth_provider?: string | null;
stripe_customer_id?: string | null;
subscription_expires_at?: string | null;
last_login_at?: string | null;
}
export interface MailAccount {
id: number;
user_id: number;
name: string;
email_address: string;
protocol: string;
host: string;
port: number;
use_ssl: boolean;
use_tls: boolean;
forward_to: string;
delivery_method: string;
is_enabled: boolean;
check_interval_minutes: number;
max_emails_per_check: number;
delete_after_forward: boolean;
status: string;
provider_name?: string | null;
auto_detected: boolean;
total_emails_processed: number;
total_emails_failed: number;
last_check_at?: string | null;
last_successful_check_at?: string | null;
last_error_at?: string | null;
last_error_message?: string | null;
created_at: string;
updated_at: string;
}
export interface MailAccountCreate {
name: string;
email_address: string;
protocol: string;
host: string;
port: number;
use_ssl: boolean;
use_tls?: boolean;
username: string;
password: string;
forward_to: string;
delivery_method?: string;
is_enabled?: boolean;
check_interval_minutes?: number;
max_emails_per_check?: number;
delete_after_forward?: boolean;
}
export interface ProcessingRun {
id: number;
mail_account_id: number;
started_at: string;
completed_at?: string | null;
duration_seconds?: number | null;
emails_fetched: number;
emails_forwarded: number;
emails_failed: number;
status: string;
error_message?: string | null;
}
export interface AutoDetectSuggestion {
protocol?: string;
host?: string;
port?: number;
use_ssl?: boolean;
[key: string]: unknown;
}
interface TokenResponse {
access_token: string;
refresh_token: string;
token_type: string;
user?: User;
}
// ── Auth API ────────────────────────────────────────────────────────────
export const authApi = {
async login(credentials: {
username: string;
password: string;
}): Promise<TokenResponse> {
const formData = new URLSearchParams();
formData.append("username", credentials.username);
formData.append("password", credentials.password);
const response = await api.post<TokenResponse>("/auth/login", formData, {
headers: { "Content-Type": "application/x-www-form-urlencoded" },
});
return response.data;
},
async register(data: {
email: string;
password: string;
full_name?: string;
}): Promise<User> {
const response = await api.post<User>("/auth/register", data);
return response.data;
},
async googleAuth(
code: string,
redirectUri: string
): Promise<TokenResponse> {
const response = await api.post<TokenResponse>("/auth/google", {
code,
redirect_uri: redirectUri,
});
return response.data;
},
async getGoogleAuthUrl(redirectUri: string): Promise<string> {
const response = await api.get<{ authorization_url: string }>(
"/auth/google/authorize-url",
{ params: { redirect_uri: redirectUri } }
);
return response.data.authorization_url;
},
};
// ── User API ────────────────────────────────────────────────────────────
export const userApi = {
async getCurrentUser(): Promise<User> {
const response = await api.get<User>("/users/me");
return response.data;
},
async updateProfile(data: { full_name?: string; email?: string }): Promise<User> {
const response = await api.put<User>("/users/me", data);
return response.data;
},
};
// ── Mail Accounts API ───────────────────────────────────────────────────
export const mailAccountsApi = {
async list(): Promise<MailAccount[]> {
const response = await api.get<MailAccount[]>("/mail-accounts");
return response.data;
},
async create(data: MailAccountCreate): Promise<MailAccount> {
const response = await api.post<MailAccount>("/mail-accounts", data);
return response.data;
},
async update(
id: number,
data: Partial<MailAccountCreate>
): Promise<MailAccount> {
const response = await api.put<MailAccount>(`/mail-accounts/${id}`, data);
return response.data;
},
async delete(id: number): Promise<void> {
await api.delete(`/mail-accounts/${id}`);
},
async test(config: {
host: string;
port: number;
protocol: string;
username: string;
password: string;
use_ssl?: boolean;
use_tls?: boolean;
}): Promise<{ success: boolean; message: string }> {
const response = await api.post<{ success: boolean; message: string }>(
"/mail-accounts/test",
config
);
return response.data;
},
async autoDetect(
emailAddress: string
): Promise<{ success: boolean; suggestions: AutoDetectSuggestion[] }> {
const response = await api.post<{
success: boolean;
suggestions: AutoDetectSuggestion[];
}>("/mail-accounts/auto-detect", { email_address: emailAddress });
return response.data;
},
};
// ── Processing Runs API ─────────────────────────────────────────────────
export const processingRunsApi = {
async list(): Promise<ProcessingRun[]> {
// TODO: Add a dedicated /processing-runs endpoint to the backend
// For now, return empty array since no user-facing endpoint exists yet
return [];
},
};
export default api;