v2
This commit is contained in:
1 parent
0efaae1715
commit
c1a2a8cc30
13 files changed
+180
-137
No files matched your search
+7
-7
@@ -18,9 +18,9 @@ REPOSITORY = Path(os.environ.get("REPOSITORY_ROOT", "/srv/repository"))
|
||||
WEB = Path(__file__).resolve().parent / "web"
|
||||
PORT = int(os.environ.get("DASHBOARD_PORT", "8000"))
|
||||
CACHE_SECONDS = max(1, int(os.environ.get("DASHBOARD_CACHE_SECONDS", "15")))
|
||||
REPOSITORY_URL = os.environ.get("REPOSITORY_URL", "http://localhost:18765").rstrip("/")
|
||||
SITES = ("linux", "suse")
|
||||
META_PATHS = {"linux": Path("LINUX-BUILD-INFO.txt"), "suse": Path("SUSE-BUILD-INFO.txt")}
|
||||
REPOSITORY_URL = os.environ.get("REPOSITORY_URL", "http://localhost:18965").rstrip("/")
|
||||
SITES = ("debian", "rhel", "suse")
|
||||
META_PATHS = {"debian": Path("DEBIAN-BUILD-INFO.txt"), "rhel": Path("RHEL-BUILD-INFO.txt"), "suse": Path("SUSE-BUILD-INFO.txt")}
|
||||
DEB_FILENAME = re.compile(r"^(?P<name>.+?)_(?P<version>[^_]+)_(?P<arch>[^_]+)\.deb$")
|
||||
RPM_FILENAME = re.compile(r"^(?P<name>.+)-(?P<version>\d[^-]*)-(?P<release>[^/]+)\.(?P<arch>x86_64|amd64|aarch64|noarch|arm64)\.rpm$")
|
||||
|
||||
@@ -88,8 +88,8 @@ def generate_catalog(repository: Path = REPOSITORY) -> dict[str, object]:
|
||||
packages: list[dict[str, object]] = []
|
||||
if current:
|
||||
scans = [
|
||||
("linux", "DEB", "deb", current / "apt" / "pool", "*.deb"),
|
||||
("linux", "RPM", "rpm", current / "rpm" / "x86_64", "*.rpm"),
|
||||
("debian", "DEB", "deb", current / "apt" / "pool", "*.deb"),
|
||||
("rhel", "RPM", "rpm", current / "rpm" / "x86_64", "*.rpm"),
|
||||
("suse", "RPM", "rpm", current / "suse" / "x86_64", "*.rpm"),
|
||||
]
|
||||
for channel, label, kind, directory, pattern in scans:
|
||||
@@ -161,9 +161,9 @@ class Handler(BaseHTTPRequestHandler):
|
||||
self.send_bytes(json.dumps(CACHE.get(), ensure_ascii=False, separators=(",", ":")).encode(), "application/json; charset=utf-8", "no-store")
|
||||
elif path in ("/", "/index.html"):
|
||||
self.send_bytes((WEB / "index.html").read_bytes(), "text/html; charset=utf-8", "no-store", True)
|
||||
elif path in ("/ui/style.css", "/ui/app.js", "/ui/favicon.svg"):
|
||||
elif path in ("/ui/style.css", "/ui/app.js"):
|
||||
name = path.rsplit("/", 1)[-1]
|
||||
mime = {"style.css": "text/css", "app.js": "application/javascript", "favicon.svg": "image/svg+xml"}[name]
|
||||
mime = {"style.css": "text/css", "app.js": "application/javascript"}[name]
|
||||
self.send_bytes((WEB / name).read_bytes(), f"{mime}; charset=utf-8", "public, max-age=600")
|
||||
else:
|
||||
self.send_error(HTTPStatus.NOT_FOUND)
|
||||
|
||||
+10
-9
@@ -2,24 +2,25 @@
|
||||
const $ = (id) => document.getElementById(id);
|
||||
const esc = (value) => String(value ?? '').replace(/[&<>"']/g, (c) => ({'&':'&','<':'<','>':'>','"':'"',"'":'''}[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 titles = {overview:'Overview',packages:'Packages',install:'Installation',repositories:'Repositories'};
|
||||
const channelNames = {debian:'Debian / Ubuntu',rhel:'RHEL / AlmaLinux / Rocky Linux',suse:'SUSE / openSUSE'};
|
||||
const fmtDate = (value) => value ? new Intl.DateTimeFormat('en-GB',{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 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(channelNames[p.channel]||p.channel)}</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)}">Download</a></div>`}
|
||||
function renderOverview(){const d=state.data;if(!d)return;const debian=d.builds.debian||{},rhel=d.builds.rhel||{},suse=d.builds.suse||{};const latest=[debian,rhel,suse].find(x=>x.app_version)||{};$('latest-version').textContent=latest.app_version||'-';$('latest-build').textContent=fmtDate(latest.built_at);$('debian-status').textContent=debian.published?'READY':'NOT BUILT';$('rhel-status').textContent=rhel.published?'READY':'NOT BUILT';$('suse-status').textContent=suse.published?'READY':'NOT BUILT';$('recent-packages').innerHTML=d.packages.slice(0,6).map(packageRow).join('')||'<div class="empty">No packages have been published yet.</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(channelNames[p.channel]||p.channel)}</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)}">Download</a></div>`).join('')||'<div class="empty">No matching packages.</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 / AlmaLinux / Rocky Linux',desc:'Install from the signed DNF repository.',code:`sudo rpm --import ${url}/repo-key.gpg\n\nsudo tee /etc/yum.repos.d/rustbox.repo >/dev/null <<'REPO'\n[rustbox]\nname=RustBox Package 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:'Install from the signed Zypper repository.',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:'Install from the signed APT repository.',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 renderRepositories(){const d=state.data;if(!d)return;const u=d.repository_url,dist=encodeURIComponent(d.apt_distribution);$('repo-cards').innerHTML=repoCard('Debian / Ubuntu',`${u}/apt`,[['GPG public key',`${u}/repo-key.gpg`],['APT InRelease',`${u}/apt/dists/${dist}/InRelease`],['Build info',`${u}/DEBIAN-BUILD-INFO.txt`]])+repoCard('RHEL / AlmaLinux / Rocky Linux',`${u}/rpm/x86_64`,[['GPG public key',`${u}/repo-key.gpg`],['RPM repomd.xml',`${u}/rpm/x86_64/repodata/repomd.xml`],['Metadata signature',`${u}/rpm/x86_64/repodata/repomd.xml.asc`],['Build info',`${u}/RHEL-BUILD-INFO.txt`]])+repoCard('SUSE / openSUSE',`${u}/suse/x86_64`,[['GPG public key',`${u}/repo-key.gpg`],['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=['debian','rhel','suse'].map(ch=>{const b=d.builds[ch]||{};return `<div class="meta-block"><h3>${esc(channelNames[ch])}</h3>${[['Version',b.app_version],['Package release',b.package_release],['Build time',fmtDate(b.built_at)],['Commit',b.source_commit?b.source_commit.slice(0,12):'-'],['Packaging revision',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}}
|
||||
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=`Last refresh: ${fmtDate(state.data.generated_at)}`;$('status-text').textContent=state.data.ready?'Repository ready':'Waiting for first build'}catch(e){$('status-text').textContent='Repository unavailable';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'));
|
||||
$('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('Copied')}catch{toast('Copy failed')}});$('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);
|
||||
})();
|
||||
+21
-22
@@ -1,60 +1,59 @@
|
||||
<!doctype html>
|
||||
<html lang="pl" data-theme="dark">
|
||||
<html lang="en" 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">
|
||||
<title>RustBox Package Repository</title>
|
||||
<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>
|
||||
<div class="brand"><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>
|
||||
<button class="nav active" data-view="overview">Overview</button>
|
||||
<button class="nav" data-view="packages">Packages</button>
|
||||
<button class="nav" data-view="install">Installation</button>
|
||||
<button class="nav" data-view="repositories">Repositories</button>
|
||||
</nav>
|
||||
<div class="sidebar-foot"><span id="status-dot" class="status-dot"></span><span id="status-text">Ładowanie…</span></div>
|
||||
<div class="sidebar-foot"><span id="status-text">Loading...</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>
|
||||
<div><h1 id="view-title">Overview</h1><p id="last-sync">-</p></div>
|
||||
<div class="toolbar"><button id="theme-toggle" type="button">Theme</button><button id="refresh" type="button">Refresh</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><span class="eyebrow">RUSTBOX PACKAGE REPOSITORY</span><h2>Linux packages</h2><p>Signed packages and repository metadata built directly from RustBox source code.</p></div>
|
||||
<div class="version-box"><span>LATEST VERSION</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>
|
||||
<article><span>Debian / Ubuntu</span><strong id="debian-status">-</strong><small>APT / DEB</small></article>
|
||||
<article><span>RHEL / AlmaLinux / Rocky Linux</span><strong id="rhel-status">-</strong><small>DNF / RPM</small></article>
|
||||
<article><span>SUSE / openSUSE</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>
|
||||
<div class="panel"><div class="panel-head"><h3>Recent packages</h3><button data-go="packages" type="button">View all</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 class="filters"><input id="search" type="search" placeholder="Search package or version"><select id="channel"><option value="all">All distributions</option><option value="debian">Debian / Ubuntu</option><option value="rhel">RHEL / AlmaLinux / Rocky Linux</option><option value="suse">SUSE / openSUSE</option></select><select id="format"><option value="all">All formats</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>
|
||||
<div class="install-tabs"><button class="active" data-install="apt" type="button">Debian / Ubuntu</button><button data-install="dnf" type="button">RHEL / AlmaLinux / Rocky Linux</button><button data-install="suse" type="button">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">Install from the signed APT repository.</p></div><button id="copy-install" type="button">Copy</button></div><pre><code id="install-command"></code></pre></div>
|
||||
<div class="note">After installation, configuration is stored in <code>/etc/rustbox/rustbox.toml</code>, application data in <code>/var/lib/rustbox</code>, and logs in <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>
|
||||
<div class="panel"><div class="panel-head"><h3>Build metadata</h3></div><div id="metadata" class="metadata"></div></div>
|
||||
</section>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@@ -1,3 +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
|
||||
*{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;align-items:center;padding:7px 8px 24px}.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}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(3,1fr);gap:12px}.repo-card{background:var(--panel);border:1px solid var(--line);border-radius:6px;padding:16px}.repo-card h3{margin:0 0 8px}.repo-card>a{color:var(--accent2);word-break:break-all}.repo-links{display:grid;margin-top:12px;border-top:1px solid var(--line)}.repo-links a{padding:9px 0;color:var(--muted);text-decoration:none;border-bottom:1px solid var(--line)}.metadata{display:grid;grid-Line truncated
|
||||
Reference in new issue
Block a user