Phase 7: Auth & Security upgrade (JWT, 2FA, Login UI)

This commit is contained in:
Gan, Jimmy
2026-02-19 03:47:48 +08:00
parent f3db7d3654
commit 651bc0580e
12 changed files with 495 additions and 38 deletions
+58 -6
View File
@@ -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");
}