mirror of
https://github.com/sern-handler/website
synced 2026-06-06 01:16:47 +00:00
refactor: move types into seperate file, add zodFetch
This commit is contained in:
@@ -1,17 +1,8 @@
|
||||
---
|
||||
import PluginModal from "./PluginModal.astro";
|
||||
import { Markdown } from "@astropub/md";
|
||||
import PluginModal from "./PluginModal.astro";
|
||||
import DeprecatedIcon from "./DeprecatedIcon.astro";
|
||||
|
||||
export interface Plugin {
|
||||
description: string;
|
||||
hash: string;
|
||||
name: string;
|
||||
author: string[];
|
||||
link: string;
|
||||
example: string;
|
||||
version: string;
|
||||
}
|
||||
import type { Plugin } from "../utils/types";
|
||||
|
||||
type Props = Plugin;
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
import type { Plugin } from "./PluginCard.astro";
|
||||
import { Code } from "@astrojs/starlight/components";
|
||||
import { Markdown } from "@astropub/md";
|
||||
import type { Plugin } from "../utils/types";
|
||||
import Modal from "./Modal.astro";
|
||||
|
||||
type Props = Plugin;
|
||||
|
||||
@@ -1,31 +1,7 @@
|
||||
---
|
||||
export interface Sponsor {
|
||||
id: string;
|
||||
name: string;
|
||||
roles: string[];
|
||||
isAdmin: boolean;
|
||||
isCore: boolean;
|
||||
isBacker: boolean;
|
||||
since: string;
|
||||
image: string;
|
||||
description: string | null;
|
||||
collectiveSlug: string;
|
||||
totalAmountDonated: number;
|
||||
type: string;
|
||||
publicMessage: string | null;
|
||||
isIncognito: boolean;
|
||||
__typename: string;
|
||||
}
|
||||
import type { Contributor } from "../utils/types";
|
||||
|
||||
type Props = Pick<
|
||||
Sponsor,
|
||||
| "name"
|
||||
| "image"
|
||||
| "totalAmountDonated"
|
||||
| "isAdmin"
|
||||
| "publicMessage"
|
||||
| "collectiveSlug"
|
||||
>;
|
||||
type Props = Contributor;
|
||||
|
||||
const {
|
||||
name,
|
||||
|
||||
@@ -1,13 +1,22 @@
|
||||
---
|
||||
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
|
||||
import { CardGrid } from "@astrojs/starlight/components";
|
||||
import PluginCard, { type Plugin } from "../components/PluginCard.astro";
|
||||
import { z } from "astro/zod";
|
||||
import PluginCard from "../components/PluginCard.astro";
|
||||
import { PluginSchema } from "../utils/types";
|
||||
import { zodFetch } from "../utils/fetch";
|
||||
|
||||
const plugins = (await (
|
||||
await fetch(
|
||||
"https://raw.githubusercontent.com/sern-handler/awesome-plugins/main/pluginlist.json",
|
||||
)
|
||||
).json()) as Plugin[];
|
||||
const pluginResponse = await zodFetch(
|
||||
z.array(PluginSchema),
|
||||
"Failed to fetch plugins",
|
||||
"https://raw.githubusercontent.com/sern-handler/awesome-plugins/main/pluginlist.json",
|
||||
);
|
||||
|
||||
if (!pluginResponse.ok) {
|
||||
return console.error(pluginResponse.error);
|
||||
}
|
||||
|
||||
const plugins = pluginResponse.value;
|
||||
---
|
||||
|
||||
<StarlightPage frontmatter={{ title: "Plugins", template: "splash" }}>
|
||||
|
||||
@@ -1,36 +1,41 @@
|
||||
---
|
||||
import StarlightPage from "@astrojs/starlight/components/StarlightPage.astro";
|
||||
import type { Sponsor } from "../components/SponsorCard.astro";
|
||||
import { z } from "astro/zod";
|
||||
import SponsorCard from "../components/SponsorCard.astro";
|
||||
import { zodFetch } from "../utils/fetch";
|
||||
import { OpenCollectiveAccountSchema } from "../utils/types";
|
||||
|
||||
interface SponsorResponse {
|
||||
data: {
|
||||
account: {
|
||||
contributors: {
|
||||
nodes: Sponsor[];
|
||||
};
|
||||
};
|
||||
};
|
||||
const sponsorResponse = await zodFetch(
|
||||
z.object({
|
||||
data: z.object({
|
||||
account: OpenCollectiveAccountSchema,
|
||||
}),
|
||||
}),
|
||||
"Failed to fetch sponsors",
|
||||
"https://opencollective.com/api/graphql/v2",
|
||||
{
|
||||
body: JSON.stringify({
|
||||
operationName: "BannerTopContributors",
|
||||
variables: {
|
||||
collectiveSlug: "sern",
|
||||
},
|
||||
query:
|
||||
"query BannerTopContributors($collectiveSlug: String!) {\n account(slug: $collectiveSlug, throwIfMissing: false) {\n id\n currency\n slug\n ... on AccountWithContributions {\n contributors(limit: 150) {\n totalCount\n nodes {\n id\n name\n roles\n isAdmin\n isCore\n isBacker\n since\n image\n description\n collectiveSlug\n totalAmountDonated\n type\n publicMessage\n isIncognito\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}",
|
||||
}),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
if (!sponsorResponse.ok) {
|
||||
return console.error(sponsorResponse.error);
|
||||
}
|
||||
|
||||
const sponsors = (
|
||||
(await (
|
||||
await fetch("https://opencollective.com/api/graphql/v2", {
|
||||
body: JSON.stringify({
|
||||
operationName: "BannerTopContributors",
|
||||
variables: {
|
||||
collectiveSlug: "sern",
|
||||
},
|
||||
query:
|
||||
"query BannerTopContributors($collectiveSlug: String!) {\n account(slug: $collectiveSlug, throwIfMissing: false) {\n id\n currency\n slug\n ... on AccountWithContributions {\n contributors(limit: 150) {\n totalCount\n nodes {\n id\n name\n roles\n isAdmin\n isCore\n isBacker\n since\n image\n description\n collectiveSlug\n totalAmountDonated\n type\n publicMessage\n isIncognito\n __typename\n }\n __typename\n }\n __typename\n }\n __typename\n }\n}",
|
||||
}),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
},
|
||||
})
|
||||
).json()) as SponsorResponse
|
||||
).data.account.contributors.nodes.filter((s) => s.totalAmountDonated > 0);
|
||||
const sponsors = sponsorResponse.value.data.account.contributors.nodes.filter(
|
||||
(s) => s.totalAmountDonated > 0,
|
||||
);
|
||||
---
|
||||
|
||||
<StarlightPage frontmatter={{ title: "Sponsors", template: "splash" }}>
|
||||
|
||||
40
src/utils/fetch.ts
Normal file
40
src/utils/fetch.ts
Normal file
@@ -0,0 +1,40 @@
|
||||
import type { z } from "astro/zod";
|
||||
import { Ok, Err } from "./types";
|
||||
|
||||
/**
|
||||
* @param schema The Zod schema to validate the response against
|
||||
* @param error An error message to return if the response is invalid (e.g. "Failed to fetch data")
|
||||
* @param args The arguments to pass to fetch (e.g. URL, headers, etc.)
|
||||
* @returns A Result type containing either the parsed JSON or an error message (specified by the error parameter)
|
||||
*
|
||||
* @example
|
||||
* const plugins = await zodFetch(PluginSchema, "Failed to fetch plugins", "/api/plugins");
|
||||
* if (!plugins.ok) {
|
||||
* console.error(plugins.error); // "Failed to fetch plugins"
|
||||
* }
|
||||
* console.log(plugins.value); // { description: "A plugin", hash: "123", name: "My Plugin", author: ["Me"], link: "https://example.com", example: "example" }
|
||||
*/
|
||||
export const zodFetch = async <TSchema extends z.Schema>(
|
||||
schema: TSchema,
|
||||
error: string,
|
||||
...args: Parameters<typeof fetch>
|
||||
) => {
|
||||
const res = await fetch(...args);
|
||||
|
||||
if (!res.ok) {
|
||||
console.error(await res.text());
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
const json = (await res.json()) as unknown;
|
||||
const parsed = schema.safeParse(json);
|
||||
|
||||
if (!parsed.success) {
|
||||
console.error(
|
||||
parsed.error.issues.map((i) => `${i.code} | ${i.message}`).join("\n"),
|
||||
);
|
||||
return Err(error);
|
||||
}
|
||||
|
||||
return Ok(json as z.infer<TSchema>);
|
||||
};
|
||||
53
src/utils/types.ts
Normal file
53
src/utils/types.ts
Normal file
@@ -0,0 +1,53 @@
|
||||
import { z } from "astro/zod";
|
||||
|
||||
export type Result<Ok, Err> =
|
||||
| { ok: true; value: Ok }
|
||||
| { ok: false; error: Err };
|
||||
export const Ok = <Ok>(value: Ok) => ({ ok: true, value } as const);
|
||||
export const Err = <Err>(error: Err) => ({ ok: false, error } as const);
|
||||
|
||||
export type Contributor = z.infer<typeof ContributorSchema>;
|
||||
export const ContributorSchema = z.object({
|
||||
id: z.string(),
|
||||
name: z.string(),
|
||||
roles: z.array(z.string()),
|
||||
isAdmin: z.boolean(),
|
||||
isCore: z.boolean(),
|
||||
isBacker: z.boolean(),
|
||||
since: z.string(),
|
||||
image: z.string(),
|
||||
description: z.string().nullable(),
|
||||
collectiveSlug: z.string(),
|
||||
totalAmountDonated: z.number(),
|
||||
type: z.string(),
|
||||
publicMessage: z.string().nullable(),
|
||||
isIncognito: z.boolean(),
|
||||
__typename: z.string(),
|
||||
});
|
||||
|
||||
export type Contributors = z.infer<typeof ContributorsSchema>;
|
||||
export const ContributorsSchema = z.object({
|
||||
totalCount: z.number(),
|
||||
nodes: z.array(ContributorSchema),
|
||||
__typename: z.string(),
|
||||
});
|
||||
|
||||
export type OpenCollectiveAccount = z.infer<typeof OpenCollectiveAccountSchema>;
|
||||
export const OpenCollectiveAccountSchema = z.object({
|
||||
id: z.string(),
|
||||
currency: z.string(),
|
||||
slug: z.string(),
|
||||
contributors: ContributorsSchema,
|
||||
__typename: z.string(),
|
||||
});
|
||||
|
||||
export type Plugin = z.infer<typeof PluginSchema>;
|
||||
export const PluginSchema = z.object({
|
||||
description: z.string(),
|
||||
hash: z.string(),
|
||||
name: z.string(),
|
||||
author: z.array(z.string()),
|
||||
link: z.string().url(),
|
||||
example: z.string(),
|
||||
version: z.string(),
|
||||
});
|
||||
Reference in New Issue
Block a user