zrcadlo https://github.com/atscan/atscan
table sorting
This commit is contained in:
rodič
d667d63891
revize
5b5c71b449
|
@ -90,7 +90,26 @@ router
|
|||
.get("/dids", async (ctx) => {
|
||||
const out = [];
|
||||
const query = { $and: [{}] };
|
||||
let sort = { lastMod: -1 };
|
||||
|
||||
const availableSort = {
|
||||
did: { key: "did" },
|
||||
lastMod: { key: "lastMod" },
|
||||
pds: { key: "pds" },
|
||||
};
|
||||
|
||||
let inputSort = ctx.request.url.searchParams.get("sort");
|
||||
let inputSortDirection = 1;
|
||||
if (inputSort && inputSort.startsWith("!")) {
|
||||
inputSortDirection = -1;
|
||||
inputSort.replace(/^!/, "");
|
||||
}
|
||||
let inputSortConfig = null;
|
||||
if (inputSort) {
|
||||
inputSortConfig = availableSort[inputSort];
|
||||
}
|
||||
let sort = inputSortConfig
|
||||
? { [inputSortConfig.key]: inputSortDirection }
|
||||
: { lastMod: -1 };
|
||||
|
||||
let q = ctx.request.url.searchParams.get("q")?.replace(/^@/, "");
|
||||
if (q) {
|
||||
|
|
|
@ -39,7 +39,7 @@ async function crawl(ats) {
|
|||
const url = `${pds}/xrpc/com.atproto.sync.getRepo?did=${did}`;
|
||||
let repoRes;
|
||||
try {
|
||||
[repoRes] = await timeout(5000, fetch(url));
|
||||
[repoRes] = await timeout(20 * 1000, fetch(url));
|
||||
} catch (e) {
|
||||
repoRes = { ok: false };
|
||||
console.error(e);
|
||||
|
|
|
@ -2,8 +2,8 @@
|
|||
import Breadcrumb from './Breadcrumb.svelte';
|
||||
|
||||
export let title;
|
||||
export let breadcrumb;
|
||||
export let data;
|
||||
export let breadcrumb = null;
|
||||
export let noHeader = false;
|
||||
</script>
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Table from '$lib/components/Table.svelte';
|
||||
import { tableMapperValues, tableSourceValues } from '@skeletonlabs/skeleton';
|
||||
import {
|
||||
|
@ -11,6 +12,12 @@
|
|||
export let sourceData;
|
||||
export let data;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function onHeadSelected(event) {
|
||||
dispatch('headSelected', event.detail);
|
||||
}
|
||||
|
||||
function tableMap({ val, key, row }) {
|
||||
if (key === 'srcHost') {
|
||||
val = row.pds
|
||||
|
@ -33,7 +40,10 @@
|
|||
)}</span></a></div>`;
|
||||
const asa = row.revs[row.revs.length - 1].operation?.alsoKnownAs;
|
||||
const handles = asa
|
||||
? asa.filter((h) => !h.match(/at:\/\/data:x\//)).map((h) => h.replace(/^at:\/\//, ''))
|
||||
? asa
|
||||
.filter((h) => h.match(/^at:\/\//))
|
||||
.map((h) => h.replace(/^at:\/\//, ''))
|
||||
.filter((h) => h.match(/^[a-z0-9\.\-]+$/))
|
||||
: [];
|
||||
val += ` <div class="mt-1.5">`;
|
||||
if (fed) {
|
||||
|
@ -41,7 +51,12 @@
|
|||
}
|
||||
val +=
|
||||
` <span>${handles
|
||||
.map((h) => `<a href="${link}" target="_blank" class="anchor">@${h}</a>`)
|
||||
.map(
|
||||
(h) =>
|
||||
`<a href="${link}" target="_blank" class="anchor">@${
|
||||
h.length > 40 ? h.substring(0, 40) + '..' : h
|
||||
}</a>`
|
||||
)
|
||||
.join(', ')} ` +
|
||||
(row.revs.length > 1 ? `(#${row.revs.length - 1})` : '') +
|
||||
`</span>`;
|
||||
|
@ -69,10 +84,15 @@
|
|||
}
|
||||
$: tableSimple = {
|
||||
// A list of heading labels.
|
||||
head: ['', 'DID', 'PDS (PLC)', 'Updated'],
|
||||
head: ['', ['DID', 'did'], ['PDS (PLC)', 'pds'], ['Updated', 'lastMod']],
|
||||
body: customTableMapper(sourceData || [], ['img', 'did', 'srcHost', 'lastMod'], tableMap),
|
||||
meta: customTableMapper(sourceData || [], ['did_raw', 'url'], tableMap)
|
||||
};
|
||||
</script>
|
||||
|
||||
<Table source={tableSimple} />
|
||||
<Table
|
||||
source={tableSimple}
|
||||
currentSort={data.sort}
|
||||
defaultSort=""
|
||||
on:headSelected={(e) => onHeadSelected(e)}
|
||||
/>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<script>
|
||||
import { createEventDispatcher } from 'svelte';
|
||||
import Table from '$lib/components/Table.svelte';
|
||||
import { tableMapperValues, tableSourceValues } from '@skeletonlabs/skeleton';
|
||||
import {
|
||||
|
@ -11,6 +12,12 @@
|
|||
export let sourceData;
|
||||
export let data;
|
||||
|
||||
const dispatch = createEventDispatcher();
|
||||
|
||||
function onHeadSelected(event) {
|
||||
dispatch('headSelected', event.detail);
|
||||
}
|
||||
|
||||
function tableMap({ val, key, row }) {
|
||||
if (key === 'plcs' && val) {
|
||||
val = val
|
||||
|
@ -93,14 +100,14 @@
|
|||
}
|
||||
$: tableSimple = {
|
||||
head: [
|
||||
'Federation',
|
||||
['Federation', 'fed'],
|
||||
'',
|
||||
'Host',
|
||||
'DIDs',
|
||||
'Location',
|
||||
'PLCs (User Domains)',
|
||||
'Resp. time',
|
||||
'Last Online'
|
||||
['Host', 'host'],
|
||||
['DIDs', 'didsCount'],
|
||||
['Location', 'country'],
|
||||
['PLCs (User Domains)', 'plcs'],
|
||||
['Resp. time', 'ms'],
|
||||
['Last Online', 'lastOnline']
|
||||
],
|
||||
body: customTableMapper(
|
||||
sourceData,
|
||||
|
@ -111,4 +118,9 @@
|
|||
};
|
||||
</script>
|
||||
|
||||
<Table source={tableSimple} />
|
||||
<Table
|
||||
source={tableSimple}
|
||||
currentSort={data.sort}
|
||||
defaultSort=""
|
||||
on:headSelected={(e) => onHeadSelected(e)}
|
||||
/>
|
||||
|
|
|
@ -11,6 +11,14 @@
|
|||
* @type {TableSource}
|
||||
*/
|
||||
export let source;
|
||||
|
||||
export let currentSort = null;
|
||||
export let defaultSort;
|
||||
|
||||
if (!currentSort) {
|
||||
currentSort = defaultSort;
|
||||
}
|
||||
|
||||
/** Enables row hover style and `on:selected` event when rows are clicked. */
|
||||
export let interactive = false;
|
||||
export let interactiveOnlyHover = true;
|
||||
|
@ -55,6 +63,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
function onHeaderClick(event, meta) {
|
||||
dispatch('headSelected', meta);
|
||||
}
|
||||
|
||||
// Row Keydown Handler
|
||||
function onRowKeydown(event, rowIndex) {
|
||||
if (['Enter', 'Space'].includes(event.code)) onRowClick(event, rowIndex);
|
||||
|
@ -78,8 +90,18 @@
|
|||
<!-- Head -->
|
||||
<thead class="table-head {regionHead}">
|
||||
<tr>
|
||||
{#each source.head as heading }
|
||||
<th class="{regionHeadCell}">{@html heading}</th>
|
||||
{#each source.head as heading}
|
||||
<th class="{regionHeadCell} cursor-pointer hover:underline text-sm"
|
||||
on:click={(e) => { onHeaderClick(e, Array.isArray(heading) ? heading[1] : heading) }}>
|
||||
{@html Array.isArray(heading) ? heading[0] : heading}
|
||||
{#if Array.isArray(heading) && (currentSort?.replace(/^!/, '') === heading[1] || '!'+currentSort === heading[1])}
|
||||
{#if currentSort?.startsWith('!')}
|
||||
↑
|
||||
{:else}
|
||||
↓
|
||||
{/if}
|
||||
{/if}
|
||||
</th>
|
||||
{/each}
|
||||
</tr>
|
||||
</thead>
|
||||
|
|
|
@ -4,7 +4,17 @@ import * as _ from 'lodash';
|
|||
export async function load({ fetch, url, parent }) {
|
||||
const { config } = await parent();
|
||||
let q = url.searchParams.get('q');
|
||||
const res = await fetch(`${config.api}/dids` + (q ? `?q=${q}` : ''), {
|
||||
let sort = url.searchParams.get('sort');
|
||||
|
||||
const args = [];
|
||||
if (q) {
|
||||
args.push(`q=${q}`);
|
||||
}
|
||||
if (sort) {
|
||||
args.push(`sort=${sort}`);
|
||||
}
|
||||
|
||||
const res = await fetch(`${config.api}/dids` + (args.length > 0 ? '?' + args.join('&') : ''), {
|
||||
headers: { 'x-ats-wrapped': 'true' }
|
||||
});
|
||||
const json = await res.json();
|
||||
|
@ -20,6 +30,7 @@ export async function load({ fetch, url, parent }) {
|
|||
did,
|
||||
totalCount,
|
||||
q,
|
||||
sort,
|
||||
onlySandbox
|
||||
};
|
||||
}
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
const search = writable(data.q?.trim() || '');
|
||||
$: sourceData = data.did;
|
||||
let onlySandbox = data.onlySandbox || null;
|
||||
let sort = data.sort || null;
|
||||
|
||||
function sandboxToggleHandler() {
|
||||
sourceData = null;
|
||||
|
@ -37,7 +38,15 @@
|
|||
q = q.replace(/fed:sandbox/, '');
|
||||
}
|
||||
q = q.trim();
|
||||
const path = '/dids' + (q !== '' ? `?q=${q}` : '');
|
||||
let args = [];
|
||||
if (q) {
|
||||
args.push(`q=${q}`);
|
||||
}
|
||||
if (sort && !(args.length === 0 && sort === '!lastMod')) {
|
||||
args.push(`sort=${sort}`);
|
||||
}
|
||||
|
||||
const path = '/dids' + (args.length > 0 ? '?' + args.join('&') : '');
|
||||
const currentPath = $page.url.pathname + $page.url.search;
|
||||
if (currentPath === path) {
|
||||
return null;
|
||||
|
@ -72,6 +81,12 @@
|
|||
function selectionHandler(i) {
|
||||
return goto(`/${i.detail[0]}`);
|
||||
}
|
||||
|
||||
function onHeadSelected(e) {
|
||||
sourceData = null;
|
||||
sort = sort === e.detail ? (sort.startsWith('!') ? '' : '!') + e.detail : e.detail;
|
||||
gotoNewTableState();
|
||||
}
|
||||
</script>
|
||||
|
||||
<BasicPage {data} title="DIDs">
|
||||
|
@ -126,6 +141,6 @@
|
|||
All DIDs {#if onlySandbox} on sandbox{/if} ({formatNumber(data.totalCount)}):
|
||||
{/if}
|
||||
</div>
|
||||
<DIDTable {sourceData} {data} />
|
||||
<DIDTable {sourceData} {data} on:headSelected={(e) => onHeadSelected(e)} />
|
||||
{/if}
|
||||
</BasicPage>
|
||||
|
|
|
@ -1,15 +1,19 @@
|
|||
import * as _ from 'lodash';
|
||||
|
||||
/** @type {import('./$types').PageLoad} */
|
||||
export async function load({ fetch, parent }) {
|
||||
export async function load({ fetch, parent, url }) {
|
||||
const { config } = await parent();
|
||||
const res = await fetch(`${config.api}/pds`);
|
||||
const arr = (await res.json()).map((i) => {
|
||||
i.err = Boolean(i.inspect?.current?.err);
|
||||
return i;
|
||||
});
|
||||
const pds = _.orderBy(arr, ['env', 'err', 'didsCount'], ['asc', 'asc', 'desc']);
|
||||
//const pds = _.orderBy(arr, ['env', 'err', 'didsCount'], ['asc', 'asc', 'desc']);
|
||||
let q = url.searchParams.get('q');
|
||||
let sort = url.searchParams.get('sort');
|
||||
return {
|
||||
pds
|
||||
pds: arr,
|
||||
sort,
|
||||
q
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,13 +6,23 @@
|
|||
import { page } from '$app/stores';
|
||||
import PDSTable from '$lib/components/PDSTable.svelte';
|
||||
import BasicPage from '$lib/components/BasicPage.svelte';
|
||||
import { orderBy } from 'lodash';
|
||||
import { compute_slots } from 'svelte/internal';
|
||||
|
||||
export let data;
|
||||
|
||||
const search = writable($page.url.searchParams.get('q') || '');
|
||||
let sort = data.sort || null;
|
||||
|
||||
function gotoNewTableState() {
|
||||
const path = '/pds' + ($search !== '' ? `?q=${$search}` : '');
|
||||
let args = [];
|
||||
if ($search !== '') {
|
||||
args.push(`q=${$search}`);
|
||||
}
|
||||
if (sort) {
|
||||
args.push(`sort=${sort}`);
|
||||
}
|
||||
const path = '/pds' + (args.length > 0 ? '?' + args.join('&') : '');
|
||||
const currentPath = $page.url.pathname + $page.url.search;
|
||||
if (currentPath === path) {
|
||||
return null;
|
||||
|
@ -32,6 +42,12 @@
|
|||
$: sourceData = (function filterSourceData(bd) {
|
||||
const tokens = $search.split(' ');
|
||||
let base = JSON.parse(JSON.stringify(bd)); //.filter(d => d.inspect?.lastOnline)
|
||||
base = base.map((i) => {
|
||||
i.country = i.ip?.country;
|
||||
i.ms = !i.inspect?.current.err ? i.inspect?.current?.ms : null;
|
||||
i.lastOnline = i.inspect?.lastOnline;
|
||||
return i;
|
||||
});
|
||||
let str = [];
|
||||
for (const t of tokens) {
|
||||
const cmatch = t.match(/^country:([\w]{2})$/);
|
||||
|
@ -54,6 +70,19 @@
|
|||
if (txt) {
|
||||
base = base.filter((i) => i.url.match(new RegExp(txt, 'i')));
|
||||
}
|
||||
if (sort) {
|
||||
let sortReal = sort;
|
||||
let sortDirection = 1;
|
||||
let sortKey = sortReal.replace(/^!/, '');
|
||||
if (sortReal.startsWith('!')) {
|
||||
sortDirection = -1;
|
||||
}
|
||||
base = orderBy(base, [sortKey], [sortDirection === -1 ? 'desc' : 'asc']);
|
||||
} else {
|
||||
console.log('x');
|
||||
base = orderBy(base, ['env', 'err', 'didsCount'], ['asc', 'asc', 'desc']);
|
||||
}
|
||||
|
||||
return base;
|
||||
})(data.pds);
|
||||
|
||||
|
@ -62,6 +91,11 @@
|
|||
goto(url);
|
||||
return false;
|
||||
}
|
||||
|
||||
function onHeadSelected(e) {
|
||||
sort = sort === e.detail ? (sort.startsWith('!') ? '' : '!') + e.detail : e.detail;
|
||||
gotoNewTableState();
|
||||
}
|
||||
</script>
|
||||
|
||||
<BasicPage {data} title="PDS Instances">
|
||||
|
@ -84,5 +118,5 @@
|
|||
All PDS Instances ({formatNumber(sourceData.length)}):
|
||||
{/if}
|
||||
</div>
|
||||
<PDSTable {sourceData} {data} />
|
||||
<PDSTable {sourceData} {data} on:headSelected={(e) => onHeadSelected(e)} />
|
||||
</BasicPage>
|
||||
|
|
Načítá se…
Odkázat v novém úkolu