first commit

This commit is contained in:
Soph :3 2025-12-12 21:12:47 +02:00
commit 672f0deb6a
18 changed files with 1274 additions and 0 deletions

88
frontend/ts/api.ts Normal file
View file

@ -0,0 +1,88 @@
import type { LinkEntry } from "../../common/lib";
const API_BASE = location.hostname == "localhost" || location.hostname == "127.0.0.1" ? "http://localhost:3001" : "";
interface ApiResponse<T> {
success: boolean;
data?: T;
error?: string;
}
function getAuthHeader() {
const password = localStorage.getItem("password");
if (!password) throw new Error("No password found in localStorage");
return { "X-Password": password };
}
async function request<T>(url: string, options: RequestInit = {}): Promise<ApiResponse<T>> {
const headers = { ...options.headers, ...getAuthHeader() };
let res: Response;
try {
res = await fetch(`${API_BASE}/api/${url}`, { ...options, headers });
} catch {
return { success: false, error: "Network error" };
}
let json = null;
try {
json = await res.json();
} catch {
return { success: false, error: `Server returned status ${res.status}` };
}
if (!res.ok) {
return {
success: false,
error: json?.error || `Request failed with status ${res.status}`
};
}
return {
success: true,
data: json as T
};
}
export async function login(password: string): Promise<ApiResponse<void>> {
localStorage.setItem("password", password);
const res = await request<void>(`login`, { method: "POST" });
return res;
}
export async function logout(): Promise<ApiResponse<void>> {
localStorage.removeItem("password");
return { success: true };
}
export async function getLinks(): Promise<ApiResponse<LinkEntry[]>> {
return request<LinkEntry[]>("links");
}
export async function getLink(id: string): Promise<ApiResponse<LinkEntry>> {
return request<LinkEntry>(`links/${id}`);
}
export async function createLink(link: Partial<LinkEntry>): Promise<ApiResponse<LinkEntry>> {
return request<LinkEntry>("links", {
method: "POST",
body: JSON.stringify(link),
headers: { "Content-Type": "application/json" },
});
}
export async function updateLink(id: string, link: Partial<LinkEntry>): Promise<ApiResponse<LinkEntry>> {
return request<LinkEntry>(`links/${id}`, {
method: "PUT",
body: JSON.stringify(link),
headers: { "Content-Type": "application/json" },
});
}
export async function deleteLink(id: string): Promise<ApiResponse<void>> {
return request<void>(`links/${id}`, { method: "DELETE" });
}
export async function getLinkStats(id: string): Promise<ApiResponse<{ clicks: number }>> {
return request<{ clicks: number }>(`links/${id}/stats`);
}