Server backed saving routes

This commit is contained in:
marijo 2026-06-29 08:52:32 +00:00
parent 18de453ac1
commit a4d6a8e99e
4 changed files with 102 additions and 25 deletions

115
app.js
View file

@ -1,6 +1,7 @@
const STORAGE_KEY = "routes.links.v1";
const AUTH_SESSION_KEY = "routes.authenticated.v1";
const AUTH_SECRET = window.ROUTES_SECRET;
const API_URL = window.ROUTES_API_URL || "api.php";
const authScreen = document.querySelector("#auth-screen");
const authForm = document.querySelector("#auth-form");
@ -29,7 +30,7 @@ const archiveCount = document.querySelector("#archive-count");
const archivedRoutesList = document.querySelector("#archived-routes-list");
const template = document.querySelector("#route-card-template");
let routes = sortRoutes(loadRoutes());
let routes = [];
authForm.addEventListener("submit", authenticate);
logoutButton.addEventListener("click", lockApp);
@ -68,12 +69,20 @@ function authenticate(event) {
unlockApp();
}
function unlockApp() {
async function unlockApp() {
authScreen.hidden = true;
appShell.hidden = false;
authSecretInput.value = "";
authMessage.textContent = "";
renderRoutes();
routeCount.textContent = "Loading routes...";
try {
routes = await loadRoutesFromBackend();
renderRoutes();
} catch (error) {
routeCount.textContent = "Could not load routes.";
alert(error.message);
}
}
function lockApp() {
@ -84,7 +93,7 @@ function lockApp() {
authSecretInput.focus();
}
function loadRoutes() {
function loadLocalRoutes() {
try {
return JSON.parse(localStorage.getItem(STORAGE_KEY)) ?? [];
} catch {
@ -92,8 +101,45 @@ function loadRoutes() {
}
}
function storeRoutes() {
async function loadRoutesFromBackend() {
const response = await fetch(API_URL, {
headers: { "X-Routes-Secret": AUTH_SECRET },
cache: "no-store",
});
if (!response.ok) {
throw new Error(`Could not load routes (${response.status}).`);
}
const data = await response.json();
const backendRoutes = sortRoutes(data.routes ?? []);
const localRoutes = sortRoutes(loadLocalRoutes());
if (!backendRoutes.length && localRoutes.length) {
routes = localRoutes;
await storeRoutes();
return localRoutes;
}
localStorage.setItem(STORAGE_KEY, JSON.stringify(backendRoutes));
return backendRoutes;
}
async function storeRoutes() {
localStorage.setItem(STORAGE_KEY, JSON.stringify(routes));
const response = await fetch(API_URL, {
method: "PUT",
headers: {
"Content-Type": "application/json",
"X-Routes-Secret": AUTH_SECRET,
},
body: JSON.stringify({ routes }),
});
if (!response.ok) {
throw new Error(`Could not save routes (${response.status}).`);
}
}
function sortRoutes(routeList) {
@ -152,9 +198,14 @@ async function saveRoute(event) {
}
routes = sortRoutes(routes);
storeRoutes();
renderRoutes();
resetForm();
try {
await storeRoutes();
renderRoutes();
resetForm();
} catch (error) {
alert(error.message);
}
}
function renderRoutes() {
@ -279,7 +330,7 @@ function handleRouteAction(event) {
}
}
function toggleArchive(id) {
async function toggleArchive(id) {
const route = routes.find((item) => item.id === id);
if (!route) {
return;
@ -287,11 +338,17 @@ function toggleArchive(id) {
route.archived = !route.archived;
routes = sortRoutes(routes);
storeRoutes();
renderRoutes();
if (idInput.value === id) {
resetForm();
try {
await storeRoutes();
renderRoutes();
if (idInput.value === id) {
resetForm();
}
} catch (error) {
route.archived = !route.archived;
alert(error.message);
}
}
@ -320,29 +377,43 @@ function editRoute(route) {
nameInput.focus();
}
function removeRoute(id) {
async function removeRoute(id) {
const route = routes.find((item) => item.id === id);
if (!route || !confirm(`Remove "${route.name}"?`)) {
return;
}
const previousRoutes = routes;
routes = routes.filter((item) => item.id !== id);
storeRoutes();
renderRoutes();
if (idInput.value === id) {
resetForm();
try {
await storeRoutes();
renderRoutes();
if (idInput.value === id) {
resetForm();
}
} catch (error) {
routes = previousRoutes;
alert(error.message);
}
}
function removeAllRoutes() {
async function removeAllRoutes() {
if (!routes.length || !confirm("Remove all saved routes?")) {
return;
}
const previousRoutes = routes;
routes = [];
storeRoutes();
renderRoutes();
resetForm();
try {
await storeRoutes();
renderRoutes();
resetForm();
} catch (error) {
routes = previousRoutes;
alert(error.message);
}
}
async function shareRoute(route) {