Phase 7: Auth & Security upgrade (JWT, 2FA, Login UI)
This commit is contained in:
@@ -1,13 +1,49 @@
|
||||
const BASE = "/api";
|
||||
let token = localStorage.getItem("token") || "";
|
||||
|
||||
export function setToken(t) {
|
||||
token = t;
|
||||
if (t) localStorage.setItem("token", t);
|
||||
else localStorage.removeItem("token");
|
||||
}
|
||||
|
||||
export function getToken() {
|
||||
return token;
|
||||
}
|
||||
|
||||
async function request(path, opts = {}) {
|
||||
const headers = opts.headers || {};
|
||||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||
|
||||
if (opts.json) {
|
||||
headers["Content-Type"] = "application/json";
|
||||
opts.body = JSON.stringify(opts.json);
|
||||
delete opts.json;
|
||||
}
|
||||
|
||||
try {
|
||||
const r = await fetch(BASE + path, opts);
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
const r = await fetch(BASE + path, { ...opts, headers });
|
||||
|
||||
if (r.status === 401) {
|
||||
// Session expired or invalid
|
||||
setToken("");
|
||||
// Only reload if not already on login page (simple check)
|
||||
if (!location.pathname.includes("login") && !token) {
|
||||
// Let the app handle redirect, but clearer is to throw
|
||||
}
|
||||
throw new Error("Unauthorized");
|
||||
}
|
||||
|
||||
if (!r.ok) {
|
||||
const error = new Error(`HTTP ${r.status}`);
|
||||
error.status = r.status;
|
||||
try { error.body = await r.json(); } catch { }
|
||||
throw error;
|
||||
}
|
||||
return r.json();
|
||||
} catch (e) {
|
||||
console.error(`API ${opts.method || "GET"} ${path}:`, e);
|
||||
return null;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,8 +51,8 @@ export function get(path) {
|
||||
return request(path);
|
||||
}
|
||||
|
||||
export function post(path) {
|
||||
return request(path, { method: "POST" });
|
||||
export function post(path, data) {
|
||||
return request(path, { method: "POST", json: data });
|
||||
}
|
||||
|
||||
export function del(path) {
|
||||
@@ -24,10 +60,26 @@ export function del(path) {
|
||||
}
|
||||
|
||||
export async function upload(path, file) {
|
||||
const headers = {};
|
||||
if (token) headers["Authorization"] = `Bearer ${token}`;
|
||||
|
||||
const form = new FormData();
|
||||
form.append("file", file);
|
||||
return request(`/files/upload?path=${encodeURIComponent(path)}`, {
|
||||
|
||||
const r = await fetch(`${BASE}/files/upload?path=${encodeURIComponent(path)}`, {
|
||||
method: "POST",
|
||||
headers,
|
||||
body: form,
|
||||
});
|
||||
|
||||
if (!r.ok) throw new Error(`HTTP ${r.status}`);
|
||||
return r.json();
|
||||
}
|
||||
|
||||
export function login(creds) {
|
||||
return request("/auth/login", { method: "POST", json: creds });
|
||||
}
|
||||
|
||||
export function checkAuth() {
|
||||
return request("/auth/me");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user