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 { SolidMarkdown } from 'solid-markdown';

View file

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

View file

@ -3,7 +3,14 @@ import fs from 'node:fs/promises';
await fs.rm('./.output/data', { recursive: true, force: 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 (
await fetch('https://api.fediverse.observer/', {
body: '{"query":"{nodes(softwarename:\\"mbin\\" status: \\"UP\\"){domain}}"}',
@ -11,65 +18,118 @@ const initServerData = async () => {
})
).json();
/** @type string[] */
const servers = fediverseObserver.data.nodes.map((v) => v.domain);
for (const server of fediverseObserver.data.nodes) {
servers.add(server.domain);
}
const serversJson = (
await Promise.allSettled(
servers.map(async (serverHost) => {
console.log('START:', serverHost);
// Fetch Mbin servers from fedidb.org. The api used below is not documented and is subject to change;
// this is used due to the current publicized api not being sufficient for fetching Mbin server lists.
// Once issue #3 (https://github.com/fedidb/issues/issues/3) is complete, then we can move to api v1.
const fedidbCookies = (await fetch('https://fedidb.org')).headers
.getSetCookie()
.map((c) => c.split(';')[0]);
const jsonNodeInfo = await (
await fetch(`https://${serverHost}/nodeinfo/2.1.json`)
).json();
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('; '),
};
if (jsonNodeInfo.software.name != 'mbin') {
throw new Error(
`${serverHost} software does not match mbin (skipped)`,
);
}
let fedidbNextCursor = '';
const jsonApiInfo = await (
await fetch(`https://${serverHost}/api/info`)
).json();
if (jsonApiInfo.websiteDomain != serverHost) {
throw new Error(`${serverHost} api not setup correctly (skipped)`);
}
const jsonApiInstance = await (
await fetch(`https://${serverHost}/api/instance`)
).json();
const jsonApiDefederated = await (
await fetch(`https://${serverHost}/api/defederated`)
).json();
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();
console.log('FINISH:', serverHost);
for (const server of fedidbServers.data) {
servers.add(server.domain);
}
/** @type import('./routes/servers').Server */
const server = {
domain: serverHost,
version: jsonNodeInfo.software.version,
name: jsonNodeInfo.metadata.nodeName,
description: jsonNodeInfo.metadata.nodeDescription,
openRegistrations: jsonNodeInfo.openRegistrations,
federationEnabled: jsonApiInfo.websiteFederationEnabled,
language: jsonApiInfo.websiteDefaultLang ?? 'en',
contactEmail: jsonApiInfo.websiteContactEmail,
totalUsers: jsonNodeInfo.usage.users.total,
activeHalfyearUsers: jsonNodeInfo.usage.users.activeHalfyear,
activeMonthUsers: jsonNodeInfo.usage.users.activeMonth,
localPosts: jsonNodeInfo.usage.localPosts,
localComments: jsonNodeInfo.usage.localComments,
pages: jsonApiInstance,
defederated: jsonApiDefederated.instances ?? [],
};
fedidbNextCursor = fedidbServers.meta.next_cursor;
} while (fedidbNextCursor);
return server;
}),
)
)
.filter((v) => v.status == 'fulfilled')
return [...servers];
};
/**
* @param {string} domain
* @returns {Promise<import('./routes/servers').Server>}
*/
const fetchServerInfo = async (domain) => {
console.log('START:', domain);
const jsonNodeInfo = await (
await fetch(`https://${domain}/nodeinfo/2.1.json`)
).json();
if (jsonNodeInfo.software.name != 'mbin') {
throw new Error(`${domain} software does not match mbin (skipped)`);
}
const jsonApiInfo = await (await fetch(`https://${domain}/api/info`)).json();
if (jsonApiInfo.websiteDomain != domain) {
throw new Error(`${domain} api not setup correctly (skipped)`);
}
const jsonApiInstance = await (
await fetch(`https://${domain}/api/instance`)
).json();
const jsonApiDefederated = await (
await fetch(`https://${domain}/api/defederated`)
).json();
console.log('FINISH:', domain);
return {
domain: domain,
version: jsonNodeInfo.software.version,
name: jsonNodeInfo.metadata.nodeName,
description: jsonNodeInfo.metadata.nodeDescription,
openRegistrations: jsonNodeInfo.openRegistrations,
federationEnabled: jsonApiInfo.websiteFederationEnabled,
language: jsonApiInfo.websiteDefaultLang ?? 'en',
contactEmail: jsonApiInfo.websiteContactEmail,
totalUsers: jsonNodeInfo.usage.users.total,
activeHalfyearUsers: jsonNodeInfo.usage.users.activeHalfyear,
activeMonthUsers: jsonNodeInfo.usage.users.activeMonth,
localPosts: jsonNodeInfo.usage.localPosts,
localComments: jsonNodeInfo.usage.localComments,
pages: jsonApiInstance,
defederated: jsonApiDefederated.instances ?? [],
};
};
const initServerData = async () => {
const servers = await fetchServerList();
const serversJson = (await Promise.allSettled(servers.map(fetchServerInfo)))
.filter((v) => {
const isOk = v.status == 'fulfilled';
if (!isOk) {
console.error(v.reason);
}
return isOk;
})
.map((v) => v.value);
console.log('Successful Mbin servers found:', serversJson.length);
fs.writeFile(
'./.output/data/servers.json',
JSON.stringify(serversJson),

View file

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

View file

@ -7,7 +7,7 @@ export default function Home() {
Mbin
</h1>
<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
<br />
(By the community, for the community)