feat: add auto-refresh and new-domain filter + deploy workflow
Some checks are pending
CI / lint (push) Waiting to run
Deploy / deploy (push) Waiting to run

This commit is contained in:
Adrian Miesikowski 2026-02-17 22:59:14 +01:00
parent c37ab7581a
commit c6a4bdfe30
3 changed files with 73 additions and 7 deletions

View File

@ -0,0 +1,17 @@
name: Deploy
on:
push:
branches: [ "main" ]
jobs:
deploy:
runs-on: self-hosted
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Deploy panel to server path
run: |
chmod +x refresh_and_publish.sh
./refresh_and_publish.sh

34
api.php
View File

@ -21,24 +21,46 @@ $action = $_GET['action'] ?? 'domains';
if ($action === 'domains') {
$q = trim($_GET['q'] ?? '');
$tld = trim($_GET['tld'] ?? '');
$onlyNew = (($_GET['only_new'] ?? '0') === '1');
$runId = $db->querySingle("SELECT value FROM metadata WHERE key='latest_run_id'");
$scannedAt = $db->querySingle("SELECT value FROM metadata WHERE key='latest_scanned_at'");
$sql = "SELECT domain, tld, score, status, keywords_json FROM domains WHERE run_id = :run";
if ($q !== '') $sql .= " AND domain LIKE :q";
if ($tld !== '') $sql .= " AND tld = :tld";
$sql .= " ORDER BY score DESC, domain ASC LIMIT 500";
$stPrev = $db->prepare("SELECT run_id FROM domains WHERE run_id != :run GROUP BY run_id ORDER BY MAX(scanned_at) DESC LIMIT 1");
$stPrev->bindValue(':run', $runId, SQLITE3_TEXT);
$prevRes = $stPrev->execute();
$prev = $prevRes ? $prevRes->fetchArray(SQLITE3_ASSOC) : null;
$prevRunId = $prev['run_id'] ?? '';
$sql = "SELECT d.domain, d.tld, d.score, d.status, d.keywords_json,
CASE
WHEN :prev = '' THEN 1
WHEN EXISTS (SELECT 1 FROM domains p WHERE p.run_id = :prev AND p.domain = d.domain) THEN 0
ELSE 1
END AS is_new
FROM domains d WHERE d.run_id = :run";
if ($q !== '') $sql .= " AND d.domain LIKE :q";
if ($tld !== '') $sql .= " AND d.tld = :tld";
if ($onlyNew) {
$sql .= " AND (CASE WHEN :prev = '' THEN 1 WHEN EXISTS (SELECT 1 FROM domains p WHERE p.run_id = :prev AND p.domain = d.domain) THEN 0 ELSE 1 END) = 1";
}
$sql .= " ORDER BY d.score DESC, d.domain ASC LIMIT 500";
$st = $db->prepare($sql);
$st->bindValue(':run', $runId, SQLITE3_TEXT);
$st->bindValue(':prev', $prevRunId, SQLITE3_TEXT);
if ($q !== '') $st->bindValue(':q', '%' . $q . '%', SQLITE3_TEXT);
if ($tld !== '') $st->bindValue(':tld', $tld, SQLITE3_TEXT);
$res = $st->execute();
$rows = [];
$newCount = 0;
while ($r = $res->fetchArray(SQLITE3_ASSOC)) {
$r['keywords'] = json_decode($r['keywords_json'] ?: '[]', true) ?: [];
$r['is_new'] = intval($r['is_new']) === 1;
if ($r['is_new']) $newCount++;
unset($r['keywords_json']);
$rows[] = $r;
}
@ -47,8 +69,10 @@ if ($action === 'domains') {
'ok' => true,
'meta' => [
'runId' => $runId,
'prevRunId' => $prevRunId,
'scannedAt' => $scannedAt,
'count' => count($rows)
'count' => count($rows),
'newCount' => $newCount
],
'domains' => $rows,
], JSON_UNESCAPED_UNICODE);

View File

