diff --git a/apps/website/astro.config.ts b/apps/website/astro.config.ts index 709c4b4..80fe7d4 100644 --- a/apps/website/astro.config.ts +++ b/apps/website/astro.config.ts @@ -1,25 +1,27 @@ import path from "path"; import { fileURLToPath } from "url"; import { defineConfig } from "astro/config"; +import preact from "@astrojs/preact"; import sitemap from "@astrojs/sitemap"; import tailwind from "@astrojs/tailwind"; import cloudflare from "@astrojs/cloudflare"; import auth from "auth-astro"; + import { CONFIG } from "./src/config"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); // https://astro.build/config export default defineConfig({ - site: CONFIG.origin, - integrations: [sitemap(), tailwind(), auth()], - output: "server", - adapter: cloudflare(), - vite: { - resolve: { - alias: { - "~": path.resolve(__dirname, "./src"), - }, - }, - }, + site: CONFIG.origin, + integrations: [sitemap(), tailwind(), auth(), preact()], + output: "server", + adapter: cloudflare(), + vite: { + resolve: { + alias: { + "~": path.resolve(__dirname, "./src"), + }, + }, + }, }); diff --git a/apps/website/auth.config.ts b/apps/website/auth.config.ts index dab32e6..389e7b6 100644 --- a/apps/website/auth.config.ts +++ b/apps/website/auth.config.ts @@ -29,17 +29,8 @@ export default defineConfig({ (session.user as unknown as User).global_name = token.global_name as string; - const guilds = await fetch( - "https://discord.com/api/v10/users/@me/guilds", - { - headers: { - Authorization: `Bearer ${token.accessToken as string}`, - "Cache-Control": "max-age=300", - }, - } - ); - - (session.user as unknown as User).guilds = await guilds.json(); + (session.user as unknown as User).discordAccessToken = + token.accessToken as string; } return session; diff --git a/apps/website/bun.lockb b/apps/website/bun.lockb index 6d5ea1d..26b57df 100755 Binary files a/apps/website/bun.lockb and b/apps/website/bun.lockb differ diff --git a/apps/website/package.json b/apps/website/package.json index 6bd6730..2d0f437 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -10,6 +10,7 @@ }, "dependencies": { "@astrojs/cloudflare": "^11.0.1", + "@astrojs/preact": "^3.5.1", "@astrojs/prefetch": "^0.2.1", "@astrojs/sitemap": "^3.1.6", "@astrojs/tailwind": "^5.1.0", @@ -19,6 +20,7 @@ "astro-google-fonts-optimizer": "^0.2.2", "astro-icon": "^0.8.0", "auth-astro": "^4.1.2", + "preact": "^10.23.1", "tailwindcss": "^3.3.1" }, "devDependencies": { diff --git a/apps/website/src/components/dashboard/UserInfo.astro b/apps/website/src/components/dashboard/UserInfo.astro new file mode 100644 index 0000000..273a362 --- /dev/null +++ b/apps/website/src/components/dashboard/UserInfo.astro @@ -0,0 +1,23 @@ +--- +import { Image } from "astro:assets"; +import { getUser } from "~/lib/user"; + +const user = await getUser(Astro.request); +if (!user) { + return Astro.redirect("/auth/login"); +} +--- + +
+ icon + +

+ @{user.name} +

