first commit

This commit is contained in:
Mateusz Gruszczyński committed 2026-10-30 12:59:58 +01:00
commit 0efaae1715
27 files changed
+1455

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
(() => {
const $ = (id) => document.getElementById(id);
const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (c) => ({'&':'&amp;','<':'&lt;','>':'&gt;','"':'&quot;',"'":'&#39;'}[c]));
const state = {data:null, view:'overview', install:'apt', search:'', channel:'all', format:'all'};
const titles = {overview:'Przegląd',packages:'Paczki',install:'Instalacja',repositories:'Repozytoria'};
const fmtDate = (value) => value ? new Intl.DateTimeFormat('pl-PL',{dateStyle:'medium',timeStyle:'short'}).format(new Date(value)) : '—';
const fmtSize = (n) => n < 1024 ? `${n} B` : n < 1048576 ? `${(n/1024).toFixed(1)} KiB` : `${(n/1048576).toFixed(1)} MiB`;
function toast(text){const t=$('toast');t.textContent=text;t.classList.add('show');setTimeout(()=>t.classList.remove('show'),1600)}
function navigate(view){state.view=view;document.querySelectorAll('.view').forEach(x=>x.classList.toggle('active',x.id===view));document.querySelectorAll('.nav').forEach(x=>x.classList.toggle('active',x.dataset.view===view));$('view-title').textContent=titles[view]||view;location.hash=view}
function packageRow(p){return `<div class="package-row"><div><div class="pkg-name">${esc(p.name)}</div><div class="muted">${esc(p.version)}</div></div><span class="pill">${esc(p.channel.toUpperCase())}</span><span class="pill">${esc(p.format)}</span><span class="muted">${esc(fmtSize(p.size_bytes))}</span><a class="download" href="${esc(p.download_url)}">Pobierz</a></div>`}
function renderOverview(){const d=state.data;if(!d)return;const linux=d.builds.linux||{};const suse=d.builds.suse||{};$('latest-version').textContent=linux.app_version||'—';$('latest-build').textContent=fmtDate(linux.built_at);$('packages-count').textContent=d.summary.packages_count;$('linux-status').textContent=linux.published?'GOTOWE':'BRAK';$('suse-status').textContent=suse.published?'GOTOWE':'BRAK';$('recent-packages').innerHTML=d.packages.slice(0,6).map(packageRow).join('')||'<div class="empty">Brak opublikowanych paczek.</div>'}
function renderPackages(){if(!state.data)return;const q=state.search.toLowerCase();const list=state.data.packages.filter(p=>(state.channel==='all'||p.channel===state.channel)&&(state.format==='all'||p.format===state.format)&&(!q||`${p.name} ${p.version} ${p.architecture}`.toLowerCase().includes(q)));$('packages-table').innerHTML=list.map(p=>`<div class="table-row"><div><div class="pkg-name">${esc(p.name)}</div><div class="muted">${esc(p.version)}</div></div><span class="pill">${esc(p.channel.toUpperCase())}</span><span class="pill">${esc(p.format)}</span><span class="muted">${esc(fmtDate(p.built_at))}</span><a class="download" href="${esc(p.download_url)}">Pobierz</a></div>`).join('')||'<div class="empty">Brak wyników.</div>'}
function instruction(kind){const d=state.data;const url=d.repository_url,dist=d.apt_distribution,comp=d.apt_component;if(kind==='dnf')return {title:'RHEL / Rocky / Alma / Fedora',desc:'Instalacja przez podpisane repozytorium DNF/YUM.',code:`sudo rpm --import ${url}/repo-key.gpg\n\nsudo tee /etc/yum.repos.d/rustbox.repo >/dev/null <<'REPO'\n[rustbox]\nname=RustBox Repository\nbaseurl=${url}/rpm/$basearch\nenabled=1\ngpgcheck=1\nrepo_gpgcheck=1\ngpgkey=${url}/repo-key.gpg\nREPO\n\nsudo dnf install rustbox`};if(kind==='suse')return {title:'SUSE / openSUSE',desc:'Instalacja przez podpisane repozytorium Zypper.',code:`sudo rpm --import ${url}/repo-key.gpg\nsudo zypper addrepo --refresh --gpgcheck ${url}/suse/x86_64/ rustbox\nsudo zypper refresh rustbox\nsudo zypper install rustbox`};return {title:'Debian / Ubuntu',desc:'Instalacja przez podpisane repozytorium APT.',code:`curl -fsSL ${url}/repo-key.gpg | sudo gpg --dearmor -o /usr/share/keyrings/rustbox.gpg\n\necho "deb [signed-by=/usr/share/keyrings/rustbox.gpg] ${url}/apt ${dist} ${comp}" | sudo tee /etc/apt/sources.list.d/rustbox.list\n\nsudo apt update\nsudo apt install rustbox`}}
function renderInstall(){if(!state.data)return;const x=instruction(state.install);$('install-title').textContent=x.title;$('install-desc').textContent=x.desc;$('install-command').textContent=x.code;document.querySelectorAll('[data-install]').forEach(b=>b.classList.toggle('active',b.dataset.install===state.install))}
function repoCard(title,url,links){return `<article class="repo-card"><h3>${esc(title)}</h3><a href="${esc(url)}">${esc(url)}</a><div class="repo-links">${links.map(([n,u])=>`<a href="${esc(u)}"><span>${esc(n)}</span></a>`).join('')}</div></article>`}
function renderRepositories(){const d=state.data;if(!d)return;const u=d.repository_url,dist=encodeURIComponent(d.apt_distribution);$('repo-cards').innerHTML=repoCard('APT + DNF',u,[['GPG public key',`${u}/repo-key.gpg`],['APT InRelease',`${u}/apt/dists/${dist}/InRelease`],['RPM repomd.xml',`${u}/rpm/x86_64/repodata/repomd.xml`],['Build info',`${u}/LINUX-BUILD-INFO.txt`]])+repoCard('SUSE / Zypper',`${u}/suse/x86_64`,[['RPM repomd.xml',`${u}/suse/x86_64/repodata/repomd.xml`],['Metadata signature',`${u}/suse/x86_64/repodata/repomd.xml.asc`],['Build info',`${u}/SUSE-BUILD-INFO.txt`]]);$('metadata').innerHTML=['linux','suse'].map(ch=>{const b=d.builds[ch]||{};return `<div class="meta-block"><h3>${ch==='linux'?'Linux':'SUSE'}</h3>${[['Wersja',b.app_version],['Wydanie paczki',b.package_release],['Build',fmtDate(b.built_at)],['Commit',b.source_commit?b.source_commit.slice(0,12):'—'],['Rewizja pakowania',b.packaging_revision]].map(([a,v])=>`<div class="meta-row"><span>${esc(a)}</span><strong>${esc(v||'—')}</strong></div>`).join('')}</div>`}).join('')}
function renderAll(){renderOverview();renderPackages();renderInstall();renderRepositories()}
async function refresh(){const btn=$('refresh');btn.disabled=true;try{const r=await fetch('/api/v1/packages',{cache:'no-store'});if(!r.ok)throw new Error(`HTTP ${r.status}`);state.data=await r.json();renderAll();$('last-sync').textContent=`Ostatnia synchronizacja: ${fmtDate(state.data.generated_at)}`;$('status-dot').classList.add('ok');$('status-text').textContent=state.data.ready?'Repozytorium aktywne':'Oczekiwanie na build'}catch(e){$('status-dot').classList.remove('ok');$('status-text').textContent='Błąd odczytu repozytorium';console.error(e)}finally{btn.disabled=false}}
function setTheme(theme){document.documentElement.dataset.theme=theme;localStorage.setItem('rustbox-repo-theme',theme)}
document.querySelectorAll('[data-view]').forEach(b=>b.addEventListener('click',()=>navigate(b.dataset.view)));
document.querySelectorAll('[data-go]').forEach(b=>b.addEventListener('click',()=>navigate(b.dataset.go)));
document.querySelectorAll('[data-install]').forEach(b=>b.addEventListener('click',()=>{state.install=b.dataset.install;renderInstall()}));
$('search').addEventListener('input',e=>{state.search=e.target.value;renderPackages()});$('channel').addEventListener('change',e=>{state.channel=e.target.value;renderPackages()});$('format').addEventListener('change',e=>{state.format=e.target.value;renderPackages()});$('refresh').addEventListener('click',refresh);$('copy-install').addEventListener('click',async()=>{try{await navigator.clipboard.writeText($('install-command').textContent);toast('Skopiowano')}catch{toast('Nie udało się skopiować')}});$('theme-toggle').addEventListener('click',()=>setTheme(document.documentElement.dataset.theme==='dark'?'light':'dark'));
try{setTheme(localStorage.getItem('rustbox-repo-theme')||'dark')}catch{}navigate(location.hash.slice(1)||'overview');window.addEventListener('hashchange',()=>navigate(location.hash.slice(1)||'overview'));refresh();setInterval(()=>{if(!document.hidden)refresh()},60000);
})();
+1
View File
@@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#8fb9a4"/><path d="M18 16h17c9 0 14 5 14 12 0 5-3 9-8 11l10 10H39L30 40h-2v9H18V16zm10 9v7h7c3 0 5-1 5-4s-2-3-5-3h-7z" fill="#102018"/></svg>

