mirror of
https://github.com/MbinOrg/mbin-website.git
synced 2025-07-01 15:48:56 +00:00
FIX: move server data generation to separate script
This commit is contained in:
parent
46e8fc0fec
commit
6ab2b70323
4 changed files with 95 additions and 85 deletions
|
@ -1,8 +1,11 @@
|
||||||
import { defineConfig } from '@solidjs/start/config';
|
import { defineConfig } from '@solidjs/start/config';
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
ssr: false,
|
ssr: true,
|
||||||
server: {
|
server: {
|
||||||
static: true,
|
preset: 'github-pages',
|
||||||
|
prerender: {
|
||||||
|
crawlLinks: true,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
|
@ -3,8 +3,9 @@
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vinxi dev",
|
"dev": "vinxi dev",
|
||||||
"build": "vinxi build",
|
"build": "pnpm run initdata && vinxi build",
|
||||||
"start": "vinxi start"
|
"start": "vinxi start",
|
||||||
|
"initdata": "node ./src/initdata.js"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@iconify-icon/solid": "^2.1.1",
|
"@iconify-icon/solid": "^2.1.1",
|
||||||
|
|
80
src/initdata.js
Normal file
80
src/initdata.js
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
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 () => {
|
||||||
|
const fediverseObserver = await (
|
||||||
|
await fetch('https://api.fediverse.observer/', {
|
||||||
|
body: '{"query":"{nodes(softwarename:\\"mbin\\" status: \\"UP\\"){domain}}"}',
|
||||||
|
method: 'POST',
|
||||||
|
})
|
||||||
|
).json();
|
||||||
|
|
||||||
|
/** @type string[] */
|
||||||
|
const servers = fediverseObserver.data.nodes.map((v) => v.domain);
|
||||||
|
|
||||||
|
const serversJson = (
|
||||||
|
await Promise.allSettled(
|
||||||
|
servers.map(async (serverHost) => {
|
||||||
|
console.log('START:', serverHost);
|
||||||
|
|
||||||
|
const jsonNodeInfo = await (
|
||||||
|
await fetch(`https://${serverHost}/nodeinfo/2.1.json`)
|
||||||
|
).json();
|
||||||
|
|
||||||
|
if (jsonNodeInfo.software.name != 'mbin') {
|
||||||
|
throw new Error(
|
||||||
|
`${serverHost} software does not match mbin (skipped)`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
console.log('FINISH:', serverHost);
|
||||||
|
|
||||||
|
/** @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 ?? [],
|
||||||
|
};
|
||||||
|
|
||||||
|
return server;
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.filter((v) => v.status == 'fulfilled')
|
||||||
|
.map((v) => v.value);
|
||||||
|
|
||||||
|
fs.writeFile(
|
||||||
|
'./.output/data/servers.json',
|
||||||
|
JSON.stringify(serversJson),
|
||||||
|
'utf8',
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
await initServerData();
|
|
@ -1,5 +1,4 @@
|
||||||
import { For, Show, createSignal } from 'solid-js';
|
import { For, Show, createSignal } from 'solid-js';
|
||||||
import { createAsync, cache } from '@solidjs/router';
|
|
||||||
import Markdown from '~/components/Markdown';
|
import Markdown from '~/components/Markdown';
|
||||||
import Chip from '~/components/Chip';
|
import Chip from '~/components/Chip';
|
||||||
import { Button } from '~/components/ui/button';
|
import { Button } from '~/components/ui/button';
|
||||||
|
@ -18,8 +17,11 @@ import {
|
||||||
AccordionItem,
|
AccordionItem,
|
||||||
AccordionTrigger,
|
AccordionTrigger,
|
||||||
} from '~/components/ui/accordion';
|
} from '~/components/ui/accordion';
|
||||||
|
import serversJson from '../../.output/data/servers.json';
|
||||||
|
|
||||||
type Server = {
|
const servers = serversJson as Server[];
|
||||||
|
|
||||||
|
export interface Server {
|
||||||
domain: string;
|
domain: string;
|
||||||
version: string;
|
version: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -41,7 +43,7 @@ type Server = {
|
||||||
terms?: string;
|
terms?: string;
|
||||||
};
|
};
|
||||||
defederated: string[];
|
defederated: string[];
|
||||||
};
|
}
|
||||||
|
|
||||||
const pageNames: Required<Server['pages']> = {
|
const pageNames: Required<Server['pages']> = {
|
||||||
about: 'About',
|
about: 'About',
|
||||||
|
@ -51,92 +53,16 @@ const pageNames: Required<Server['pages']> = {
|
||||||
terms: 'Terms of Service',
|
terms: 'Terms of Service',
|
||||||
};
|
};
|
||||||
|
|
||||||
const getServers = cache(async () => {
|
|
||||||
'use server';
|
|
||||||
|
|
||||||
const fediverseObserver = await (
|
|
||||||
await fetch('https://api.fediverse.observer/', {
|
|
||||||
body: '{"query":"{nodes(softwarename:\\"mbin\\" status: \\"UP\\"){domain}}"}',
|
|
||||||
method: 'POST',
|
|
||||||
})
|
|
||||||
).json();
|
|
||||||
|
|
||||||
const servers: string[] = fediverseObserver.data.nodes.map((v) => v.domain);
|
|
||||||
|
|
||||||
return (
|
|
||||||
await Promise.allSettled(
|
|
||||||
servers.map(async (serverHost) => {
|
|
||||||
console.log('START:', serverHost);
|
|
||||||
|
|
||||||
const jsonNodeInfo = await (
|
|
||||||
await fetch(`https://${serverHost}/nodeinfo/2.1.json`)
|
|
||||||
).json();
|
|
||||||
|
|
||||||
if (jsonNodeInfo.software.name != 'mbin') {
|
|
||||||
throw new Error(
|
|
||||||
`${serverHost} software does not match mbin (skipped)`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
|
||||||
|
|
||||||
console.log('FINISH:', serverHost);
|
|
||||||
|
|
||||||
const server: 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 ?? [],
|
|
||||||
};
|
|
||||||
|
|
||||||
return server;
|
|
||||||
}),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.filter((v) => v.status == 'fulfilled')
|
|
||||||
.map((v) => v.value as Server);
|
|
||||||
}, 'users');
|
|
||||||
|
|
||||||
export const route = {
|
|
||||||
load: () => getServers(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const languageNames = new Intl.DisplayNames(['en'], {
|
const languageNames = new Intl.DisplayNames(['en'], {
|
||||||
type: 'language',
|
type: 'language',
|
||||||
});
|
});
|
||||||
|
|
||||||
export default function ServersPage() {
|
export default function ServersPage() {
|
||||||
const servers = createAsync(() => getServers());
|
|
||||||
|
|
||||||
const [filterRegistration, setFilterRegistration] = createSignal(true);
|
const [filterRegistration, setFilterRegistration] = createSignal(true);
|
||||||
const [langFilter, setLangFilter] = createSignal<string>('');
|
const [langFilter, setLangFilter] = createSignal<string>('');
|
||||||
|
|
||||||
const resultServers = () => {
|
const resultServers = () => {
|
||||||
if (!servers()) return undefined;
|
return servers.filter(
|
||||||
|
|
||||||
return servers()!.filter(
|
|
||||||
(server) =>
|
(server) =>
|
||||||
(!filterRegistration() || server.openRegistrations) &&
|
(!filterRegistration() || server.openRegistrations) &&
|
||||||
(!langFilter() || server.language == langFilter()),
|
(!langFilter() || server.language == langFilter()),
|
||||||
|
@ -158,7 +84,7 @@ export default function ServersPage() {
|
||||||
Language:
|
Language:
|
||||||
<select onChange={(e) => setLangFilter(e.target.value)}>
|
<select onChange={(e) => setLangFilter(e.target.value)}>
|
||||||
<option value="">All Languages</option>
|
<option value="">All Languages</option>
|
||||||
<For each={[...new Set(servers()?.map((v) => v.language))]}>
|
<For each={[...new Set(servers.map((v) => v.language))]}>
|
||||||
{(lang) => <option value={lang}>{languageNames.of(lang)}</option>}
|
{(lang) => <option value={lang}>{languageNames.of(lang)}</option>}
|
||||||
</For>
|
</For>
|
||||||
</select>
|
</select>
|
||||||
|
|
Loading…
Add table
Reference in a new issue