Files
emailtemplate.it/public/assets/js/api.js
2025-12-08 03:03:41 +01:00

118 lines
3.7 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// assets/js/api.js
const BASE = (window.APP_BASE_URL || '').replace(/\/$/, '');
const API =
(window.APP_API_BASE && window.APP_API_BASE.replace(/\/$/, '')) ||
(BASE ? `${BASE}/api.php` : 'api.php');
const searchParams = new URLSearchParams(window.location.search || '');
const disableAuthRedirect =
Boolean(window.DISABLE_AUTH_REDIRECT) || searchParams.has('debug_redirect');
/** ---- 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;
}
}
// ...ss 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) {
if (!disableAuthRedirect) {
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);
}
}