After

Width:  |  Height:  |  Size: 245 B

+64
View File
@@ -0,0 +1,64 @@
<!doctype html>
<html lang="pl" data-theme="dark">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<meta name="color-scheme" content="dark light">
<title>RustBox Repository</title>
<link rel="icon" href="/ui/favicon.svg">
<link rel="stylesheet" href="/ui/style.css">
</head>
<body>
<div class="shell">
<aside class="sidebar">
<div class="brand"><div class="brand-icon">R</div><div><strong>RustBox</strong><span>Package Repository</span></div></div>
<nav>
<button class="nav active" data-view="overview">Przegląd</button>
<button class="nav" data-view="packages">Paczki</button>
<button class="nav" data-view="install">Instalacja</button>
<button class="nav" data-view="repositories">Repozytoria</button>
</nav>
<div class="sidebar-foot"><span id="status-dot" class="status-dot"></span><span id="status-text">Ładowanie…</span></div>
</aside>
<main>
<header class="topbar">
<div><h1 id="view-title">Przegląd</h1><p id="last-sync">—</p></div>
<div class="toolbar"><button id="theme-toggle" title="Motyw">◐</button><button id="refresh" title="Odśwież">↻</button></div>
</header>
<section id="overview" class="view active">
<div class="hero">
<div><span class="eyebrow">RUSTBOX / LINUX</span><h2>Repozytorium wydań</h2><p>Podpisane paczki APT, DNF/RPM i Zypper budowane bezpośrednio ze źródeł RustBox.</p></div>
<div class="version-box"><span>NAJNOWSZA WERSJA</span><strong id="latest-version">—</strong><small id="latest-build">—</small></div>
</div>
<div class="cards">
<article><span>Paczki</span><strong id="packages-count">0</strong><small>opublikowane artefakty</small></article>
<article><span>Linux</span><strong id="linux-status">—</strong><small>APT + DNF</small></article>
<article><span>SUSE</span><strong id="suse-status">—</strong><small>Zypper RPM</small></article>
</div>
<div class="panel"><div class="panel-head"><h3>Ostatnie paczki</h3><button data-go="packages">Pokaż wszystkie</button></div><div id="recent-packages" class="package-list"></div></div>
</section>
<section id="packages" class="view">
<div class="panel">
<div class="filters"><input id="search" type="search" placeholder="Szukaj paczki lub wersji"><select id="channel"><option value="all">Wszystkie kanały</option><option value="linux">Linux</option><option value="suse">SUSE</option></select><select id="format"><option value="all">Wszystkie formaty</option><option value="DEB">DEB</option><option value="RPM">RPM</option></select></div>
<div id="packages-table" class="table"></div>
</div>
</section>
<section id="install" class="view">
<div class="install-tabs"><button class="active" data-install="apt">Debian / Ubuntu</button><button data-install="dnf">RHEL / Rocky / Alma / Fedora</button><button data-install="suse">SUSE / openSUSE</button></div>
<div class="panel command-panel"><div class="panel-head"><div><h3 id="install-title">Debian / Ubuntu</h3><p id="install-desc">Instalacja przez podpisane repozytorium APT.</p></div><button id="copy-install">Kopiuj</button></div><pre><code id="install-command"></code></pre></div>
<div class="note">Po instalacji konfiguracja znajduje się w <code>/etc/rustbox/rustbox.toml</code>, dane w <code>/var/lib/rustbox</code>, a logi aplikacji w <code>/var/log/rustbox/rustbox.log</code>.</div>
</section>
<section id="repositories" class="view">
<div id="repo-cards" class="repo-grid"></div>
<div class="panel"><div class="panel-head"><h3>Metadane buildu</h3></div><div id="metadata" class="metadata"></div></div>
</section>
</main>
</div>
<div id="toast" class="toast"></div>
<script src="/ui/app.js" defer></script>
</body>
</html>
+3
View File
@@ -0,0 +1,3 @@
:root{color-scheme:dark;--bg:#121513;--panel:#181c19;--panel2:#1e231f;--panel3:#242a25;--sidebar:#101310;--line:#313832;--line2:#485149;--text:#edf0ec;--muted:#9da79f;--subtle:#7c877f;--accent:#8fb9a4;--accent2:#a4c9b6;--good:#a7c892;--field:#121713;--hover:#202621;--shadow:rgba(0,0,0,.32);font-family:Inter,ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif}
html[data-theme="light"]{color-scheme:light;--bg:#f2f4f1;--panel:#fff;--panel2:#f7f8f6;--panel3:#edf1ec;--sidebar:#e9eee9;--line:#cbd2cb;--line2:#aeb8af;--text:#202622;--muted:#647068;--subtle:#7a847d;--accent:#507b67;--accent2:#416b58;--good:#587a49;--field:#fff;--hover:#e8ede8;--shadow:rgba(31,39,33,.16)}
*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--text);font-size:14px}.shell{min-height:100vh;display:grid;grid-template-columns:228px minmax(0,1fr)}.sidebar{position:sticky;top:0;height:100vh;background:var(--sidebar);border-right:1px solid var(--line);padding:18px 12px;display:flex;flex-direction:column}.brand{display:flex;gap:11px;align-items:center;padding:5px 8px 24px}.brand-icon{width:34px;height:34px;border-radius:9px;background:var(--accent);color:#102018;display:grid;place-items:center;font-weight:800;font-size:18px}.brand strong,.brand span{display:block}.brand span{color:var(--muted);font-size:11px;margin-top:2px}.sidebar nav{display:grid;gap:3px}.nav{border:0;background:transparent;color:var(--muted);text-align:left;padding:10px 12px;border-radius:5px;cursor:pointer;font-weight:600}.nav:hover{background:var(--hover);color:var(--text)}.nav.active{background:var(--panel3);color:var(--text);box-shadow:inset 2px 0 var(--accent)}.sidebar-foot{margin-top:auto;border-top:1px solid var(--line);padding:15px 8px 2px;color:var(--muted);display:flex;align-items:center;gap:8px;font-size:12px}.status-dot{width:8px;height:8px;border-radius:50%;background:var(--subtle)}.status-dot.ok{background:var(--good);box-shadow:0 0 0 3px color-mix(in srgb,var(--good) 15%,transparent)}main{min-width:0}.topbar{height:72px;border-bottom:1px solid var(--line);display:flex;align-items:center;justify-content:space-between;padding:0 24px;background:color-mix(in srgb,var(--bg) 84%,transparent);backdrop-filter:blur(10px);position:sticky;top:0;z-index:5}.topbar h1{font-size:18px;margin:0}.topbar p{margin:3px 0 0;color:var(--subtle);font-size:11px}.toolbar{display:flex;gap:7px}.toolbar button,.panel button,.install-tabs button{border:1px solid var(--line);background:var(--panel);color:var(--text);min-height:32px;padding:0 11px;border-radius:5px;cursor:pointer}.toolbar button:hover,.panel button:hover,.install-tabs button:hover{border-color:var(--line2);background:var(--hover)}.view{display:none;padding:24px;max-width:1320px;margin:0 auto}.view.active{display:block}.hero{display:grid;grid-template-columns:minmax(0,1fr) 240px;gap:18px;background:linear-gradient(135deg,var(--panel),var(--panel2));border:1px solid var(--line);border-radius:7px;padding:28px}.hero h2{font-size:30px;margin:7px 0 8px}.hero p{color:var(--muted);max-width:700px;margin:0;line-height:1.55}.eyebrow{color:var(--accent2);font-size:11px;font-weight:800;letter-spacing:.14em}.version-box{background:var(--field);border:1px solid var(--line);border-radius:6px;padding:18px;display:flex;flex-direction:column;justify-content:center}.version-box span{color:var(--subtle);font-size:10px;letter-spacing:.1em}.version-box strong{font-size:27px;margin:5px 0}.version-box small{color:var(--muted)}.cards{display:grid;grid-template-columns:repeat(3,1fr);gap:12px;margin:12px 0}.cards article{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:17px}.cards span,.cards small{display:block;color:var(--muted)}.cards strong{display:block;font-size:22px;margin:5px 0}.panel{background:var(--panel);border:1px solid var(--line);border-radius:6px;margin-top:12px;overflow:hidden}.panel-head{display:flex;align-items:center;justify-content:space-between;padding:14px 16px;border-bottom:1px solid var(--line)}.panel-head h3{margin:0;font-size:14px}.panel-head p{margin:4px 0 0;color:var(--muted)}.package-row,.table-row{display:grid;grid-template-columns:minmax(220px,1fr) 150px 90px 110px 150px;gap:12px;align-items:center;padding:12px 16px;border-bottom:1px solid var(--line)}.package-row:last-child,.table-row:last-child{border-bottom:0}.package-row:hover,.table-row:hover{background:var(--hover)}.pkg-name{font-weight:650;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.muted{color:var(--muted)}.pill{display:inline-flex;align-items:center;border:1px solid var(--line2);border-radius:99px;padding:3px 7px;font-size:10px;color:var(--muted);width:max-content}.download{color:var(--accent2);text-decoration:none;font-weight:650}.filters{display:grid;grid-template-columns:minmax(240px,1fr) 190px 170px;gap:8px;padding:12px;border-bottom:1px solid var(--line)}input,select{width:100%;height:34px;background:var(--field);color:var(--text);border:1px solid var(--line);border-radius:5px;padding:0 10px;outline:none}input:focus,select:focus{border-color:var(--accent)}.install-tabs{display:flex;gap:7px;flex-wrap:wrap}.install-tabs button.active{background:var(--accent);border-color:var(--accent);color:#102018;font-weight:700}.command-panel pre{margin:0;background:#0c0f0d;color:#e8efe9;padding:18px;overflow:auto;font:12px/1.6 ui-monospace,SFMono-Regular,Consolas,monospace}.note{margin-top:12px;border:1px solid var(--line);background:var(--panel2);padding:14px;border-radius:6px;color:var(--muted)}code{font-family:ui-monospace,SFMono-Regular,Consolas,monospace}.repo-grid{display:grid;grid-template-columns:repeat(2,1fr);gap:12px}.repo-card{background:var(--panel);bordLine truncated