Repository navigation
Expand file tree
/
Copy pathapp.js
More file actions
127 lines (116 loc) · 3.46 KB
/
Copy pathapp.js
File metadata and controls
127 lines (116 loc) · 3.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// Shared frontend helper + API wiring
const API_BASE = "http://localhost:8080"; // same origin. If backend at different URL set like 'http://localhost:8080'
const TOKEN_KEY = "flight_token";
// Decode JWT to extract payload
function parseJwt(token) {
try {
const base64Url = token.split(".")[1];
const base64 = base64Url.replace(/-/g, "+").replace(/_/g, "/");
const jsonPayload = decodeURIComponent(
atob(base64)
.split("")
.map((c) => "%" + ("00" + c.charCodeAt(0).toString(16)).slice(-2))
.join("")
);
return JSON.parse(jsonPayload);
} catch (e) {
return null;
}
}
// Redirect based on user role
async function redirectByRole(token) {
const decoded = parseJwt(token);
if (decoded && decoded.role && decoded.role.toLowerCase() === "admin") {
location.href = "admin.html";
} else {
location.href = "flights.html";
}
}
async function apiFetch(path, opts = {}) {
const headers = opts.headers || {};
const token = localStorage.getItem(TOKEN_KEY);
if (token) headers["Authorization"] = "Bearer " + token;
headers["Content-Type"] = headers["Content-Type"] || "application/json";
const res = await fetch(API_BASE + path, { ...opts, headers });
if (res.ok) {
const ct = res.headers.get("content-type") || "";
if (ct.includes("application/json")) return res.json();
return res.text();
} else {
let errText = await res.text();
try {
const j = JSON.parse(errText);
if (j.error) errText = j.error;
} catch (e) {}
const err = new Error(errText || "Request failed: " + res.status);
err.status = res.status;
throw err;
}
}
async function apiGet(path) {
return apiFetch(path, { method: "GET" });
}
async function apiPost(path, body) {
return apiFetch(path, { method: "POST", body: JSON.stringify(body) });
}
async function apiPut(path, body) {
return apiFetch(path, { method: "PUT", body: JSON.stringify(body) });
}
async function apiDelete(path) {
return apiFetch(path, { method: "DELETE" });
}
// Auth helpers
async function apiLogin(username, password) {
const r = await apiPost("/auth/login", { username, password });
if (r && r.token) {
localStorage.setItem(TOKEN_KEY, r.token);
return r;
}
throw new Error("Login failed");
}
async function apiRegister(username, email, password) {
const r = await apiPost("/auth/register", { username, email, password });
if (r && r.token) {
localStorage.setItem(TOKEN_KEY, r.token);
return r;
}
throw new Error("Register failed");
}
function logout() {
localStorage.removeItem(TOKEN_KEY);
location.href = "index.html";
}
function ensureAuth() {
if (!localStorage.getItem(TOKEN_KEY)) {
location.href = "index.html";
throw new Error("Not authenticated");
}
}
// small helpers
function escapeHtml(s) {
if (s === null || s === undefined) return "";
return String(s).replace(
/[&<>"']/g,
(c) =>
({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[
c
])
);
}
function formatDate(iso) {
if (!iso) return "";
const d = new Date(iso);
if (isNaN(d)) return iso;
return d.toLocaleString();
}
// convert ISO to local datetime-local input value
function toLocalInput(iso) {
const d = new Date(iso);
const pad = (n) => String(n).padStart(2, "0");
const yyyy = d.getFullYear();
const mm = pad(d.getMonth() + 1);
const dd = pad(d.getDate());
const hh = pad(d.getHours());
const min = pad(d.getMinutes());
return `${yyyy}-${mm}-${dd}T${hh}:${min}`;
}