Key changes:

- Add MapLibre GL JS viewer with light/dark themes, terrain layer,
  and embedded static map page for coordinate sharing
- Add map pin support with persistent map_pins.yaml config
- Add map region subscriptions and downloader with progress tracking,
  sourcing pre-extracted PMTiles from a cold CDN
- Add map export to PNG and GPS coordinate copy via context menu
- Add MapViewer React component alongside reworked Map page
- Add map catalog and region-building script (build_map_regions.py)
- Remove OSM import/render scripts, leaflet files, Apache configs,
  renderd service, and osm-map-dumper Docker container
- Update Caddyfile to serve PMTiles and map static assets
- Update build/install/repair scripts for Protomaps dependencies
- Drop map_file table, add/drop map_subscription table (alembic)
- Adding button to skip primary drive mounting during onboarding.
This commit is contained in:
Roland 2026-03-29 18:58:31 -06:00
parent 0a5fe7eebe
commit b8a532c8d9
No known key found for this signature in database
GPG key ID: 1BA620CE6C9A4680
102 changed files with 5033 additions and 2712 deletions

View file

@ -75,23 +75,6 @@ DEFAULT_CONFIG = {
"use_https": True,
"description": "Kiwix/Zim server",
},
# Map services
{
"name": "renderd",
"systemd_name": "renderd",
"port": None,
"viewable": False,
"use_https": False,
"description": "Map tile rendering daemon",
},
{
"name": "apache2",
"systemd_name": "apache2",
"port": 8084,
"viewable": True,
"use_https": True,
"description": "Map tile web server",
},
# System services
{
"name": "postgresql",

View file

@ -683,8 +683,6 @@ async def restart_all_services() -> dict:
"wrolpi-app",
"wrolpi-kiwix",
"wrolpi-help",
"renderd",
"apache2",
# Controller restarts itself last (scheduled after response)
]

View file

@ -787,6 +787,10 @@
<span class="loading"></span>
</div>
<p id="onboarding-no-drives" style="display: none;">No unmounted drives detected. Plug in a USB drive and refresh.</p>
<div style="margin-top: 1.5rem; padding-top: 1rem; border-top: 1px solid rgba(255,255,255,0.2);">
<p style="margin-bottom: 0.5rem; font-size: 0.9em; opacity: 0.8;">No external drive? You can still set up WROLPi using internal storage.</p>
<button class="btn btn-secondary" onclick="repairWithoutDrive()">Run repair without external drive</button>
</div>
</div>
<!-- Step 2: Confirm -->
@ -1872,6 +1876,12 @@
currentScriptOutput = status.script_name;
// Hide onboarding while a script is running
const onboardingEl = document.getElementById('onboarding-banner');
if (onboardingEl) {
onboardingEl.style.display = 'none';
}
// Disable run buttons while script is running
document.querySelectorAll('.script-row .btn').forEach(btn => {
btn.disabled = true;
@ -1881,6 +1891,12 @@
banner.classList.remove('show');
currentScriptOutput = null;
// Re-check if onboarding is still needed (e.g. after repair finishes)
const onboardingEl = document.getElementById('onboarding-banner');
if (onboardingEl) {
loadOnboardingCandidates();
}
// Re-enable run buttons
document.querySelectorAll('.script-row').forEach(row => {
if (!row.classList.contains('unavailable')) {
@ -2105,6 +2121,9 @@
const candidates = await resp.json();
container.innerHTML = '';
// Re-show the onboarding banner (may have been hidden while a script was running)
document.getElementById('onboarding-banner').style.display = '';
if (candidates.length === 0) {
noDrives.style.display = 'block';
return;
@ -2229,6 +2248,35 @@
loadOnboardingCandidates();
}
async function repairWithoutDrive() {
showOnboardingStep('progress');
document.querySelector('#onboarding-progress h2').textContent = 'Running Repair...';
document.querySelector('#onboarding-progress p').textContent = 'Starting repair without external drive. Please wait...';
try {
const resp = await fetch('api/scripts/repair/start', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Accept': 'application/json'},
signal: AbortSignal.timeout(60000)
});
if (!resp.ok) {
const data = await resp.json().catch(() => ({}));
throw new Error(data.detail || 'Failed to start repair');
}
showOnboardingStep('complete');
document.getElementById('onboarding-complete-msg').textContent =
'Repair started without external drive. The main UI will be available shortly.';
setTimeout(() => {
window.location.href = '?fallback=true';
}, 10000);
} catch (e) {
document.getElementById('onboarding-error-msg').textContent = e.message;
showOnboardingStep('error');
}
}
function resetOnboarding() {
cancelOnboarding();
}

View file

@ -57,7 +57,7 @@ class TestManagedServicesConfig:
def test_managed_services_count(self):
"""Should have expected number of managed services."""
services = DEFAULT_CONFIG["managed_services"]
assert len(services) == 14
assert len(services) == 12
@pytest.mark.parametrize("name,expected", [
("wrolpi-api",
@ -81,7 +81,6 @@ class TestManagedServicesConfig:
@pytest.mark.parametrize("name,use_https", [
("wrolpi-help", True),
("wrolpi-kiwix", True),
("apache2", True),
("wrolpi-api", False),
("wrolpi-app", False),
("postgresql", False),

View file

@ -134,7 +134,6 @@ class TestGetServiceStatus:
@pytest.mark.parametrize("service_name,expected_https", [
("wrolpi-help", True),
("wrolpi-kiwix", True),
("apache2", True),
("wrolpi-api", False),
])
def test_returns_use_https_field(self, service_name, expected_https):