frontend: prefix-agnostic fetch paths (reverse-proxy subpath safe)
login.html/app.js now resolve fetch targets relative to the page's directory (BASE), so the app works served at / or under a path prefix (https://host/ani/) as long as the proxy strips the prefix.
This commit is contained in:
1 parent
c697500c7d
commit
7e6db5a41c
2 files changed
+19
-7
No files matched your search
+11
-5
@@ -1,11 +1,17 @@
|
|||||||
// app.js — ani-web frontend: search -> detail -> play, vanilla JS + hls.js
|
// app.js — ani-web frontend: search -> detail -> play, vanilla JS + hls.js
|
||||||
|
// BASE = this page's directory, so the app works at / or behind a
|
||||||
|
// prefixing reverse proxy (e.g. https://host/ani/)
|
||||||
|
const BASE = (() => {
|
||||||
|
const p = location.pathname;
|
||||||
|
return p.endsWith("/") ? p : p.slice(0, p.lastIndexOf("/") + 1);
|
||||||
|
})();
|
||||||
const main = document.getElementById("main");
|
const main = document.getElementById("main");
|
||||||
let hls = null, detail = null, lang = "eng", seasonId = null;
|
let hls = null, detail = null, lang = "eng", seasonId = null;
|
||||||
|
|
||||||
async function api(path) {
|
async function api(path) {
|
||||||
const r = await fetch(path);
|
const r = await fetch(BASE + path.replace(/^\//, ""));
|
||||||
if (r.status === 401) {
|
if (r.status === 401) {
|
||||||
location.href = "/login?next=" + encodeURIComponent(location.pathname + location.search);
|
location.href = BASE + "login?next=" + encodeURIComponent(location.pathname + location.search);
|
||||||
throw new Error("not authenticated");
|
throw new Error("not authenticated");
|
||||||
}
|
}
|
||||||
if (!r.ok) {
|
if (!r.ok) {
|
||||||
@@ -40,7 +46,7 @@ async function refreshHistory() {
|
|||||||
try { history = await api("/api/history"); } catch { /* home just shows empty */ }
|
try { history = await api("/api/history"); } catch { /* home just shows empty */ }
|
||||||
}
|
}
|
||||||
function postHistory(entry) {
|
function postHistory(entry) {
|
||||||
fetch("/api/history", {method: "POST", headers: {"Content-Type": "application/json"},
|
fetch(BASE + "api/history", {method: "POST", headers: {"Content-Type": "application/json"},
|
||||||
body: JSON.stringify(entry)}).catch(() => {});
|
body: JSON.stringify(entry)}).catch(() => {});
|
||||||
}
|
}
|
||||||
const fmtPos = s => { s = Math.floor(s || 0); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; };
|
const fmtPos = s => { s = Math.floor(s || 0); return `${Math.floor(s / 60)}:${String(s % 60).padStart(2, "0")}`; };
|
||||||
@@ -222,12 +228,12 @@ function showPlayOverlay(v) {
|
|||||||
document.getElementById("me").textContent = "hi, " + me.user;
|
document.getElementById("me").textContent = "hi, " + me.user;
|
||||||
const lo = document.getElementById("logout");
|
const lo = document.getElementById("logout");
|
||||||
lo.style.display = ""; lo.onclick = async () => {
|
lo.style.display = ""; lo.onclick = async () => {
|
||||||
await fetch("/logout", {method: "POST"}); location.href = "/login";
|
await fetch(BASE + "logout", {method: "POST"}); location.href = BASE + "login";
|
||||||
};
|
};
|
||||||
const da = document.getElementById("delacct");
|
const da = document.getElementById("delacct");
|
||||||
da.style.display = ""; da.onclick = async () => {
|
da.style.display = ""; da.onclick = async () => {
|
||||||
if (!confirm("Delete your account and watch history?")) return;
|
if (!confirm("Delete your account and watch history?")) return;
|
||||||
await fetch("/account/delete", {method: "POST"}); location.href = "/login";
|
await fetch(BASE + "account/delete", {method: "POST"}); location.href = BASE + "login";
|
||||||
};
|
};
|
||||||
} catch { /* not logged in: api() already redirected to /login */ }
|
} catch { /* not logged in: api() already redirected to /login */ }
|
||||||
await refreshHistory();
|
await refreshHistory();
|
||||||
|
|||||||
+8
-2
@@ -27,15 +27,21 @@ button { width:100%; margin-top:.4rem; padding:.55rem; border:0; border-radius:8
|
|||||||
<p class="err" id="err"></p>
|
<p class="err" id="err"></p>
|
||||||
</form>
|
</form>
|
||||||
<script>
|
<script>
|
||||||
|
// base = this page's directory, so the app works at / or behind a
|
||||||
|
// prefixing reverse proxy (e.g. https://host/ani/)
|
||||||
|
const BASE = (() => {
|
||||||
|
const p = location.pathname;
|
||||||
|
return p.endsWith("/") ? p : p.slice(0, p.lastIndexOf("/") + 1);
|
||||||
|
})();
|
||||||
const $ = id => document.getElementById(id);
|
const $ = id => document.getElementById(id);
|
||||||
const next = new URLSearchParams(location.search).get("next");
|
const next = new URLSearchParams(location.search).get("next");
|
||||||
$("go").onclick = async () => {
|
$("go").onclick = async () => {
|
||||||
$("err").textContent = "";
|
$("err").textContent = "";
|
||||||
const r = await fetch("/login", {method: "POST",
|
const r = await fetch(BASE + "login", {method: "POST",
|
||||||
headers: {"Content-Type": "application/json"},
|
headers: {"Content-Type": "application/json"},
|
||||||
body: JSON.stringify({username: $("user").value, password: $("pw").value})});
|
body: JSON.stringify({username: $("user").value, password: $("pw").value})});
|
||||||
if (r.ok) {
|
if (r.ok) {
|
||||||
location.href = (next && next.startsWith("/") && !next.startsWith("//")) ? next : "/";
|
location.href = next ? BASE + next.replace(/^\/+/, "") : BASE;
|
||||||
} else {
|
} else {
|
||||||
$("err").textContent = (await r.json().catch(() => ({}))).detail || "login failed";
|
$("err").textContent = (await r.json().catch(() => ({}))).detail || "login failed";
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user