@ -20,6 +20,8 @@
.pill{display:inline-flex;align-items:center;gap:6px;background:#1f2b56;border:1px solid #3a4a87;border-radius:999px;padding:4px 8px;margin:4px 6px 4px 0;font-size:12px}
.pill input{width:78px;padding:4px 6px}
.ok{color:#63db8f}.bad{color:#ff8b8b}
.chip{display:inline-block;padding:2px 8px;border-radius:999px;font-size:11px;background:#264a32;color:#9df5bc;border:1px solid #3f8b5b}
.small{font-size:12px}
@media (max-width: 980px){.grid{grid-template-columns:1fr}}
@media (max-width: 700px){
body{padding:10px}
@ -42,6 +44,8 @@
<label>TLD
<select id="tld"><option value="">Wszystkie</option><option>pl</option><option>com</option><option>ai</option></select>
</label>
<label class="small"><input type="checkbox" id="onlyNew" /> Tylko nowe domeny</label>
<label class="small"><input type="checkbox" id="autoRefresh" checked /> Auto-refresh 60s</label>
<button id="reload">Odśwież</button>
</div>
<p class="muted" id="meta"></p>
@ -112,13 +116,15 @@ function best2y(regs, tld){
async function loadDomains(){
const q = document.getElementById('q').value.trim();
const tld = document.getElementById('tld').value;
const onlyNew = document.getElementById('onlyNew').checked;
const u = new URL('./api.php', location.href);
u.searchParams.set('action','domains');
if(q) u.searchParams.set('q', q);
if(tld) u.searchParams.set('tld', tld);
if(onlyNew) u.searchParams.set('only_new', '1');
const d = await (await fetch(u)).json();
domains = d.domains || [];
document.getElementById('meta').textContent = `runId: ${d.meta?.runId||'—'} | scannedAt: ${d.meta?.scannedAt||'—'} | widoczne: ${domains.length}`;
document.getElementById('meta').textContent = `runId: ${d.meta?.runId||'—'} | prev: ${d.meta?.prevRunId||'—'} | scannedAt: ${d.meta?.scannedAt||'—'} | nowe: ${d.meta?.newCount||0} | widoczne: ${domains.length}`;
}
async function loadPrices(){
@ -176,7 +182,7 @@ function renderRows(){
const renew = best(registrars,d.tld,'renew');
const two = best2y(registrars,d.tld);
const tr = document.createElement('tr');
tr.innerHTML = `<td><b>${d.domain}</b></td><td>${d.score}</td>
tr.innerHTML = `<td><b>${d.domain}</b> ${d.is_new ? `<span class='chip'>NEW</span>` : ''}</td><td>${d.score}</td>
<td>${buy?`<a href='${buy.url}' target='_blank'>${buy.name}</a> (${money(buy.value)})`:'—'}</td>
<td>${renew?`<a href='${renew.url}' target='_blank'>${renew.name}</a> (${money(renew.value)})`:'—'}</td>
<td>${two?`<a href='${two.url}' target='_blank'>${two.name}</a> (${money(two.value)})`:'—'}</td>`;
@ -192,13 +198,32 @@ async function refreshAll(){
}
let qTimer;
let autoTimer;
function setupAutoRefresh(){
if(autoTimer) clearInterval(autoTimer);
const enabled = document.getElementById('autoRefresh').checked;
if(enabled){
autoTimer = setInterval(async()=>{
await loadDomains();
await loadPrices();
renderPricing();
renderRows();
}, 60000);
}
}
document.getElementById('reload').addEventListener('click', refreshAll);
document.getElementById('q').addEventListener('input', async()=>{
clearTimeout(qTimer);
qTimer = setTimeout(async()=>{ await loadDomains(); renderRows(); }, 250);
});
document.getElementById('tld').addEventListener('change', async()=>{await loadDomains(); renderRows();});
document.getElementById('onlyNew').addEventListener('change', async()=>{await loadDomains(); renderRows();});
document.getElementById('autoRefresh').addEventListener('change', setupAutoRefresh);
refreshAll();
setupAutoRefresh();
</script>
</body>
</html>