Initial Commit

This commit is contained in:
HPL-JesusCastro
2026-05-26 16:04:14 +08:00
commit 5d6f228224
62 changed files with 10152 additions and 0 deletions

145
client/src/api.js Normal file
View File

@@ -0,0 +1,145 @@
const ADS_BASE = '/api/ads';
const APPS_BASE = '/api/apps';
function authHeaders(extra = {}) {
const token = localStorage.getItem('showcase_token');
return token ? { Authorization: `Bearer ${token}`, ...extra } : { ...extra };
}
async function apiFetch(url, options = {}) {
const res = await fetch(url, {
...options,
headers: { ...authHeaders(), ...options.headers },
});
if (res.status === 401) {
localStorage.removeItem('showcase_token');
window.location.href = '/login';
throw new Error('Unauthorized');
}
return res;
}
// ── auth ─────────────────────────────────────────────────────────────────────
export async function login(password) {
const res = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ password }),
});
if (!res.ok) throw new Error('Invalid password');
const { token } = await res.json();
localStorage.setItem('showcase_token', token);
}
export function logout() {
localStorage.removeItem('showcase_token');
}
export function isAuthenticated() {
return !!localStorage.getItem('showcase_token');
}
// ── ads ──────────────────────────────────────────────────────────────────────
export async function listAds(appId) {
const url = appId ? `${ADS_BASE}?appId=${encodeURIComponent(appId)}` : ADS_BASE;
const res = await apiFetch(url);
if (!res.ok) throw new Error('Failed to fetch ads');
return res.json();
}
export async function getAd(id) {
const res = await apiFetch(`${ADS_BASE}/${id}`);
if (!res.ok) throw new Error('Ad not found');
return res.json();
}
export async function uploadAd(file) {
const form = new FormData();
form.append('file', file);
const res = await apiFetch(ADS_BASE, { method: 'POST', body: form });
if (!res.ok) throw new Error('Upload failed');
return res.json();
}
export async function updateAd(id, data) {
const res = await apiFetch(`${ADS_BASE}/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Update failed');
return res.json();
}
export async function updateAdFile(id, file) {
const form = new FormData();
form.append('file', file);
const res = await apiFetch(`${ADS_BASE}/${id}/file`, { method: 'POST', body: form });
if (!res.ok) throw new Error('File update failed');
return res.json();
}
export async function deleteAd(id) {
const res = await apiFetch(`${ADS_BASE}/${id}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Delete failed');
}
export async function requestScreenshot(id) {
const res = await apiFetch(`${ADS_BASE}/${id}/screenshot`, { method: 'POST' });
if (!res.ok) throw new Error('Screenshot failed');
return res.json();
}
export async function saveThumbnail(id, dataUrl) {
const res = await apiFetch(`${ADS_BASE}/${id}/thumbnail`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ dataUrl }),
});
if (!res.ok) throw new Error('Thumbnail save failed');
return res.json();
}
// ── apps ─────────────────────────────────────────────────────────────────────
export async function lookupApp(url) {
const res = await apiFetch(`${APPS_BASE}/lookup?url=${encodeURIComponent(url)}`);
if (!res.ok) {
const body = await res.json().catch(() => ({}));
throw new Error(body.error || 'Lookup failed');
}
return res.json();
}
export async function listApps() {
const res = await apiFetch(APPS_BASE);
if (!res.ok) throw new Error('Failed to fetch apps');
return res.json();
}
export async function createApp(data) {
const res = await apiFetch(APPS_BASE, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Create app failed');
return res.json();
}
export async function updateApp(id, data) {
const res = await apiFetch(`${APPS_BASE}/${id}`, {
method: 'PATCH',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!res.ok) throw new Error('Update app failed');
return res.json();
}
export async function deleteApp(id) {
const res = await apiFetch(`${APPS_BASE}/${id}`, { method: 'DELETE' });
if (!res.ok) throw new Error('Delete app failed');
}