Creating a new Collection model for use in organizing FileGroups.

Replacing Channels and Domains with Collection.

Moving downloading relationships from Channel to Collection.

Created domains.yaml config to manage tagged domain collections.

Adding turn-key upgrade solution in the UI.
This commit is contained in:
Roland 2025-10-29 22:16:33 -06:00 • committed by lrnselfreliance
parent 1ed0d63bac
commit a1e0fb4e92
115 changed files with 13309 additions and 986 deletions

View file

@ -12,4 +12,6 @@
%wrolpi ALL= NOPASSWD:/usr/bin/systemctl stop wrolpi-app.service
%wrolpi ALL= NOPASSWD:/usr/bin/systemctl start wrolpi-app.service
%wrolpi ALL= NOPASSWD:/opt/wrolpi/scripts/import_map.sh
%wrolpi ALL= NOPASSWD:/opt/wrolpi/upgrade.sh *
%wrolpi ALL= NOPASSWD:/usr/bin/systemctl start wrolpi-upgrade.service
%wrolpi ALL= NOPASSWD:/usr/sbin/shutdown

View file

@ -0,0 +1,189 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WROLPi - Upgrade in Progress</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
color: #333;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
background: linear-gradient(135deg, #f5f7fa 0%, #e4e8f0 50%, #dde4ed 100%);
}
.container {
text-align: center;
padding: 2rem;
max-width: 500px;
width: 90%;
}
.logo {
font-size: 3rem;
margin-bottom: 1rem;
}
h1 {
color: #5829bb;
font-size: 1.8rem;
margin-bottom: 0.5rem;
font-weight: 600;
}
.subtitle {
color: #666;
font-size: 1rem;
margin-bottom: 2rem;
}
.spinner-container {
margin: 2rem 0;
}
.spinner {
width: 50px;
height: 50px;
border: 4px solid #ddd;
border-top: 4px solid #4CAF50;
border-radius: 50%;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.status {
font-size: 1.1rem;
margin: 1.5rem 0;
min-height: 1.5em;
}
.elapsed {
color: #666;
font-size: 0.9rem;
margin-top: 1rem;
}
.warning {
background: rgba(255, 193, 7, 0.1);
border: 1px solid rgba(255, 193, 7, 0.3);
border-radius: 8px;
padding: 1rem;
margin-top: 2rem;
font-size: 0.9rem;
color: #b38600;
}
.success {
color: #4CAF50;
}
.check-icon {
font-size: 3rem;
color: #4CAF50;
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.5); }
to { opacity: 1; transform: scale(1); }
}
.hidden {
display: none;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">WROLPi</div>
<h1>Upgrade in Progress</h1>
<p class="subtitle">Please wait while your system is being upgraded</p>
<div class="spinner-container" id="spinnerContainer">
<div class="spinner"></div>
</div>
<div class="spinner-container hidden" id="successContainer">
<div class="check-icon">&#10003;</div>
</div>
<p class="status" id="status">Checking upgrade status...</p>
<p class="elapsed" id="elapsed">Elapsed: 0:00</p>
<div class="warning">
<strong>Do not power off</strong> your device or close this window during the upgrade.
</div>
</div>
<script>
const startTime = Date.now();
let pollInterval;
let elapsedInterval;
function updateElapsed() {
const elapsed = Math.floor((Date.now() - startTime) / 1000);
const minutes = Math.floor(elapsed / 60);
const seconds = elapsed % 60;
document.getElementById('elapsed').textContent =
`Elapsed: ${minutes}:${seconds.toString().padStart(2, '0')}`;
}
function showSuccess() {
document.getElementById('spinnerContainer').classList.add('hidden');
document.getElementById('successContainer').classList.remove('hidden');
}
async function checkApiStatus() {
try {
const response = await fetch('/api/status', {
method: 'GET',
headers: { 'Accept': 'application/json' },
// Short timeout to avoid long waits
signal: AbortSignal.timeout(5000)
});
if (response.ok) {
// API is back online!
clearInterval(pollInterval);
clearInterval(elapsedInterval);
showSuccess();
document.getElementById('status').innerHTML =
'<span class="success">Upgrade complete! Redirecting...</span>';
// Redirect to home after a short delay
setTimeout(() => {
window.location.href = '/';
}, 2000);
}
} catch (e) {
// API is still down or timeout - this is expected during upgrade
document.getElementById('status').textContent = 'Upgrade in progress...';
}
}
// Update elapsed time every second
elapsedInterval = setInterval(updateElapsed, 1000);
// Check API status every 3 seconds
pollInterval = setInterval(checkApiStatus, 3000);
// Initial check
checkApiStatus();
</script>
</body>
</html>

View file

@ -0,0 +1,11 @@
[Unit]
Description=WROLPi Upgrade Service
After=network.target
[Service]
Type=oneshot
EnvironmentFile=/tmp/wrolpi-upgrade.env
ExecStart=/opt/wrolpi/upgrade.sh -b ${BRANCH}
User=root
StandardOutput=journal
StandardError=journal

View file

@ -1,5 +1,22 @@
# Nginx config for the WROLPi services.
#
# Maintenance page for upgrades
#
location = /maintenance.html {
alias /var/www/maintenance.html;
}
#
# Error pages
#
location /error/ {
alias /var/www/;
internal;
}
#
# App
#