8e9401c6bf
gates / gates (push) Successful in 27s
Part C shipped the same day the pilot was read: fifty apps in three pushes, 1 031 of 1 032 strings. The English Apps list shows ZERO Hungarian app descriptions across all 53 apps — the only Hungarian left on it is the "Naprakész" badge (R-589) and the language picker naming itself, which is correct. The Hungarian Apps list is identical to the pre-slice capture once the per-session CSRF token AND Docker's own "Up N hours" container string are normalised. Both normalisations are stated in the evidence rather than applied quietly — the second one moved because two hours of wall clock passed between captures, not because any copy changed. Fleet floor raised to 0.257.0 with the declared MinAgent 0.131.0, above the vouched golden so the declaration carries it. demo-felhom went 0.255.0 -> 0.257.0 by itself in under 12 seconds and THEN rendered the English tagline: the floor delivered the feature, not a version string. Rows: R-593 (papra describes a session-signing key as "the app's subdomain" — the one string left untranslated) and R-594 (the catalog gate can convict a retrieval promise but has no way to REGISTER a true one, which the shared vocabulary's design calls for). R-560 closed. 281 -> 287 rows. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
3292 lines
142 KiB
HTML
3292 lines
142 KiB
HTML
|
|
|
|
<!DOCTYPE html>
|
|
<html lang="en" class="no-js">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Apps — Felhom.eu</title>
|
|
|
|
<link rel="icon" type="image/svg+xml" href="/static/favicon.svg?v=0.257.0">
|
|
<link rel="stylesheet" href="/static/style.css?v=0.257.0">
|
|
<meta name="csrf-token" content="8869722e21aa4e65abe2803da4fbb736f7a901b80840b9976cb56fe18295b071">
|
|
<script>
|
|
|
|
|
|
document.documentElement.className=document.documentElement.className.replace('no-js','js');
|
|
function csrfHeaders(){var el=document.querySelector('meta[name="csrf-token"]');return el?{'X-CSRF-Token':el.content}:{};}
|
|
|
|
|
|
|
|
|
|
|
|
function felhomConfirm(el,question,onYes){
|
|
if(!el||el.dataset.fcOpen)return;
|
|
el.dataset.fcOpen='1';
|
|
var wrap=document.createElement('span');wrap.className='inline-confirm';
|
|
var q=document.createElement('span');q.className='inline-confirm-q';q.textContent=question;
|
|
var yes=document.createElement('button');yes.type='button';yes.className='btn btn-xs btn-danger';yes.textContent='Yes';
|
|
var no=document.createElement('button');no.type='button';no.className='btn btn-xs btn-outline';no.textContent='Cancel';
|
|
wrap.appendChild(q);wrap.appendChild(yes);wrap.appendChild(no);
|
|
el.style.display='none';el.parentNode.insertBefore(wrap,el.nextSibling);
|
|
function close(){wrap.remove();el.style.display='';delete el.dataset.fcOpen;}
|
|
no.addEventListener('click',close);
|
|
yes.addEventListener('click',function(){close();onYes();});
|
|
}
|
|
document.addEventListener('click',function(e){
|
|
var btn=e.target.closest?e.target.closest('[data-confirm]'):null;
|
|
if(!btn)return;
|
|
e.preventDefault();
|
|
felhomConfirm(btn,btn.getAttribute('data-confirm'),function(){
|
|
var form=btn.closest('form');
|
|
if(form){if(form.requestSubmit)form.requestSubmit(btn);else form.submit();}
|
|
});
|
|
});
|
|
|
|
|
|
document.addEventListener('click',function(e){
|
|
var btn=e.target.closest?e.target.closest('.nav-group-toggle'):null;
|
|
if(!btn)return;
|
|
var group=btn.closest('.nav-group');
|
|
if(!group)return;
|
|
var willOpen=!group.classList.contains('is-open');
|
|
document.querySelectorAll('.nav-group.is-open').forEach(function(g){
|
|
g.classList.remove('is-open');
|
|
var t=g.querySelector('.nav-group-toggle');
|
|
if(t)t.setAttribute('aria-expanded','false');
|
|
});
|
|
if(willOpen){group.classList.add('is-open');btn.setAttribute('aria-expanded','true');}
|
|
});
|
|
function showAlert(msg){var o=document.createElement('div');o.className='modal-overlay';o.id='alert-modal';o.addEventListener('click',function(e){if(e.target===o)o.remove();});var c=document.createElement('div');c.className='modal-card';c.innerHTML='<h3>Message</h3><pre style="white-space:pre-wrap;word-break:break-word;background:var(--bg-2);padding:.75rem;border-radius:.375rem;font-size:.85rem;max-height:60vh;overflow-y:auto;user-select:text;cursor:text">'+msg.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>')+'</pre><div class="modal-actions"><button class="btn btn-primary" onclick="document.getElementById(\'alert-modal\').remove()">OK</button></div>';o.appendChild(c);document.body.appendChild(o);}
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<svg xmlns="http://www.w3.org/2000/svg" style="display:none" aria-hidden="true">
|
|
<symbol id="i-hard-drive" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 16h.01" /> <path d="M2.212 11.577a2 2 0 0 0-.212.896V18a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-5.527a2 2 0 0 0-.212-.896L18.55 5.11A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" /> <path d="M21.946 12.013H2.054" /> <path d="M6 16h.01" /></symbol>
|
|
<symbol id="i-cpu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 20v2" /> <path d="M12 2v2" /> <path d="M17 20v2" /> <path d="M17 2v2" /> <path d="M2 12h2" /> <path d="M2 17h2" /> <path d="M2 7h2" /> <path d="M20 12h2" /> <path d="M20 17h2" /> <path d="M20 7h2" /> <path d="M7 20v2" /> <path d="M7 2v2" /> <rect x="4" y="4" width="16" height="16" rx="2" /> <rect x="8" y="8" width="8" height="8" rx="1" /></symbol>
|
|
<symbol id="i-memory-stick" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 12v-2" /> <path d="M12 18v-2" /> <path d="M16 12v-2" /> <path d="M16 18v-2" /> <path d="M2 11h1.5" /> <path d="M20 18v-2" /> <path d="M20.5 11H22" /> <path d="M4 18v-2" /> <path d="M8 12v-2" /> <path d="M8 18v-2" /> <rect x="2" y="6" width="20" height="10" rx="2" /></symbol>
|
|
<symbol id="i-thermometer" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14 4v10.54a4 4 0 1 1-4 0V4a2 2 0 0 1 4 0Z" /></symbol>
|
|
<symbol id="i-triangle-alert" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" /> <path d="M12 9v4" /> <path d="M12 17h.01" /></symbol>
|
|
<symbol id="i-lock" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="11" x="3" y="11" rx="2" ry="2" /> <path d="M7 11V7a5 5 0 0 1 10 0v4" /></symbol>
|
|
<symbol id="i-cloud" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.5 19H9a7 7 0 1 1 6.71-9h1.79a4.5 4.5 0 1 1 0 9Z" /></symbol>
|
|
<symbol id="i-square" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="18" height="18" x="3" y="3" rx="2" /></symbol>
|
|
<symbol id="i-rotate-cw" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12a9 9 0 1 1-9-9c2.52 0 4.93 1 6.74 2.74L21 8" /> <path d="M21 3v5h-5" /></symbol>
|
|
<symbol id="i-file-text" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" /> <path d="M14 2v5a1 1 0 0 0 1 1h5" /> <path d="M10 9H8" /> <path d="M16 13H8" /> <path d="M16 17H8" /></symbol>
|
|
<symbol id="i-external-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M15 3h6v6" /> <path d="M10 14 21 3" /> <path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6" /></symbol>
|
|
<symbol id="i-shield" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 13c0 5-3.5 7.5-7.66 8.95a1 1 0 0 1-.67-.01C7.5 20.5 4 18 4 13V6a1 1 0 0 1 1-1c2 0 4.5-1.2 6.24-2.72a1.17 1.17 0 0 1 1.52 0C14.51 3.81 17 5 19 5a1 1 0 0 1 1 1z" /></symbol>
|
|
<symbol id="i-server" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="20" height="8" x="2" y="2" rx="2" ry="2" /> <rect width="20" height="8" x="2" y="14" rx="2" ry="2" /> <line x1="6" x2="6.01" y1="6" y2="6" /> <line x1="6" x2="6.01" y1="18" y2="18" /></symbol>
|
|
<symbol id="i-share" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="18" cy="5" r="3" /> <circle cx="6" cy="12" r="3" /> <circle cx="18" cy="19" r="3" /> <line x1="8.59" x2="15.42" y1="13.51" y2="17.49" /> <line x1="15.41" x2="8.59" y1="6.51" y2="10.49" /></symbol>
|
|
<symbol id="i-layout-grid" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect width="7" height="7" x="3" y="3" rx="1" /> <rect width="7" height="7" x="14" y="3" rx="1" /> <rect width="7" height="7" x="14" y="14" rx="1" /> <rect width="7" height="7" x="3" y="14" rx="1" /></symbol>
|
|
<symbol id="i-rocket" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4.5 16.5c-1.5 1.26-2 5-2 5s3.74-.5 5-2c.71-.84.7-2.13-.09-2.91a2.18 2.18 0 0 0-2.91 0z" /> <path d="m12 15-3-3a22 22 0 0 1 2-3.95A12.88 12.88 0 0 1 22 2c0 2.72-.78 7.5-6 11a22.35 22.35 0 0 1-4 2z" /> <path d="M9 12H4s.55-3.03 2-4c1.62-1.08 5 0 5 0" /> <path d="M12 15v5s3.03-.55 4-2c1.08-1.62 0-5 0-5" /></symbol>
|
|
<symbol id="i-settings" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M9.671 4.136a2.34 2.34 0 0 1 4.659 0 2.34 2.34 0 0 0 3.319 1.915 2.34 2.34 0 0 1 2.33 4.033 2.34 2.34 0 0 0 0 3.831 2.34 2.34 0 0 1-2.33 4.033 2.34 2.34 0 0 0-3.319 1.915 2.34 2.34 0 0 1-4.659 0 2.34 2.34 0 0 0-3.32-1.915 2.34 2.34 0 0 1-2.33-4.033 2.34 2.34 0 0 0 0-3.831A2.34 2.34 0 0 1 6.35 6.051a2.34 2.34 0 0 0 3.319-1.915" /> <circle cx="12" cy="12" r="3" /></symbol>
|
|
<symbol id="i-bell" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10.268 21a2 2 0 0 0 3.464 0" /> <path d="M3.262 15.326A1 1 0 0 0 4 17h16a1 1 0 0 0 .74-1.673C19.41 13.956 18 12.499 18 8A6 6 0 0 0 6 8c0 4.499-1.411 5.956-2.738 7.326" /></symbol>
|
|
<symbol id="i-x" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 6 6 18" /> <path d="m6 6 12 12" /></symbol>
|
|
<symbol id="i-check" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6 9 17l-5-5" /></symbol>
|
|
<symbol id="i-pencil" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21.174 6.812a1 1 0 0 0-3.986-3.987L3.842 16.174a2 2 0 0 0-.5.83l-1.321 4.352a.5.5 0 0 0 .623.622l4.353-1.32a2 2 0 0 0 .83-.497z" /> <path d="m15 5 4 4" /></symbol>
|
|
<symbol id="i-info" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="10" /> <path d="M12 16v-4" /> <path d="M12 8h.01" /></symbol>
|
|
<symbol id="i-download" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 15V3" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /> <path d="m7 10 5 5 5-5" /></symbol>
|
|
<symbol id="i-upload" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 3v12" /> <path d="m17 8-5-5-5 5" /> <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" /></symbol>
|
|
<symbol id="i-trash-2" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 11v6" /> <path d="M14 11v6" /> <path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6" /> <path d="M3 6h18" /> <path d="M8 6V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" /></symbol>
|
|
<symbol id="i-wrench" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M14.7 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l3.106-3.105c.32-.322.863-.22.983.218a6 6 0 0 1-8.259 7.057l-7.91 7.91a1 1 0 0 1-2.999-3l7.91-7.91a6 6 0 0 1 7.057-8.259c.438.12.54.662.219.984z" /></symbol>
|
|
<symbol id="i-link" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" /> <path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" /></symbol>
|
|
<symbol id="i-search" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m21 21-4.34-4.34" /> <circle cx="11" cy="11" r="8" /></symbol>
|
|
<symbol id="i-plus" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 12h14" /> <path d="M12 5v14" /></symbol>
|
|
<symbol id="i-chevron-down" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="m6 9 6 6 6-6" /></symbol>
|
|
<symbol id="i-play" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M5 5a2 2 0 0 1 3.008-1.728l11.997 6.998a2 2 0 0 1 .003 3.458l-12 7A2 2 0 0 1 5 19z" /></symbol>
|
|
<symbol id="i-pause" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><rect x="14" y="3" width="5" height="18" rx="1" /> <rect x="5" y="3" width="5" height="18" rx="1" /></symbol>
|
|
<symbol id="i-menu" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M4 12h16" /> <path d="M4 6h16" /> <path d="M4 18h16" /></symbol>
|
|
</svg>
|
|
<header class="mobile-topbar">
|
|
<a href="/" class="mobile-topbar-logo"><img src="/static/felhom-logo.svg?v=0.257.0" alt="Felhom.eu"></a>
|
|
<button type="button" class="nav-burger" aria-expanded="false" aria-controls="sidebar" aria-label="Menu">
|
|
<svg class="ico"><use href="#i-menu"/></svg>
|
|
</button>
|
|
</header>
|
|
<div class="nav-backdrop" hidden></div>
|
|
<nav class="sidebar" id="sidebar">
|
|
<div class="sidebar-header">
|
|
<img src="/static/felhom-logo.svg?v=0.257.0" alt="Felhom.eu" class="sidebar-logo">
|
|
</div>
|
|
<ul class="nav-links">
|
|
<li><a href="/launcher" class=""><svg class="ico"><use href="#i-rocket"/></svg>Launcher</a></li>
|
|
<li><a href="/dashboard" class=""><svg class="ico"><use href="#i-layout-grid"/></svg>Dashboard</a></li>
|
|
<li><a href="/stacks" class="active"><svg class="ico"><use href="#i-cloud"/></svg>Apps</a></li>
|
|
|
|
<li class="nav-group">
|
|
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-storage"><svg class="ico"><use href="#i-hard-drive"/></svg>Storage<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
|
<ul id="nav-group-storage" class="nav-links nav-links-sub nav-links-nested">
|
|
<li><a href="/storage" class="">Drives</a></li>
|
|
<li><a href="/storage/network" class="">Network storage</a></li>
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="nav-group">
|
|
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-backups"><svg class="ico"><use href="#i-shield"/></svg>Backup<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
|
<ul id="nav-group-backups" class="nav-links nav-links-sub nav-links-nested">
|
|
<li><a href="/backups" class="">Overview</a></li>
|
|
<li><a href="/backups/remote" class="">Remote backup</a></li>
|
|
<li><a href="/backups/apps" class="">Apps</a></li>
|
|
<li><a href="/backups/restore" class="">Restore</a></li>
|
|
</ul>
|
|
</li>
|
|
|
|
<li class="nav-group">
|
|
<button type="button" class="nav-group-toggle" aria-expanded="false" aria-controls="nav-group-sharing"><svg class="ico"><use href="#i-share"/></svg>Sharing<svg class="ico nav-chevron"><use href="#i-chevron-down"/></svg></button>
|
|
<ul id="nav-group-sharing" class="nav-links nav-links-sub nav-links-nested">
|
|
<li><a href="/sharing" class="">Network sharing</a></li>
|
|
</ul>
|
|
</li>
|
|
<li><a href="/monitoring" class=""><svg class="ico"><use href="#i-cpu"/></svg>System monitor</a></li>
|
|
<li><a href="/debug" class=""><svg class="ico"><use href="#i-wrench"/></svg>Debug</a></li>
|
|
</ul>
|
|
<div class="sidebar-bottom">
|
|
<div class="nav-group-label">Settings</div>
|
|
<ul class="nav-links nav-links-sub">
|
|
<li><a href="/settings" class=""><svg class="ico"><use href="#i-settings"/></svg>System</a></li>
|
|
<li><a href="/settings/notifications" class=""><svg class="ico"><use href="#i-bell"/></svg>Notifications</a></li>
|
|
<li><a href="/settings/security" class=""><svg class="ico"><use href="#i-lock"/></svg>Security and access</a></li>
|
|
</ul>
|
|
<div class="sidebar-footer">
|
|
<span class="version">0.257.0</span><details class="lang-globe">
|
|
<summary class="lang-globe-btn" aria-label="Language" title="Language"><svg class="ico" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><circle cx="12" cy="12" r="10" /><path d="M2 12h20" /><path d="M12 2a15.3 15.3 0 0 1 4 10 15.3 15.3 0 0 1-4 10 15.3 15.3 0 0 1-4-10 15.3 15.3 0 0 1 4-10z" /></svg></summary>
|
|
<ul class="lang-globe-menu">
|
|
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="8869722e21aa4e65abe2803da4fbb736f7a901b80840b9976cb56fe18295b071"><input type="hidden" name="back" value="/stacks"><button type="submit" name="lang" value="hu" lang="hu" class="lang-globe-item">Magyar</button></form></li>
|
|
<li><form method="POST" action="/settings/language"><input type="hidden" name="_csrf" value="8869722e21aa4e65abe2803da4fbb736f7a901b80840b9976cb56fe18295b071"><input type="hidden" name="back" value="/stacks"><button type="submit" name="lang" value="en" lang="en" class="lang-globe-item current" aria-current="true">English</button></form></li>
|
|
</ul>
|
|
</details>
|
|
<a href="/logout" class="logout-link">Sign out ↗</a>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
<main class="content">
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<div class="page-header">
|
|
<h2>Apps</h2>
|
|
<span class="domain-badge">enkisfelhom.hu</span>
|
|
<a href="/import" class="btn btn-sm btn-outline" title="Restore an app from an exported package">Import</a>
|
|
<button class="btn btn-sm btn-outline" id="sync-btn" onclick="syncTemplates()" title="Refresh the templates from the central catalog">↻ Refresh templates</button>
|
|
</div>
|
|
<div id="sync-toast" class="sync-toast" style="display:none"></div>
|
|
|
|
<div class="filter-bar" id="filter-bar">
|
|
<button class="filter-btn active" data-filter="all">All <span class="filter-count" id="count-all"></span></button>
|
|
<button class="filter-btn" data-filter="running">Running <span class="filter-count" id="count-running"></span></button>
|
|
<button class="filter-btn" data-filter="stopped">Stopped <span class="filter-count" id="count-stopped"></span></button>
|
|
<button class="filter-btn" data-filter="available">Installable <span class="filter-count" id="count-available"></span></button>
|
|
</div>
|
|
|
|
<div class="stack-grid">
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/actualbudget">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/actualbudget-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/actualbudget-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>ActualBudget</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Personal finances and budgeting</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/actualbudget/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'actualbudget')">Install</a>
|
|
<a href="/apps/actualbudget" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/adventurelog">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/adventurelog-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/adventurelog-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>AdventureLog</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://travel.enkisfelhom.hu" target="_blank">
|
|
travel.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Travel journal and trip planner</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">adventurelog</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">adventurelog-postgres</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">adventurelog-frontend</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'adventurelog', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'adventurelog', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'adventurelog', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/adventurelog/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/adventurelog" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/audiobookshelf">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/audiobookshelf-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/audiobookshelf-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Audiobookshelf</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">An audiobook and podcast server</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/audiobookshelf/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'audiobookshelf')">Install</a>
|
|
<a href="/apps/audiobookshelf" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/bentopdf">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/bentopdf-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/bentopdf-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>BentoPDF</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://pdf.enkisfelhom.hu" target="_blank">
|
|
pdf.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A privacy-first PDF toolbox</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">bentopdf</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'bentopdf', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'bentopdf', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'bentopdf', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/bentopdf/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/bentopdf" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/bookstack">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/bookstack-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/bookstack-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>BookStack</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://wiki.enkisfelhom.hu" target="_blank">
|
|
wiki.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A simple, book-shaped wiki and documentation platform</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~150M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">bookstack</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">bookstack-db</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'bookstack', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'bookstack', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'bookstack', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/bookstack/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/bookstack" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/calcom">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/calcom-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/calcom-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Cal.com</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Open source appointment booking (a Calendly alternative)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/calcom/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'calcom')">Install</a>
|
|
<a href="/apps/calcom" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/calibre-web">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/calibre-web-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/calibre-web-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Calibre-Web Automated</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://books.enkisfelhom.hu" target="_blank">
|
|
books.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">An automated e-book library - conversion, metadata and a web reader</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
<span class="metarow" title="Data storage: NVME 1TB"><svg class="ico"><use href="#i-hard-drive"/></svg>NVME 1TB</span>
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">calibre-web</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'calibre-web', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'calibre-web', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'calibre-web', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/calibre-web/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/calibre-web" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/claper">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/claper-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/claper-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Claper</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Interactive presentations that bring the room in</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/claper/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'claper')">Install</a>
|
|
<a href="/apps/claper" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/cloudflared">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/cloudflared-logo.svg" alt="" data-fallback="/static/infra-logo.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/cloudflared-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Cloudflare Tunnel</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Secure internet connection — the server is reachable from outside without opening ports.</p>
|
|
|
|
|
|
<div class="metarows">
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">cloudflared</span>
|
|
<span class="container-status state-text-run">Up 2 weeks</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>Protected system component</span>
|
|
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'cloudflared', 'restart')">Restart</button>
|
|
|
|
|
|
<a href="/apps/cloudflared" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/code-server">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/code-server-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/code-server-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Code-Server</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">VS Code in your browser - write code from anywhere</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/code-server/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'code-server')">Install</a>
|
|
<a href="/apps/code-server" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/crafty-controller">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/crafty-controller-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/crafty-controller-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Crafty Controller</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A Minecraft server manager with a web interface</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~256M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/crafty-controller/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'crafty-controller')">Install</a>
|
|
<a href="/apps/crafty-controller" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/docmost">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/docmost-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/docmost-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Docmost</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://docs.enkisfelhom.hu" target="_blank">
|
|
docs.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A modern wiki and documentation platform (Notion-like)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">docmost</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">docmost-postgres</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">docmost-redis</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'docmost', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'docmost', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'docmost', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/docmost/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/docmost" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/emby">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/emby-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/emby-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Emby</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A personal media server with live TV and recording</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/emby/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'emby')">Install</a>
|
|
<a href="/apps/emby" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/filebrowser">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/filebrowser-logo.svg" alt="" data-fallback="/static/infra-logo.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/filebrowser-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>FileBrowser</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://files.enkisfelhom.hu" target="_blank">
|
|
files.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">File manager — browse the storage files from your browser.</p>
|
|
|
|
|
|
<div class="metarows">
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">filebrowser</span>
|
|
<span class="container-status state-text-run">Up 2 weeks (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>Protected system component</span>
|
|
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'filebrowser', 'restart')">Restart</button>
|
|
|
|
|
|
<a href="/apps/filebrowser" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/ghost">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/ghost-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/ghost-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Ghost</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A professional blog and newsletter platform</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~150M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/ghost/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'ghost')">Install</a>
|
|
<a href="/apps/ghost" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/gitea">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/gitea-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/gitea-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Gitea</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A light Git server of your own, with a web interface</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/gitea/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'gitea')">Install</a>
|
|
<a href="/apps/gitea" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/glance">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/glance-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/glance-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Glance</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A minimalist information dashboard</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~20M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/glance/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'glance')">Install</a>
|
|
<a href="/apps/glance" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/gokapi">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/gokapi-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/gokapi-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Gokapi</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Temporary file sharing with links that expire</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/gokapi/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'gokapi')">Install</a>
|
|
<a href="/apps/gokapi" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/grafana">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/grafana-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/grafana-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Grafana</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A professional monitoring and visualisation platform</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/grafana/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'grafana')">Install</a>
|
|
<a href="/apps/grafana" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/gramps-web">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/gramps-web-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/gramps-web-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Gramps Web</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Family tree and genealogy software</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~384M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/gramps-web/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'gramps-web')">Install</a>
|
|
<a href="/apps/gramps-web" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/home-assistant">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/home-assistant-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/home-assistant-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Home Assistant</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">An open source smart home hub</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~256M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/home-assistant/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'home-assistant')">Install</a>
|
|
<a href="/apps/home-assistant" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/homebox">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/homebox-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/homebox-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Homebox</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A home inventory for your things</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/homebox/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'homebox')">Install</a>
|
|
<a href="/apps/homebox" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/homepage">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/homepage-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/homepage-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Homepage</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A start page you build yourself, with service widgets</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/homepage/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'homepage')">Install</a>
|
|
<a href="/apps/homepage" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/immich">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/immich-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/immich-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Immich</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Photos and videos in one place (a Google Photos alternative)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~2048M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/immich/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'immich')">Install</a>
|
|
<a href="/apps/immich" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/jellyfin">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/jellyfin-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/jellyfin-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Jellyfin</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A free and open source media server</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/jellyfin/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'jellyfin')">Install</a>
|
|
<a href="/apps/jellyfin" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/seerr">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/seerr-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/seerr-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Jellyseerr</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Media requests, wired into Jellyfin/Plex</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/seerr/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'seerr')">Install</a>
|
|
<a href="/apps/seerr" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/kimai">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/kimai-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/kimai-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Kimai</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://time.enkisfelhom.hu" target="_blank">
|
|
time.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Time tracking and project management</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">kimai</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">kimai-db</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'kimai', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'kimai', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'kimai', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/kimai/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/kimai" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/komga">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/komga-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/komga-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Komga</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A comic and manga server with OPDS support</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/komga/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'komga')">Install</a>
|
|
<a href="/apps/komga" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/mealie">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/mealie-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/mealie-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Mealie</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A recipe manager and meal planner</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/mealie/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'mealie')">Install</a>
|
|
<a href="/apps/mealie" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/navidrome">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/navidrome-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/navidrome-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Navidrome</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A light music server with Subsonic API support</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/navidrome/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'navidrome')">Install</a>
|
|
<a href="/apps/navidrome" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/nextcloud">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/nextcloud-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/nextcloud-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Nextcloud</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Your own cloud storage - a Google Drive/Dropbox alternative</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~256M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/nextcloud/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'nextcloud')">Install</a>
|
|
<a href="/apps/nextcloud" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/onlyoffice">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/onlyoffice-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/onlyoffice-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>OnlyOffice</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A full office suite in your browser</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/onlyoffice/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'onlyoffice')">Install</a>
|
|
<a href="/apps/onlyoffice" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/opengist">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/opengist-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/opengist-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>OpenGist</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://gist.enkisfelhom.hu" target="_blank">
|
|
gist.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Share code snippets (a GitHub Gist alternative)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">opengist</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'opengist', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'opengist', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'opengist', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/opengist/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/opengist" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/outline">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/outline-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/outline-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Outline</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A modern team knowledge base with Markdown</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~200M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/outline/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'outline')">Install</a>
|
|
<a href="/apps/outline" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/paperless-ngx">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/paperless-ngx-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/paperless-ngx-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Paperless-ngx</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://papir.enkisfelhom.hu" target="_blank">
|
|
papir.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Digitise your documents and keep them in order</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~500M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
<span class="metarow" title="Data storage: NVME 1TB"><svg class="ico"><use href="#i-hard-drive"/></svg>NVME 1TB</span>
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">paperless-webserver</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">paperless-postgres</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">paperless-redis</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'paperless-ngx', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'paperless-ngx', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'paperless-ngx', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/paperless-ngx/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/paperless-ngx" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/papra">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/papra-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/papra-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Papra</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A minimalist document store and organiser</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/papra/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'papra')">Install</a>
|
|
<a href="/apps/papra" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/plex">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/plex-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/plex-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Plex</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A popular media server with a polished interface</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/plex/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'plex')">Install</a>
|
|
<a href="/apps/plex" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/privatebin">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/privatebin-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/privatebin-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>PrivateBin</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://paste.enkisfelhom.hu" target="_blank">
|
|
paste.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Encrypted note and text sharing</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">privatebin</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'privatebin', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'privatebin', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'privatebin', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/privatebin/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/privatebin" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/radarr">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/radarr-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/radarr-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Radarr</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Films download and file themselves</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~150M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/radarr/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'radarr')">Install</a>
|
|
<a href="/apps/radarr" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/rallly">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/rallly-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/rallly-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Rallly</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Vote on a date (a Doodle alternative)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~384M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/rallly/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'rallly')">Install</a>
|
|
<a href="/apps/rallly" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/recipe-importer">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/recipe-importer-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/recipe-importer-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Recipe Importer</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Import Hungarian recipe sites into Mealie and Tandoor</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/recipe-importer/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'recipe-importer')">Install</a>
|
|
<a href="/apps/recipe-importer" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/romm">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/romm-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/romm-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>RomM</h3>
|
|
|
|
|
|
<a class="subdomain-link" href="https://jatek.enkisfelhom.hu" target="_blank">
|
|
jatek.enkisfelhom.hu ↗
|
|
</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
<span class="tag tag-ok" title="Ez az alkalmazás a legfrissebb elérhető változatot futtatja.">Naprakész</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Retro game collection manager</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~300M</span></span>
|
|
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
<span class="metarow" title="Data storage: NVME 1TB"><svg class="ico"><use href="#i-hard-drive"/></svg>NVME 1TB</span>
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">romm</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">romm-redis</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">romm-db</span>
|
|
<span class="container-status state-text-run">Up 10 hours (healthy)</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
|
|
|
|
<button class="btn btn-success" onclick="stackAction(event, 'romm', 'update')">Update</button>
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'romm', 'restart')">Restart</button>
|
|
<button class="btn btn-danger" onclick="stackAction(event, 'romm', 'stop')">Stop</button>
|
|
|
|
<a href="/stacks/romm/logs" class="btn btn-outline">Logs</a>
|
|
<a href="/apps/romm" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/sonarr">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/sonarr-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/sonarr-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Sonarr</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Series download and file themselves</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~150M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-hard-drive"/></svg>Needs a hard drive</span>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/sonarr/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'sonarr')">Install</a>
|
|
<a href="/apps/sonarr" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/sparkyfitness">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/sparkyfitness-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/sparkyfitness-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>SparkyFitness</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A food and exercise tracker</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~400M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/sparkyfitness/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'sparkyfitness')">Install</a>
|
|
<a href="/apps/sparkyfitness" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/tandoor">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/tandoor-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/tandoor-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Tandoor Recipes</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A recipe manager and meal planner</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/tandoor/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'tandoor')">Install</a>
|
|
<a href="/apps/tandoor" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/termix">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/termix-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/termix-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Termix</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">SSH and server management in a browser</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/termix/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'termix')">Install</a>
|
|
<a href="/apps/termix" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-run" data-filter-state="running" data-href="/apps/traefik">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/traefik-logo.svg" alt="" data-fallback="/static/infra-logo.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/traefik-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Traefik</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-run"><span class="dot"></span>Running</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Traffic router (reverse proxy) — sends each request to the right app.</p>
|
|
|
|
|
|
<div class="metarows">
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<div class="container-list">
|
|
|
|
<div class="container-row">
|
|
<span class="container-name">traefik</span>
|
|
<span class="container-status state-text-run">Up 2 weeks</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
<span class="tag"><svg class="ico ico-sm"><use href="#i-lock"/></svg>Protected system component</span>
|
|
|
|
<button class="btn btn-warning" onclick="stackAction(event, 'traefik', 'restart')">Restart</button>
|
|
|
|
|
|
<a href="/apps/traefik" class="btn btn-outline">Details</a>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/uptime-kuma">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/uptime-kuma-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/uptime-kuma-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Uptime Kuma</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Watch your services and websites</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/uptime-kuma/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'uptime-kuma')">Install</a>
|
|
<a href="/apps/uptime-kuma" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/vaultwarden">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/vaultwarden-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/vaultwarden-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Vaultwarden</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A password manager (Bitwarden-compatible)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/vaultwarden/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'vaultwarden')">Install</a>
|
|
<a href="/apps/vaultwarden" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/vikunja">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/vikunja-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/vikunja-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Vikunja</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Task lists and boards (a Todoist/Trello alternative)</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~50M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/vikunja/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'vikunja')">Install</a>
|
|
<a href="/apps/vikunja" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/wanderer">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/wanderer-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/wanderer-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Wanderer</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A hike planner that follows your tracks</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~350M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/wanderer/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'wanderer')">Install</a>
|
|
<a href="/apps/wanderer" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/wishlist">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/wishlist-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/wishlist-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Wishlist</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Wish lists shared across the household</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~30M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/wishlist/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'wishlist')">Install</a>
|
|
<a href="/apps/wishlist" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/zipline">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/zipline-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/zipline-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>Zipline</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A ShareX/Flameshot server - screenshots and file sharing</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/zipline/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'zipline')">Install</a>
|
|
<a href="/apps/zipline" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/n8n">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/n8n-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/n8n-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>n8n</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">Workflow automation with a visual editor</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~512M</span></span>
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/n8n/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'n8n')">Install</a>
|
|
<a href="/apps/n8n" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
|
|
<div class="stack-detail-card stack-state-off" data-filter-state="available" data-href="/apps/wger">
|
|
<div class="stack-detail-header">
|
|
<div class="stack-title-row">
|
|
<img class="stack-logo-lg" src="/static/assets/wger-logo.svg" alt="" data-fallback="/static/app-placeholder.svg"
|
|
onerror="if(!this.dataset.step){this.dataset.step='1';this.src='\/static\/assets\/wger-logo.png';}else if(this.dataset.fallback&&this.dataset.step==='1'){this.dataset.step='2';this.src=this.dataset.fallback;}else{this.onerror=null;this.style.visibility='hidden';}">
|
|
<div>
|
|
<h3>wger</h3>
|
|
|
|
|
|
</div>
|
|
</div>
|
|
<span class="tag tag-off"><span class="dot"></span>Not installed</span>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
<p class="stack-detail-desc">A workout log and fitness planner</p>
|
|
|
|
|
|
<div class="metarows">
|
|
<span class="metarow"><svg class="ico"><use href="#i-memory-stick"/></svg><span class="mono">~100M</span></span>
|
|
<span class="metarow"><svg class="ico"><use href="#i-check"/></svg>Runs on Pi</span>
|
|
|
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
<div class="stack-detail-actions">
|
|
|
|
|
|
<a href="/stacks/wger/deploy" class="btn btn-primary" onclick="return checkBeforeDeploy(event, 'wger')">Install</a>
|
|
<a href="/apps/wger" class="btn btn-outline">Details</a>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<script>
|
|
(function() {
|
|
var cards = document.querySelectorAll('.stack-detail-card[data-filter-state]');
|
|
var counts = {all: cards.length, running: 0, stopped: 0, available: 0};
|
|
|
|
cards.forEach(function(card) {
|
|
var cat = card.getAttribute('data-filter-state');
|
|
if (counts[cat] !== undefined) counts[cat]++;
|
|
});
|
|
|
|
['all', 'running', 'stopped', 'available'].forEach(function(f) {
|
|
var el = document.getElementById('count-' + f);
|
|
if (el) el.textContent = '(' + counts[f] + ')';
|
|
});
|
|
|
|
function applyFilter(filter) {
|
|
cards.forEach(function(card) {
|
|
if (filter === 'all' || card.getAttribute('data-filter-state') === filter) {
|
|
card.style.display = '';
|
|
} else {
|
|
card.style.display = 'none';
|
|
}
|
|
});
|
|
|
|
document.querySelectorAll('.filter-btn').forEach(function(btn) {
|
|
btn.classList.toggle('active', btn.getAttribute('data-filter') === filter);
|
|
});
|
|
|
|
var url = new URL(window.location);
|
|
if (filter === 'all') {
|
|
url.searchParams.delete('filter');
|
|
} else {
|
|
url.searchParams.set('filter', filter);
|
|
}
|
|
history.replaceState(null, '', url);
|
|
}
|
|
|
|
document.querySelectorAll('.filter-btn').forEach(function(btn) {
|
|
btn.addEventListener('click', function() {
|
|
applyFilter(this.getAttribute('data-filter'));
|
|
});
|
|
});
|
|
|
|
var urlFilter = new URLSearchParams(window.location.search).get('filter');
|
|
if (urlFilter && ['running', 'stopped', 'available'].indexOf(urlFilter) !== -1) {
|
|
applyFilter(urlFilter);
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
|
|
</main>
|
|
<script>
|
|
|
|
|
|
|
|
(function(){
|
|
var burger=document.querySelector('.nav-burger');
|
|
var sidebar=document.getElementById('sidebar');
|
|
var backdrop=document.querySelector('.nav-backdrop');
|
|
if(!burger||!sidebar||!backdrop)return;
|
|
function setOpen(open){
|
|
sidebar.classList.toggle('is-open',open);
|
|
document.body.classList.toggle('nav-open',open);
|
|
backdrop.hidden=!open;
|
|
burger.setAttribute('aria-expanded',open?'true':'false');
|
|
}
|
|
burger.addEventListener('click',function(){setOpen(!sidebar.classList.contains('is-open'));});
|
|
backdrop.addEventListener('click',function(){setOpen(false);});
|
|
document.addEventListener('keydown',function(e){
|
|
if(e.key==='Escape'&&sidebar.classList.contains('is-open'))setOpen(false);
|
|
});
|
|
})();
|
|
document.addEventListener('click', function(e) {
|
|
if (e.target.closest('a, button, .btn, input, select, textarea, .app-row-actions, .stack-detail-actions')) return;
|
|
var card = e.target.closest('[data-href]');
|
|
if (card) window.location.href = card.dataset.href;
|
|
});
|
|
async function checkBeforeDeploy(e, name) {
|
|
try {
|
|
var resp = await fetch('/api/stacks/' + name);
|
|
var data = await resp.json();
|
|
if (data.ok && data.data && data.data.deployed) {
|
|
e.preventDefault();
|
|
showAlert('This app is already installed.');
|
|
window.location.reload();
|
|
return false;
|
|
}
|
|
} catch(err) {}
|
|
return true;
|
|
}
|
|
async function syncTemplates() {
|
|
const btn = document.getElementById('sync-btn');
|
|
const toast = document.getElementById('sync-toast');
|
|
if (!btn) return;
|
|
const origText = btn.innerHTML;
|
|
btn.disabled = true;
|
|
btn.innerHTML = '↻ Refreshing...';
|
|
btn.classList.add('loading');
|
|
try {
|
|
const resp = await fetch('/api/sync', {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
|
});
|
|
const data = await resp.json();
|
|
if (toast) {
|
|
toast.textContent = data.ok ? (data.message || 'Templates refreshed') : ('Error: ' + (data.error || 'Unknown error'));
|
|
toast.className = 'sync-toast ' + (data.ok ? 'sync-toast-ok' : 'sync-toast-err');
|
|
toast.style.display = 'block';
|
|
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
|
}
|
|
if (data.ok && data.data && (data.data.new_apps && data.data.new_apps.length > 0 || data.data.updated && data.data.updated.length > 0)) {
|
|
setTimeout(function() { window.location.reload(); }, 1500);
|
|
}
|
|
} catch (err) {
|
|
if (toast) {
|
|
toast.textContent = 'Network error: ' + err.message;
|
|
toast.className = 'sync-toast sync-toast-err';
|
|
toast.style.display = 'block';
|
|
setTimeout(function() { toast.style.display = 'none'; }, 5000);
|
|
}
|
|
}
|
|
btn.innerHTML = origText;
|
|
btn.disabled = false;
|
|
btn.classList.remove('loading');
|
|
}
|
|
async function stackAction(event, name, action) {
|
|
const btn = event.currentTarget;
|
|
const origText = btn.textContent;
|
|
btn.disabled = true;
|
|
btn.textContent = 'In progress...';
|
|
btn.classList.add('loading');
|
|
try {
|
|
const resp = await fetch('/api/stacks/' + name + '/' + action, {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders())
|
|
});
|
|
const data = await resp.json();
|
|
if (!data.ok) {
|
|
showAlert('Error: ' + (data.error || 'Unknown error'));
|
|
btn.textContent = origText;
|
|
btn.disabled = false;
|
|
btn.classList.remove('loading');
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
if (action === 'update') {
|
|
followUpdate(name, btn);
|
|
return;
|
|
}
|
|
window.location.reload();
|
|
} catch (err) {
|
|
showAlert('Network error: ' + err.message);
|
|
btn.textContent = origText;
|
|
btn.disabled = false;
|
|
btn.classList.remove('loading');
|
|
}
|
|
}
|
|
function followUpdate(name, btn) {
|
|
var started = Date.now();
|
|
var timer = setInterval(async function() {
|
|
if (Date.now() - started > 30 * 60 * 1000) { clearInterval(timer); window.location.reload(); return; }
|
|
try {
|
|
var r = await fetch('/api/stacks/' + name);
|
|
var d = await r.json();
|
|
if (!d.ok || !d.data) return;
|
|
if (d.data.update_phase_label) btn.textContent = d.data.update_phase_label;
|
|
if (!d.data.updating) { clearInterval(timer); window.location.reload(); }
|
|
} catch (e) {}
|
|
}, 3000);
|
|
}
|
|
async function deleteOrphanStack(name) {
|
|
var modal = document.createElement('div');
|
|
modal.className = 'modal-overlay';
|
|
modal.id = 'delete-modal';
|
|
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
|
modal.addEventListener('click', function(e) { if (e.target === modal) closeDeleteModal(); });
|
|
document.body.appendChild(modal);
|
|
try {
|
|
var resp = await fetch('/api/stacks/' + name + '/hdd-data');
|
|
var data = await resp.json();
|
|
var hddInfo = '';
|
|
var checkboxHTML = '';
|
|
if (data.ok && data.data && data.data.has_hdd_data) {
|
|
hddInfo = '<div class="modal-hdd-info"><strong>Your data on the hard drive:</strong>';
|
|
data.data.hdd_paths.forEach(function(p) {
|
|
hddInfo += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
|
});
|
|
hddInfo += '</div>';
|
|
checkboxHTML = '<label class="modal-checkbox"><input type="checkbox" id="delete-hdd-check"> Delete your data from the hard drive</label>';
|
|
}
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Delete app: ' + name + '</h3>' +
|
|
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">This removes the containers, the volumes and the configuration files.</p>' +
|
|
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
|
hddInfo + checkboxHTML +
|
|
'<div class="modal-actions">' +
|
|
'<button class="btn btn-outline" onclick="closeDeleteModal()">Cancel</button>' +
|
|
'<button class="btn btn-danger" id="confirm-delete-btn" onclick="confirmDelete(\'' + name + '\')">Delete</button>' +
|
|
'</div>';
|
|
} catch (err) {
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
|
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeDeleteModal()">Close</button></div>';
|
|
}
|
|
}
|
|
function closeDeleteModal() {
|
|
var modal = document.getElementById('delete-modal');
|
|
if (modal) modal.remove();
|
|
}
|
|
async function confirmDelete(name) {
|
|
var btn = document.getElementById('confirm-delete-btn');
|
|
var checkbox = document.getElementById('delete-hdd-check');
|
|
var removeHDD = checkbox ? checkbox.checked : false;
|
|
btn.disabled = true;
|
|
btn.textContent = 'Deleting...';
|
|
try {
|
|
var resp = await fetch('/api/stacks/' + name, {
|
|
method: 'DELETE',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
body: JSON.stringify({remove_hdd_data: removeHDD})
|
|
});
|
|
var data = await resp.json();
|
|
if (data.ok) {
|
|
var modal = document.getElementById('delete-modal');
|
|
var removedInfo = '';
|
|
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
|
removedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
|
}
|
|
var preservedInfo = '';
|
|
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
|
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
|
}
|
|
if (data.data && data.data.hdd_note) {
|
|
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
|
}
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Deleted!</h3>' +
|
|
'<p style="color:var(--text-2)">The app (' + name + ') was deleted.</p>' +
|
|
removedInfo + preservedInfo +
|
|
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
|
} else {
|
|
showAlert('Error: ' + (data.error || 'Unknown error'));
|
|
btn.disabled = false;
|
|
btn.textContent = 'Delete';
|
|
}
|
|
} catch (err) {
|
|
showAlert('Network error: ' + err.message);
|
|
btn.disabled = false;
|
|
btn.textContent = 'Delete';
|
|
}
|
|
}
|
|
async function removeStack(name) {
|
|
var modal = document.createElement('div');
|
|
modal.className = 'modal-overlay';
|
|
modal.id = 'remove-modal';
|
|
modal.innerHTML = '<div class="modal-card"><h3>Loading...</h3></div>';
|
|
modal.addEventListener('click', function(e) { if (e.target === modal) closeRemoveModal(); });
|
|
document.body.appendChild(modal);
|
|
try {
|
|
var [hddResp, backupResp] = await Promise.all([
|
|
fetch('/api/stacks/' + name + '/hdd-data').then(function(r) { return r.json(); }),
|
|
fetch('/api/stacks/' + name + '/backup-data').then(function(r) { return r.json(); })
|
|
]);
|
|
var sections = '';
|
|
|
|
sections += '<div class="modal-section">' +
|
|
'<strong>Always deleted:</strong>' +
|
|
'<ul style="margin:.25rem 0;padding-left:1.2rem;color:var(--text-2);font-size:.85rem">' +
|
|
'<li>Docker volumes (database, app configuration)</li>' +
|
|
'<li>Install configuration (app.yaml)</li>' +
|
|
'<li>Secondary backup schedule</li>' +
|
|
'</ul></div>';
|
|
|
|
var hddCheckbox = '';
|
|
if (hddResp.ok && hddResp.data && hddResp.data.has_hdd_data) {
|
|
var hddPaths = '';
|
|
hddResp.data.hdd_paths.forEach(function(p) {
|
|
hddPaths += '<div class="modal-hdd-path">' + p.path + ' (' + (p.exists ? p.size_human : 'does not exist') + ')</div>';
|
|
});
|
|
sections += '<div class="modal-section">' +
|
|
'<strong>Your data on the hard drive:</strong>' + hddPaths +
|
|
'<label class="modal-checkbox"><input type="checkbox" id="remove-hdd-check"> Delete your data</label>' +
|
|
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem" id="remove-hdd-keep-warning">' +
|
|
'If you reinstall the app, you need to import the data again, because the database is deleted. Data you keep is NOT backed up automatically any more.' +
|
|
'</div></div>';
|
|
hddCheckbox = '\x3Cscript>document.getElementById("remove-hdd-check").addEventListener("change",function(){document.getElementById("remove-hdd-keep-warning").style.display=this.checked?"none":"";});<\/script>';
|
|
}
|
|
|
|
var backupCheckbox = '';
|
|
if (backupResp.ok && backupResp.data && backupResp.data.has_backups) {
|
|
var bkPaths = '';
|
|
backupResp.data.backup_paths.forEach(function(p) {
|
|
if (p.exists) bkPaths += '<div class="modal-hdd-path">' + p.path + ' (' + p.size_human + ')</div>';
|
|
});
|
|
if (bkPaths) {
|
|
sections += '<div class="modal-section">' +
|
|
'<strong>Backup data:</strong>' + bkPaths +
|
|
'<label class="modal-checkbox"><input type="checkbox" id="remove-backup-check"> Delete backup data</label>' +
|
|
'<div class="alert alert-info" style="margin-top:.5rem;font-size:.8rem">' +
|
|
'The nightly restic snapshots cannot be deleted one by one — they expire automatically under the retention rules.' +
|
|
'</div></div>';
|
|
}
|
|
}
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Remove app: ' + name + '</h3>' +
|
|
'<p style="color:var(--text-2);font-size:.9rem;margin-bottom:.75rem">The app goes back to "Not installed". The template stays, and you can install it again.</p>' +
|
|
'<div class="alert alert-warning" style="margin-bottom:.75rem">You cannot undo this!</div>' +
|
|
sections +
|
|
'<div class="modal-actions">' +
|
|
'<button class="btn btn-outline" onclick="closeRemoveModal()">Cancel</button>' +
|
|
'<button class="btn btn-danger" id="confirm-remove-btn" onclick="confirmRemoveStack(\'' + name + '\')">Remove</button>' +
|
|
'</div>' + hddCheckbox;
|
|
} catch (err) {
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Error</h3><p style="color:var(--text-2)">Could not load the details: ' + err.message + '</p>' +
|
|
'<div class="modal-actions"><button class="btn btn-outline" onclick="closeRemoveModal()">Close</button></div>';
|
|
}
|
|
}
|
|
function closeRemoveModal() {
|
|
var modal = document.getElementById('remove-modal');
|
|
if (modal) modal.remove();
|
|
}
|
|
async function confirmRemoveStack(name) {
|
|
var btn = document.getElementById('confirm-remove-btn');
|
|
var hddCheck = document.getElementById('remove-hdd-check');
|
|
var backupCheck = document.getElementById('remove-backup-check');
|
|
var removeHDD = hddCheck ? hddCheck.checked : false;
|
|
var removeBackups = backupCheck ? backupCheck.checked : false;
|
|
btn.disabled = true;
|
|
btn.textContent = 'Removing...';
|
|
try {
|
|
var resp = await fetch('/api/stacks/' + name + '/remove', {
|
|
method: 'POST',
|
|
headers: Object.assign({'Content-Type': 'application/json'}, csrfHeaders()),
|
|
body: JSON.stringify({remove_hdd_data: removeHDD, remove_backups: removeBackups})
|
|
});
|
|
var data = await resp.json();
|
|
if (data.ok) {
|
|
var modal = document.getElementById('remove-modal');
|
|
var removedInfo = '';
|
|
if (data.data && data.data.hdd_paths_removed && data.data.hdd_paths_removed.length > 0) {
|
|
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted data: ' + data.data.hdd_paths_removed.join(', ') + '</p>';
|
|
}
|
|
if (data.data && data.data.backup_paths_removed && data.data.backup_paths_removed.length > 0) {
|
|
removedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Deleted backups: ' + data.data.backup_paths_removed.join(', ') + '</p>';
|
|
}
|
|
var preservedInfo = '';
|
|
if (data.data && data.data.hdd_paths_preserved && data.data.hdd_paths_preserved.length > 0) {
|
|
preservedInfo = '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Kept data: ' + data.data.hdd_paths_preserved.join(', ') + '</p>';
|
|
}
|
|
if (data.data && data.data.hdd_note) {
|
|
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">' + data.data.hdd_note + '</p>';
|
|
}
|
|
if (data.data && data.data.backup_paths_refused && data.data.backup_paths_refused.length > 0) {
|
|
preservedInfo += '<p style="color:var(--text-2);font-size:.85rem;margin-top:.5rem">Backups not deleted: ' + data.data.backup_paths_refused.join('; ') + '</p>';
|
|
}
|
|
modal.querySelector('.modal-card').innerHTML =
|
|
'<h3>Removed!</h3>' +
|
|
'<p style="color:var(--text-2)">The app (' + name + ') was removed. You can install it again with the Install button.</p>' +
|
|
removedInfo + preservedInfo +
|
|
'<div class="modal-actions"><button class="btn btn-primary" onclick="window.location.href=\'/stacks\'">Close</button></div>';
|
|
} else {
|
|
showAlert('Error: ' + (data.error || 'Unknown error'));
|
|
btn.disabled = false;
|
|
btn.textContent = 'Remove';
|
|
}
|
|
} catch (err) {
|
|
showAlert('Network error: ' + err.message);
|
|
btn.disabled = false;
|
|
btn.textContent = 'Remove';
|
|
}
|
|
}
|
|
</script>
|
|
</body>
|
|
</html>
|
|
|