diff --git a/src/components/MarkdownInner.tsx b/src/components/MarkdownInner.tsx index a808162..7af90cf 100644 --- a/src/components/MarkdownInner.tsx +++ b/src/components/MarkdownInner.tsx @@ -1,4 +1,3 @@ -import { clientOnly } from '@solidjs/start'; import { ParentComponent } from 'solid-js'; import { SolidMarkdown } from 'solid-markdown'; diff --git a/src/entry-server.tsx b/src/entry-server.tsx index 401eff8..20cb2dc 100644 --- a/src/entry-server.tsx +++ b/src/entry-server.tsx @@ -1,5 +1,5 @@ // @refresh reload -import { createHandler, StartServer } from "@solidjs/start/server"; +import { createHandler, StartServer } from '@solidjs/start/server'; export default createHandler(() => ( ( + Join Mbin {assets} diff --git a/src/initdata.js b/src/initdata.js index 129238c..6bd92e4 100644 --- a/src/initdata.js +++ b/src/initdata.js @@ -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} + */ +const fetchServerList = async () => { + /** @type Set */ + 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} + */ +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), diff --git a/src/lib/utils.ts b/src/lib/utils.ts index 951433c..568578c 100644 --- a/src/lib/utils.ts +++ b/src/lib/utils.ts @@ -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)) -} \ No newline at end of file + return twMerge(clsx(inputs)); +} diff --git a/src/routes/index.tsx b/src/routes/index.tsx index 053b5a5..ae50a38 100644 --- a/src/routes/index.tsx +++ b/src/routes/index.tsx @@ -7,7 +7,7 @@ export default function Home() { Mbin

- a federated content aggregator, voting, discussion and microblogging + A federated content aggregator, voting, discussion and microblogging platform
(By the community, for the community)