vuetify-rework #2

Merged
anonfunc merged 18 commits from vuetify-rework into main 2024-01-24 16:11:55 +01:00
2 changed files with 1 additions and 431 deletions
Showing only changes of commit 56208dd32c - Show all commits

View File

@@ -1,5 +1,5 @@
# ALHP Web
This project serves as backend for the ALHP API.
This project serves as backend for the ALHP API.
vikingowl marked this conversation as resolved Outdated

Please remove the tailing whitespace. Or better, drop the whole change to this file.

Please remove the tailing whitespace. Or better, drop the whole change to this file.
Docs: https://api.alhp.dev/

View File

@@ -1,430 +0,0 @@
<!DOCTYPE html>
<html lang="en" data-bs-theme="dark">
<head>
<meta charset="UTF-8">
<meta content="width=device-width, initial-scale=1" name="viewport">
<title>ALHP Status</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-T3c6CoIi6uLrA9TneNEoa7RxnatzjcDSCmG1MXxSR1GAsXEV/Dwwykc2MPK8M2HN" crossorigin="anonymous">
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/fork-awesome@1.2.0/css/fork-awesome.min.css" integrity="sha256-XoaMnoYC5TH6/+ihMEnospgm0J1PM/nioxbOUdnM8HY=" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/Scalesoft/s-pagination@3.0.1/dist/pagination.css" crossorigin="anonymous">
<style>
/* This style fixes the position of the pagination */
.pagination-container ul {
margin-top: 0 !important;
}
</style>
</head>
<body style="background-color: #111217">
<nav class="navbar navbar-expand-lg sticky-top navbar-light bg-primary">
<div class="container">
<div class="d-flex justify-content-start">
<span class="navbar-brand align-middle">ALHP Status</span>
<span class="navbar-text">
<a class="align-middle" href="https://somegit.dev/ALHP/ALHP.GO" target="_blank">
<i class="fa fa-gitea fs-4"></i>
</a>
</span>
</div>
</div>
</nav>
<div class="container">
<div class="pt-4 pb-4">
<h4>Buildserver Stats</h4>
<iframe allowtransparency="true" class="container-fluid rounded-1 overflow-hidden" height="420px" src="https://stats.itsh.dev/public-dashboards/0fb04abb0c5e4b7390cf26a98e6dead1"></iframe>
</div>
<h3>Packages</h3>
<div class="d-flex justify-space-between mt-4" style="height: 37px">
<div class="d-flex gap-3 w-50">
<input type="text" class="form-control" id="input-pkgbase" placeholder="Search Pkgbase">
<select class="form-select" id="select-repository" aria-label="Repository select">
<option value="any" selected>Repository (any)</option>
<option value="core-x86-64-v2">core-x86-64-v2</option>
<option value="extra-x86-64-v2">extra-x86-64-v2</option>
<option value="multilib-x86-64-v2">multilib-x86-64-v2</option>
<option value="core-x86-64-v3">core-x86-64-v3</option>
<option value="extra-x86-64-v3">extra-x86-64-v3</option>
<option value="multilib-x86-64-v3">multilib-x86-64-v3</option>
<option value="core-x86-64-v4">core-x86-64-v4</option>
<option value="extra-x86-64-v4">extra-x86-64-v4</option>
<option value="multilib-x86-64-v4">multilib-x86-64-v4</option>
</select>
<select class="form-select" id="select-status" aria-label="Status select">
<option value="any" selected>Status (any)</option>
<option value="latest">Latest</option>
<option value="build">Built</option>
<option value="failed">Failed</option>
<option value="skipped">Skipped</option>
<option value="delayed">Delayed</option>
<option value="queued">Queued</option>
<option value="building">Building</option>
<option value="signing">Signing</option>
<option value="unknown">Unknown</option>
</select>
</div>
<div id="pagination" style="line-height: 27px; margin-left: auto"></div>
</div>
<table class="table mt-2" style="margin-bottom: 100px">
<thead>
<tr>
<th scope="col">Repository</th>
<th scope="col">Pkgbase</th>
<th scope="col">Status</th>
<th scope="col">Reason</th>
<th scope="col" title="link time optimization&#10;does not guarantee that package is actually built with LTO">
LTO
</th>
<th scope="col" title="Debug-symbols available via debuginfod">DS
</th>
<th scope="col">Archlinux Version</th>
<th scope="col">ALHP Version</th>
<th class="text-end" scope="col">Info</th>
</tr>
</thead>
<tbody id="main-tbody"></tbody>
</table>
</div>
<footer class="text-center text-lg-start fixed-bottom" style="background-color: #000000">
<div class="p-2 text-center ">
<span id="stats-latest"></span><span style="color: #084f46"> latest</span>
<span id="stats-queued"></span><span style="color: #5d2f03"> queued</span>
<span id="stats-skipped"></span><span style="color: #4c4c4c"> skipped</span>
<span id="stats-failed"></span><span style="color: #4f140f"> failed</span> &#8214;
<span class="fw-bold">LTO: </span> <span id="stats-lto"></span><span style="color: #084f46"> enabled</span>
<span id="stats-lto-disabled"></span><span style="color: #4f140f"> disabled</span>
<span id="stats-lto-unknown"></span><span style="color: #646464"> unknown</span>
</div>
</footer>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-C6RzsynM9kWDrMNeT87bh95OGNyZPhcTNXj1NW7RuBCsyN/o0jlpcV8Qyq46cDfL" crossorigin="anonymous"></script>
<script src="https://cdn.jsdelivr.net/gh/Scalesoft/s-pagination@3.0.1/dist/pagination.js"></script>
<script>
const url = new URL(window.location)
const pagination = new Pagination({
container: document.getElementById('pagination'),
pageClickCallback: function (pageNumber) {
searchPackages((pageNumber - 1) * 50)
}
})
const ref = {
totalItems: 0
}
const handler = {
get(target, prop, receiver) {
if (prop === 'totalItems') {
return target.totalItems
}
return Reflect.get(...arguments)
},
set(obj, prop, value) {
if (prop === 'totalItems') {
obj[prop] = value
}
pagination.make(value, 50)
return true
}
}
const pages = new Proxy(ref, handler)
const inputPkgbase = document.getElementById('input-pkgbase')
const selectRepo = document.getElementById('select-repository')
const selectStatus = document.getElementById('select-status')
function getStats() {
fetch('https://api.alhp.dev/stats')
.then(response => {
if (!response.ok) throw new Error(response.statusText)
return response.json()
}).then(stats => {
document.getElementById('stats-latest').innerText = stats.latest
document.getElementById('stats-queued').innerText = stats.queued
document.getElementById('stats-skipped').innerText = stats.skipped
document.getElementById('stats-failed').innerText = stats.failed
document.getElementById('stats-lto').innerText = stats.lto.enabled
document.getElementById('stats-lto-disabled').innerText = stats.lto.disabled
document.getElementById('stats-lto-unknown').innerText = stats.lto.unknown
}).catch(error => {
console.error(error)
}).finally(() => {})
}
function searchPackages(offset) {
const params = new URLSearchParams({
limit: 50,
offset
})
if (inputPkgbase.value) params.append('pkgbase', inputPkgbase.value.toLowerCase())
if (selectRepo.value !== 'any') params.append('repo', selectRepo.value)
if (selectStatus.value !== 'any') params.append('status', selectStatus.value)
fetch('https://api.alhp.dev/packages?' + params, {
method: 'GET',
}).then(response => {
if (response.ok) return response.json()
if (response.status === 404) {
const tableBody = document.getElementById('main-tbody')
const ntb = document.createElement('tbody')
ntb.id = 'main-tbody'
const ntr = document.createElement('tr')
const ntd = document.createElement('td')
ntd.innerText = 'No Packages found'
ntb.appendChild(ntr.appendChild(ntd))
tableBody.replaceWith(ntb)
pages.totalItems = 0
} else {
throw new Error(response.statusText)
}
}).then(packages => {
if (!packages) return
if (pages.totalItems !== packages.total) {
pages.totalItems = packages.total
}
const tableBody = document.getElementById('main-tbody')
const ntb = document.createElement('tbody')
ntb.id = 'main-tbody'
for (const pkg of packages.packages) {
const rowColor = getStatus(pkg.status)
const ntr = document.createElement('tr')
ntr.id = pkg.pkgbase + '-' + pkg.repo
// Create table cells
const Repository = document.createElement('td')
Repository.style.backgroundColor = rowColor
const repo = pkg.repo.split('-')
Repository.innerHTML = `<span class="fw-bold">${repo[0]}</span> <span style="width: 1.25rem; height: 1.25rem; border-radius: 5px; background-color: ${ getVersionColor(repo[repo.length - 1]) }; padding: 0 10px">${repo[repo.length - 1]}</span>`
ntr.appendChild(Repository)
const PkgBase = document.createElement('td')
PkgBase.style.backgroundColor = rowColor
PkgBase.innerText = pkg.pkgbase
ntr.appendChild(PkgBase)
const Status = document.createElement('td')
Status.style.backgroundColor = rowColor
Status.innerText = pkg.status.toLocaleUpperCase()
ntr.appendChild(Status)
const Reason = document.createElement('td')
Reason.style.backgroundColor = rowColor
Reason.innerText = pkg.skip_reason || ''
ntr.appendChild(Reason)
const LTO = document.createElement('td')
LTO.style.backgroundColor = rowColor
const ltoObj = getLto(pkg.lto)
LTO.innerHTML = `<i class="${ltoObj.class}" title="${ltoObj.title}"></i>`
ntr.appendChild(LTO)
const DS = document.createElement('td')
DS.style.backgroundColor = rowColor
const dsObj = getDs(pkg.debug_symbols)
DS.innerHTML = `<i class="${dsObj.class}" title="${dsObj.title}"></i>`
ntr.appendChild(DS)
const ArchLinuxVersion = document.createElement('td')
ArchLinuxVersion.style.backgroundColor = rowColor
ArchLinuxVersion.innerText = pkg.arch_version
ntr.appendChild(ArchLinuxVersion)
const AlhpVersion = document.createElement('td')
AlhpVersion.style.backgroundColor = rowColor
AlhpVersion.innerText = pkg.repo_version
ntr.appendChild(AlhpVersion)
const Info = document.createElement('td')
Info.style.backgroundColor = rowColor
const logFile = pkg.status === 'failed' ? '<a href="https://alhp.dev/logs/' + pkg.repo.slice(pkg.repo.indexOf('-') + 1) + '/' + pkg.pkgbase + '.log" class="text-decoration-none text-white-50" target="_blank"><i class="fa fa-file-text fa-lg"></i></a>' : ''
const aw = '<a href="https://archlinux.org/packages/?q=' + pkg.pkgbase + '" target="_blank" title="ArchWeb" class="text-decoration-none text-white-50 fw-bold">AW</a>'
let info = ''
if (pkg.build_date && pkg.peak_mem) {
info = '<i class="fa fa-info-circle fa-lg text-white-50" data-bs-html="true" data-bs-placement="bottom" data-bs-toggle="tooltip" title="' + `${pkg.build_date ? 'Built on ' + pkg.build_date + '&#10;' : ''}${pkg.peak_mem ? 'Peak-Memory: ' + pkg.peak_mem : ''}` + '"></i>'
}
Info.classList.add('text-end')
Info.innerHTML = `${logFile} ${aw} ${info}`
ntr.appendChild(Info)
// Append Table Row to Table Body
ntb.appendChild(ntr)
}
tableBody.replaceWith(ntb)
}).catch(error => {
console.error(error)
})
}
const getVersionColor = (version) => {
let color = 'grey'
switch (version) {
case 'v2':
color = '#3498db'
break
case 'v3':
color = '#f39c12'
break
case 'v4':
color = '#2ecc71'
break
}
return color
}
const getStatus = (status) => {
let color = ''
switch (status) {
case 'skipped':
color = '#373737'
break
case 'queued':
color = '#5d2f03'
break
case 'latest':
color = ''
break
case 'failed':
color = '#4f140f'
break
case 'signing':
color = '#093372'
break
case 'building':
color ='#084f46'
break
case 'unknown':
color ='#191919'
break
}
return color
}
const getLto = (lto) => {
let icon = {
title: '',
class: ''
}
switch (lto) {
case 'enabled':
icon = {
title: 'built with LTO',
class: 'fa fa-check fa-lg text-success'
}
break
case 'unknown':
icon = {
title: 'not built with LTO yet',
class: 'fa fa-hourglass-o fa-lg text-black-50'
}
break
case 'disabled':
icon = {
title: 'LTO explicitly disabled',
class: 'fa fa-times fa-lg text-danger'
}
break
case 'auto_disabled':
icon = {
title: 'LTO automatically disabled',
class: 'fa fa-times-circle-o fa-lg text-warning'
}
break
}
return icon
}
const getDs = (lto) => {
let icon = {
title: '',
class: ''
}
switch (lto) {
case 'available':
icon = {
title: 'Debug symbols available',
class: 'fa fa-check fa-lg text-success'
}
break
case 'unknown':
icon = {
title: 'Not built yet',
class: 'fa fa-hourglass-o fa-lg text-black-50'
}
break
case 'not_available':
icon = {
title: 'Not built with debug symbols',
class: 'fa fa-times fa-lg text-danger'
}
break
}
return icon
}
window.addEventListener('load', () => {
inputPkgbase.value = url.searchParams.has('pkgbase') ? url.searchParams.get('pkgbase').toLowerCase() : ''
selectRepo.value = url.searchParams.has('repo') ? url.searchParams.get('repo').toLowerCase() : 'any'
selectStatus.value = url.searchParams.has('status') ? url.searchParams.get('status').toLowerCase() : 'any'
getStats()
searchPackages(0)
})
let delayTimer = null
inputPkgbase.addEventListener('input', () => {
if (delayTimer) clearTimeout(delayTimer);
delayTimer = setTimeout(function() {
if (inputPkgbase.value) {
url.searchParams.set('pkgbase', inputPkgbase.value.toLowerCase())
} else {
url.searchParams.delete('pkgbase')
}
window.history.pushState(null, '', url.toString())
searchPackages(0)
}, 250);
});
selectRepo.addEventListener('change', () => {
if (selectRepo.value) {
if (selectRepo.value === 'any') {
url.searchParams.delete('repo')
} else {
url.searchParams.set('repo', selectRepo.value)
}
window.history.pushState(null, '', url.toString())
}
searchPackages(0)
})
selectStatus.addEventListener('change', () => {
if (selectStatus.value) {
if (selectStatus.value === 'any') {
url.searchParams.delete('status')
} else {
url.searchParams.set('status', selectStatus.value)
}
window.history.pushState(null, '', url.toString())
}
searchPackages(0)
})
</script>
</body>
</html>