+
diff --git a/apps/website/src/components/preact/Guild.tsx b/apps/website/src/components/preact/Guild.tsx new file mode 100644 index 0000000..08ef8d6 --- /dev/null +++ b/apps/website/src/components/preact/Guild.tsx @@ -0,0 +1,28 @@ +import type { Guild } from "~/env"; + +interface Props { + guild: Guild; + mutual: boolean; +} + +export default function Guild({ guild, mutual }: Props) { + return ( +
+
+ icon + +

{guild.name}

+
+
+ ); +} diff --git a/apps/website/src/components/preact/GuildSelector.tsx b/apps/website/src/components/preact/GuildSelector.tsx new file mode 100644 index 0000000..f9d89ee --- /dev/null +++ b/apps/website/src/components/preact/GuildSelector.tsx @@ -0,0 +1,31 @@ +import { Component } from "preact"; +import Guild from "./Guild"; +import LoadingGuild from "./LoadingGuild"; +import type { MutualeGuild } from "~/env"; + +interface State { + guilds: MutualeGuild[] | null; +} + +export default class GuildSelector extends Component<{}, State> { + constructor(props) { + super(props); + + this.state = { + guilds: null, + }; + } + + async componentDidMount() { + const response = await fetch("/api/user/guilds"); + const data = await response.json(); + this.setState({ guilds: data }); + } + + render() { + const { guilds } = this.state; + if (!guilds) return [...Array(15)].map(() => ); + + return guilds.map((g) => ); + } +} diff --git a/apps/website/src/components/preact/LoadingGuild.tsx b/apps/website/src/components/preact/LoadingGuild.tsx new file mode 100644 index 0000000..59e2a06 --- /dev/null +++ b/apps/website/src/components/preact/LoadingGuild.tsx @@ -0,0 +1,9 @@ +export default function LoadingGuild() { + return ( +
+
+
+
+
+ ); +} diff --git a/apps/website/src/env.d.ts b/apps/website/src/env.d.ts index 47ddad0..1bcd6b7 100644 --- a/apps/website/src/env.d.ts +++ b/apps/website/src/env.d.ts @@ -4,7 +4,7 @@ import type { User as AuthCoreUser } from "@auth/core/types"; export type User = AuthCoreUser & { global_name: string; - guilds: Guild[]; + discordAccessToken: string; }; export interface Guild { @@ -14,3 +14,8 @@ export interface Guild { permissions: string; owner: boolean; } + +export interface MutualeGuild { + mutual: boolean; + guild: Guild; +} diff --git a/apps/website/src/lib/guilds.ts b/apps/website/src/lib/guilds.ts index 6f4220a..5776160 100644 --- a/apps/website/src/lib/guilds.ts +++ b/apps/website/src/lib/guilds.ts @@ -1,20 +1,28 @@ -import type { Guild } from "~/env"; +import type { Guild, MutualeGuild, User } from "~/env"; + +export async function getUserGuilds(user: User): Promise { + const discordApiGuildsResponse = await fetch( + "https://discord.com/api/v10/users/@me/guilds", + { + headers: { + Authorization: `Bearer ${user.discordAccessToken as string}`, + "Cache-Control": "max-age=300", + }, + } + ); + + return (await discordApiGuildsResponse.json()) as Guild[]; +} // Checks if user has AMDINISTRATOR permissions in the guild -export async function filterUserGuilds(guilds: Guild[]): Promise< - { - mutual: boolean; - guild: Guild; - }[] -> { +export async function filterUserGuilds( + guilds: Guild[] +): Promise { const filtered = guilds .filter((g) => (BigInt(g.permissions) & 0x8n) == 0x8n) .sort((a, b) => Number(b.owner) - Number(a.owner)); - const result: { - mutual: boolean; - guild: Guild; - }[] = []; + const result: MutualeGuild[] = []; for (const guild of filtered) { const mutual = await isMutualGuild(guild); diff --git a/apps/website/src/lib/user.ts b/apps/website/src/lib/user.ts new file mode 100644 index 0000000..eae9165 --- /dev/null +++ b/apps/website/src/lib/user.ts @@ -0,0 +1,9 @@ +import { getSession } from "auth-astro/server"; +import type { User } from "~/env"; + +export async function getUser(req: Request): Promise { + const session = await getSession(req); + if (!session || !session.user) return null; + + return session.user as User; +} diff --git a/apps/website/src/pages/api/user/guilds.ts b/apps/website/src/pages/api/user/guilds.ts index 9f28f00..13a3a47 100644 --- a/apps/website/src/pages/api/user/guilds.ts +++ b/apps/website/src/pages/api/user/guilds.ts @@ -1,23 +1,17 @@ import type { APIRoute } from "astro"; -import { getSession } from "auth-astro/server"; -import type { User } from "~/env"; +import { filterUserGuilds, getUserGuilds } from "~/lib/guilds"; +import { getUser } from "~/lib/user"; export const GET: APIRoute = async ({ request }) => { - const session = await getSession(request); - if (!session || !session.user) { + const user = await getUser(request); + if (!user) { return new Response(null, { status: 401, }); } - const user = session.user as User; + const guilds = await getUserGuilds(user); + const res = await filterUserGuilds(guilds); - return Response.json( - user.guilds.map((g) => ({ - id: g.id, - name: g.name, - owner: g.owner, - permissions: g.permissions, - })) - ); + return Response.json(res); }; diff --git a/apps/website/src/pages/api/user/index.ts b/apps/website/src/pages/api/user/index.ts deleted file mode 100644 index 6736781..0000000 --- a/apps/website/src/pages/api/user/index.ts +++ /dev/null @@ -1,21 +0,0 @@ -import type { APIRoute } from "astro"; -import { getSession } from "auth-astro/server"; -import type { User } from "~/env"; - -export const GET: APIRoute = async ({ request }) => { - const session = await getSession(request); - if (!session || !session.user) { - return new Response(null, { - status: 401, - }); - } - - const user = session.user as User; - - return Response.json({ - id: user.id, - username: user.name, - global_name: user.global_name, - avatar_url: user.image, - }); -}; diff --git a/apps/website/src/pages/dashboard/index.astro b/apps/website/src/pages/dashboard/index.astro index 9a3baed..3165a2f 100644 --- a/apps/website/src/pages/dashboard/index.astro +++ b/apps/website/src/pages/dashboard/index.astro @@ -1,60 +1,18 @@ --- -import type { User } from "~/env"; -import { filterUserGuilds } from "~/lib/guilds"; -import { getSession } from "auth-astro/server"; import Layout from "~/layouts/Layout.astro"; import Container from "~/components/Container.astro"; -import { Image } from "astro:assets"; - -const session = await getSession(Astro.request); -if (!session || !session.user) { - return Astro.redirect("/auth/login"); -} - -const user = session.user as User; -const guilds = await filterUserGuilds(user.guilds); +import GuildSelector from "~/components/preact/GuildSelector"; +import UserInfo from "~/components/dashboard/UserInfo.astro"; --- -
- icon - -

- @{user.name} -

-
+
- { - guilds.map(({ guild, mutual }) => ( -
-
- icon - -

{guild.name}

-
-
- )) - } +