Use url_for() for all internal URLs; fix ProxyPass compatibility
All hardcoded paths (/load, /docs, /, /api/route/ etc.) are replaced with url_for() in templates, so Flask's APPLICATION_ROOT and ProxyFix generate correct URLs regardless of mount path. For app.js (a static file), inject a URLS object from the template alongside RELATION_ID: URLS.routeApi, .segmentApi, .routeMasterApi, .routePage Each is generated with url_for(..., relation_id=0)[:-1] to give a prefix that JS appends relation IDs to. The popstate handler now strips the URLS.routePage prefix instead of matching a hardcoded leading slash. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
53aab8c4bc
commit
17df83d043
3 changed files with 23 additions and 14 deletions
|
|
@ -313,7 +313,7 @@ document.getElementById('slot-to').addEventListener('click', () => setActiveSlot
|
||||||
function navigateTo(id, e) {
|
function navigateTo(id, e) {
|
||||||
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
if (e.metaKey || e.ctrlKey || e.shiftKey || e.button !== 0) return;
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
history.pushState(null, '', `/${id}`);
|
history.pushState(null, '', URLS.routePage + id);
|
||||||
loadRoute(id);
|
loadRoute(id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -325,7 +325,7 @@ function navigateTo(id, e) {
|
||||||
*/
|
*/
|
||||||
async function loadRoute(relationId) {
|
async function loadRoute(relationId) {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/route/${relationId}`);
|
const resp = await fetch(URLS.routeApi + relationId);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
if (data.error === 'is_route_master') {
|
if (data.error === 'is_route_master') {
|
||||||
|
|
@ -374,7 +374,7 @@ async function loadRoute(relationId) {
|
||||||
dirPanel.classList.remove('d-none');
|
dirPanel.classList.remove('d-none');
|
||||||
for (const dir of data.other_directions) {
|
for (const dir of data.other_directions) {
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = `/${dir.id}`;
|
a.href = URLS.routePage + dir.id;
|
||||||
a.className = 'stop-item d-block text-decoration-none';
|
a.className = 'stop-item d-block text-decoration-none';
|
||||||
a.textContent = dir.name;
|
a.textContent = dir.name;
|
||||||
a.addEventListener('click', (e) => navigateTo(dir.id, e));
|
a.addEventListener('click', (e) => navigateTo(dir.id, e));
|
||||||
|
|
@ -396,7 +396,7 @@ async function loadSegment() {
|
||||||
const rid = currentRelationId;
|
const rid = currentRelationId;
|
||||||
const params = new URLSearchParams({ from: selectedFrom, to: selectedTo, stops: '1' });
|
const params = new URLSearchParams({ from: selectedFrom, to: selectedTo, stops: '1' });
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/segment/${rid}?${params}`);
|
const resp = await fetch(URLS.segmentApi + rid + '?' + params);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
showError(data.message || 'Error loading segment.');
|
showError(data.message || 'Error loading segment.');
|
||||||
|
|
@ -415,7 +415,7 @@ async function loadSegment() {
|
||||||
*/
|
*/
|
||||||
async function loadRouteMaster(relationId) {
|
async function loadRouteMaster(relationId) {
|
||||||
try {
|
try {
|
||||||
const resp = await fetch(`/api/route_master/${relationId}`);
|
const resp = await fetch(URLS.routeMasterApi + relationId);
|
||||||
const data = await resp.json();
|
const data = await resp.json();
|
||||||
if (!resp.ok) {
|
if (!resp.ok) {
|
||||||
showError(data.message || `Error loading route master ${relationId}.`);
|
showError(data.message || `Error loading route master ${relationId}.`);
|
||||||
|
|
@ -463,7 +463,7 @@ async function loadRouteMaster(relationId) {
|
||||||
`background:${colour};flex-shrink:0`;
|
`background:${colour};flex-shrink:0`;
|
||||||
|
|
||||||
const a = document.createElement('a');
|
const a = document.createElement('a');
|
||||||
a.href = `/${route.id}`;
|
a.href = URLS.routePage + route.id;
|
||||||
a.className = 'text-decoration-none text-reset flex-grow-1';
|
a.className = 'text-decoration-none text-reset flex-grow-1';
|
||||||
a.textContent = route.name;
|
a.textContent = route.name;
|
||||||
a.addEventListener('click', (e) => navigateTo(route.id, e));
|
a.addEventListener('click', (e) => navigateTo(route.id, e));
|
||||||
|
|
@ -486,8 +486,11 @@ async function loadRouteMaster(relationId) {
|
||||||
// ── Init ───────────────────────────────────────────────────────────────────
|
// ── Init ───────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
window.addEventListener('popstate', () => {
|
window.addEventListener('popstate', () => {
|
||||||
const match = location.pathname.match(/^\/(\d+)$/);
|
const prefix = URLS.routePage;
|
||||||
if (match) loadRoute(parseInt(match[1], 10));
|
if (location.pathname.startsWith(prefix)) {
|
||||||
|
const id = parseInt(location.pathname.slice(prefix.length), 10);
|
||||||
|
if (id) loadRoute(id);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (RELATION_ID) {
|
if (RELATION_ID) {
|
||||||
|
|
|
||||||
|
|
@ -17,8 +17,8 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-dark bg-dark px-3" style="height:56px">
|
<nav class="navbar navbar-dark bg-dark px-3" style="height:56px">
|
||||||
<a class="navbar-brand" href="/">OSM Public Transport → GeoJSON</a>
|
<a class="navbar-brand" href="{{ url_for('index') }}">OSM Public Transport → GeoJSON</a>
|
||||||
<a class="nav-link text-white" href="/docs">API docs</a>
|
<a class="nav-link text-white" href="{{ url_for('docs') }}">API docs</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container py-5" style="max-width:860px">
|
<div class="container py-5" style="max-width:860px">
|
||||||
|
|
|
||||||
|
|
@ -12,8 +12,8 @@
|
||||||
<body>
|
<body>
|
||||||
|
|
||||||
<nav class="navbar navbar-dark bg-dark px-3" style="height:56px">
|
<nav class="navbar navbar-dark bg-dark px-3" style="height:56px">
|
||||||
<a class="navbar-brand" href="/">OSM Public Transport → GeoJSON</a>
|
<a class="navbar-brand" href="{{ url_for('index') }}">OSM Public Transport → GeoJSON</a>
|
||||||
<a class="nav-link text-white" href="/docs">API docs</a>
|
<a class="nav-link text-white" href="{{ url_for('docs') }}">API docs</a>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<div class="container-fluid h-100 p-0">
|
<div class="container-fluid h-100 p-0">
|
||||||
|
|
@ -23,7 +23,7 @@
|
||||||
<div class="col-3 border-end" id="sidebar">
|
<div class="col-3 border-end" id="sidebar">
|
||||||
|
|
||||||
<!-- Load form -->
|
<!-- Load form -->
|
||||||
<form method="post" action="/load" class="mb-3">
|
<form method="post" action="{{ url_for('load') }}" class="mb-3">
|
||||||
<label class="form-label fw-semibold small">Relation ID or OSM URL</label>
|
<label class="form-label fw-semibold small">Relation ID or OSM URL</label>
|
||||||
<div class="input-group input-group-sm">
|
<div class="input-group input-group-sm">
|
||||||
<input type="text" name="relation" class="form-control"
|
<input type="text" name="relation" class="form-control"
|
||||||
|
|
@ -121,8 +121,14 @@
|
||||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||||
<script>
|
<script>
|
||||||
// Relation ID injected by Flask; null if none.
|
// Injected by Flask so app.js works correctly under any mount path.
|
||||||
const RELATION_ID = {{ relation_id | tojson }};
|
const RELATION_ID = {{ relation_id | tojson }};
|
||||||
|
const URLS = {
|
||||||
|
routeApi: {{ url_for('api_route', relation_id=0)[:-1] | tojson }},
|
||||||
|
segmentApi: {{ url_for('api_segment', relation_id=0)[:-1] | tojson }},
|
||||||
|
routeMasterApi: {{ url_for('api_route_master', relation_id=0)[:-1] | tojson }},
|
||||||
|
routePage: {{ url_for('route_page', relation_id=0)[:-1] | tojson }},
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
<script src="{{ url_for('static', filename='app.js') }}"></script>
|
||||||
</body>
|
</body>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue