Add fedidb.org as server list source, add title to webpages

This commit is contained in:
John Wesley 2024-07-05 22:02:43 -04:00
parent e118d9d5cd
commit ed7d5303a0
5 changed files with 119 additions and 59 deletions

View file

@ -1,4 +1,3 @@
import { clientOnly } from '@solidjs/start';
import { ParentComponent } from 'solid-js'; import { ParentComponent } from 'solid-js';
import { SolidMarkdown } from 'solid-markdown'; import { SolidMarkdown } from 'solid-markdown';

View file

@ -1,5 +1,5 @@
// @refresh reload // @refresh reload
import { createHandler, StartServer } from "@solidjs/start/server"; import { createHandler, StartServer } from '@solidjs/start/server';
export default createHandler(() => ( export default createHandler(() => (
<StartServer <StartServer
@ -9,6 +9,7 @@ export default createHandler(() => (
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<title>Join Mbin</title>
{assets} {assets}
</head> </head>
<body> <body>

View file

@ -3,7 +3,14 @@ import fs from 'node:fs/promises';
await fs.rm('./.output/data', { recursive: true, force: true }); await fs.rm('./.output/data', { recursive: true, force: true });
await fs.mkdir('./.output/data', { recursive: true }); await fs.mkdir('./.output/data', { recursive: true });
const initServerData = async () => { /**
* @returns {Promise<string>}
*/
const fetchServerList = async () => {
/** @type Set<string> */
const servers = new Set();
// Fetch Mbin servers from fediverse.observer
const fediverseObserver = await ( const fediverseObserver = await (
await fetch('https://api.fediverse.observer/', { await fetch('https://api.fediverse.observer/', {
body: '{"query":"{nodes(softwarename:\\"mbin\\" status: \\"UP\\"){domain}}"}', body: '{"query":"{nodes(softwarename:\\"mbin\\" status: \\"UP\\"){domain}}"}',
@ -11,42 +18,84 @@ const initServerData = async () => {
}) })
).json(); ).json();
/** @type string[] */ for (const server of fediverseObserver.data.nodes) {
const servers = fediverseObserver.data.nodes.map((v) => v.domain); servers.add(server.domain);
}
const serversJson = ( // Fetch Mbin servers from fedidb.org. The api used below is not documented and is subject to change;
await Promise.allSettled( // this is used due to the current publicized api not being sufficient for fetching Mbin server lists.
servers.map(async (serverHost) => { // Once issue #3 (https://github.com/fedidb/issues/issues/3) is complete, then we can move to api v1.
console.log('START:', serverHost); const fedidbCookies = (await fetch('https://fedidb.org')).headers
.getSetCookie()
.map((c) => c.split(';')[0]);
const fedidbHeaders = {
Accept: 'application/json',
'Content-Type': 'application/json',
'X-XSRF-TOKEN': decodeURIComponent(
fedidbCookies
.find((c) => c.startsWith('XSRF-TOKEN='))
.substring('XSRF-TOKEN='.length),
),
Cookie: fedidbCookies.join('; '),
};
let fedidbNextCursor = '';
do {
const fedidbServers = await (
await fetch(
`https://fedidb.org/api/web/network/software/servers${
fedidbNextCursor ? `?cursor=` + fedidbNextCursor : ''
}`,
{
headers: fedidbHeaders,
body: '{"slug":"mbin"}',
method: 'POST',
},
)
).json();
for (const server of fedidbServers.data) {
servers.add(server.domain);
}
fedidbNextCursor = fedidbServers.meta.next_cursor;
} while (fedidbNextCursor);
return [...servers];
};
/**
* @param {string} domain
* @returns {Promise<import('./routes/servers').Server>}
*/
const fetchServerInfo = async (domain) => {
console.log('START:', domain);
const jsonNodeInfo = await ( const jsonNodeInfo = await (
await fetch(`https://${serverHost}/nodeinfo/2.1.json`) await fetch(`https://${domain}/nodeinfo/2.1.json`)
).json(); ).json();
if (jsonNodeInfo.software.name != 'mbin') { if (jsonNodeInfo.software.name != 'mbin') {
throw new Error( throw new Error(`${domain} software does not match mbin (skipped)`);
`${serverHost} software does not match mbin (skipped)`,
);
} }
const jsonApiInfo = await ( const jsonApiInfo = await (await fetch(`https://${domain}/api/info`)).json();
await fetch(`https://${serverHost}/api/info`) if (jsonApiInfo.websiteDomain != domain) {
).json(); throw new Error(`${domain} api not setup correctly (skipped)`);
if (jsonApiInfo.websiteDomain != serverHost) {
throw new Error(`${serverHost} api not setup correctly (skipped)`);
} }
const jsonApiInstance = await ( const jsonApiInstance = await (
await fetch(`https://${serverHost}/api/instance`) await fetch(`https://${domain}/api/instance`)
).json(); ).json();
const jsonApiDefederated = await ( const jsonApiDefederated = await (
await fetch(`https://${serverHost}/api/defederated`) await fetch(`https://${domain}/api/defederated`)
).json(); ).json();
console.log('FINISH:', serverHost); console.log('FINISH:', domain);
/** @type import('./routes/servers').Server */ return {
const server = { domain: domain,
domain: serverHost,
version: jsonNodeInfo.software.version, version: jsonNodeInfo.software.version,
name: jsonNodeInfo.metadata.nodeName, name: jsonNodeInfo.metadata.nodeName,
description: jsonNodeInfo.metadata.nodeDescription, description: jsonNodeInfo.metadata.nodeDescription,
@ -62,14 +111,25 @@ const initServerData = async () => {
pages: jsonApiInstance, pages: jsonApiInstance,
defederated: jsonApiDefederated.instances ?? [], defederated: jsonApiDefederated.instances ?? [],
}; };
};
return server; const initServerData = async () => {
}), const servers = await fetchServerList();
)
) const serversJson = (await Promise.allSettled(servers.map(fetchServerInfo)))
.filter((v) => v.status == 'fulfilled') .filter((v) => {
const isOk = v.status == 'fulfilled';
if (!isOk) {
console.error(v.reason);
}
return isOk;
})
.map((v) => v.value); .map((v) => v.value);
console.log('Successful Mbin servers found:', serversJson.length);
fs.writeFile( fs.writeFile(
'./.output/data/servers.json', './.output/data/servers.json',
JSON.stringify(serversJson), JSON.stringify(serversJson),

View file

@ -1,7 +1,7 @@
import type { ClassValue } from "clsx" import type { ClassValue } from 'clsx';
import { clsx } from "clsx" import { clsx } from 'clsx';
import { twMerge } from "tailwind-merge" import { twMerge } from 'tailwind-merge';
export function cn(...inputs: ClassValue[]) { export function cn(...inputs: ClassValue[]) {
return twMerge(clsx(inputs)) return twMerge(clsx(inputs));
} }

View file

@ -7,7 +7,7 @@ export default function Home() {
Mbin Mbin
</h1> </h1>
<p class="my-10 text-2xl text-gray-400 block mx-auto"> <p class="my-10 text-2xl text-gray-400 block mx-auto">
a federated content aggregator, voting, discussion and microblogging A federated content aggregator, voting, discussion and microblogging
platform platform
<br /> <br />
(By the community, for the community) (By the community, for the community)