110 lines
3.3 KiB
JavaScript
110 lines
3.3 KiB
JavaScript
// assets/js/api.js
|
||
const API = "api.php";
|
||
|
||
/** ---- intern: Hilfen ---- */
|
||
function withTs(url) {
|
||
const sep = url.includes("?") ? "&" : "?";
|
||
return `${url}${sep}t=${Date.now()}`; // no-store Absicherung
|
||
}
|
||
|
||
async function parseJsonSafe(res) {
|
||
const text = await res.text();
|
||
try {
|
||
return JSON.parse(text);
|
||
} catch (e) {
|
||
console.error("API: invalid JSON", { status: res.status, text });
|
||
return null;
|
||
}
|
||
}
|
||
|
||
// ... oberer Teil unverändert ...
|
||
|
||
/** zentraler Fetch-Wrapper: Credentials, no-store, 401→Login */
|
||
async function apiFetch(url, init = {}) {
|
||
const res = await fetch(withTs(url), {
|
||
credentials: "include",
|
||
cache: "no-store",
|
||
...init,
|
||
});
|
||
if (res.status === 401) {
|
||
window.location.href = "/login.php";
|
||
throw new Error("unauthorized");
|
||
}
|
||
return res;
|
||
}
|
||
|
||
/** ---- Public API ---- */
|
||
|
||
/**
|
||
* Action-Call:
|
||
* - apiAction('auth.me')
|
||
* - apiAction('sections.list', { method:'GET', data:{ template_id: 123 } })
|
||
* - apiAction('templates.create', { method:'POST', data:{ name:'...' } })
|
||
*/
|
||
export async function apiAction(
|
||
action,
|
||
{ method = "GET", data = null, headers = {} } = {}
|
||
) {
|
||
let url = `${API}?action=${encodeURIComponent(action)}`;
|
||
const init = { method, headers: { ...headers } };
|
||
|
||
// GET/HEAD → data als Query-String anhängen (kein Body!)
|
||
if ((method === "GET" || method === "HEAD") && data && typeof data === "object") {
|
||
const params = new URLSearchParams();
|
||
for (const [k, v] of Object.entries(data)) {
|
||
if (v !== undefined && v !== null) params.append(k, String(v));
|
||
}
|
||
const qs = params.toString();
|
||
if (qs) url += `&${qs}`;
|
||
} else if (data != null) {
|
||
init.headers["Content-Type"] = "application/json";
|
||
init.body = JSON.stringify(data);
|
||
}
|
||
|
||
const res = await apiFetch(url, init);
|
||
return await parseJsonSafe(res);
|
||
}
|
||
|
||
// ... Rest (apiList, apiCreate, apiUpdate, apiDelete, toast) unverändert ...
|
||
|
||
/**
|
||
* Listen-Helper für Ressourcen – ruft `${res}.list` auf.
|
||
* Optional kannst du query-Objekte mitgeben, z.B. { template_id: 123 } für sections.
|
||
*/
|
||
export async function apiList(res, query = {}) {
|
||
const q = new URLSearchParams(query);
|
||
const qs = q.toString() ? `&${q.toString()}` : "";
|
||
const r = await apiAction(`${res}.list`, { method: "GET" });
|
||
// Falls du query serverseitig brauchst (z.B. template_id), nutze eine Action-Variante:
|
||
// return await apiAction(`${res}.list`, { method:"GET", data: query });
|
||
return r?.items ?? [];
|
||
}
|
||
|
||
/** GET by id: nur nutzen, wenn du eine `${res}.get`-Action hast */
|
||
export async function apiGet(res, id) {
|
||
return await apiAction(`${res}.get`, { method: "GET", data: { id } });
|
||
}
|
||
|
||
/** CREATE / UPDATE / DELETE – sprechen `${res}.create|update|delete` an */
|
||
export async function apiCreate(res, payload) {
|
||
return await apiAction(`${res}.create`, { method: "POST", data: payload });
|
||
}
|
||
|
||
export async function apiUpdate(res, id, payload) {
|
||
return await apiAction(`${res}.update`, { method: "POST", data: { id, ...payload } });
|
||
}
|
||
|
||
export async function apiDelete(res, id) {
|
||
return await apiAction(`${res}.delete`, { method: "POST", data: { id } });
|
||
}
|
||
|
||
/** optionaler Toast-Fallback (keine harte Abhängigkeit) */
|
||
export function toast(msg, ok = true, opts = {}) {
|
||
if (window.Toast?.show) {
|
||
window.Toast.show(msg, { type: ok ? "success" : "error", duration: 2200, ...opts });
|
||
} else {
|
||
(ok ? console.log : console.error)(msg);
|
||
}
|
||
}
|
||
|