From 43b8e55022c29baf54b3cb3285e94f8ee0c45eb8 Mon Sep 17 00:00:00 2001 From: Ayden Jahola Date: Tue, 29 Oct 2024 13:00:28 +0000 Subject: [PATCH] add inital auth implementation --- dashboard/app/admin/manage/[guildId]/page.tsx | 255 ++++++++++++++++++ dashboard/app/admin/page.tsx | 90 +++++++ dashboard/app/api/auth/[...nextauth]/route.ts | 21 ++ dashboard/app/api/discord/channels/route.ts | 44 +++ dashboard/app/api/discord/guilds/route.ts | 74 +++++ dashboard/app/layout.tsx | 21 +- dashboard/app/signin/page.tsx | 24 ++ dashboard/components/ClientProvider.tsx | 11 + dashboard/lib/mongodb.ts | 10 + dashboard/package.json | 14 +- 10 files changed, 550 insertions(+), 14 deletions(-) create mode 100644 dashboard/app/admin/manage/[guildId]/page.tsx create mode 100644 dashboard/app/admin/page.tsx create mode 100644 dashboard/app/api/auth/[...nextauth]/route.ts create mode 100644 dashboard/app/api/discord/channels/route.ts create mode 100644 dashboard/app/api/discord/guilds/route.ts create mode 100644 dashboard/app/signin/page.tsx create mode 100644 dashboard/components/ClientProvider.tsx create mode 100644 dashboard/lib/mongodb.ts diff --git a/dashboard/app/admin/manage/[guildId]/page.tsx b/dashboard/app/admin/manage/[guildId]/page.tsx new file mode 100644 index 0000000..6570283 --- /dev/null +++ b/dashboard/app/admin/manage/[guildId]/page.tsx @@ -0,0 +1,255 @@ +"use client"; + +import { useRouter } from "next/navigation"; +import { useEffect, useState } from "react"; +import axios from "axios"; + +interface ServerSetting { + _id: string; + guildId: string; + emailDomains: string[]; + countingChannelId: string; + generalChannelId: string; + logChannelId: string; + verificationChannelId: string; + verifiedRoleName: string; + actionItemsChannelId: string; + actionItemsTargetChannelId: string; +} + +interface Channel { + id: string; + name: string; +} + +interface GuildData { + guildId: string; + guildName: string; + guildIcon: string | null; + settings: ServerSetting | null; + channels: Channel[]; +} + +const getChannelNames = async ( + guildId: string, + channelIds: string[] +): Promise => { + const channels: Channel[] = []; + try { + const response = await axios.get( + `/api/discord/channels?guildId=${guildId}` + ); + + const allChannels: Channel[] = response.data; + channelIds.forEach((channelId) => { + const channel = allChannels.find((c) => c.id === channelId); + if (channel) { + channels.push({ id: channel.id, name: channel.name }); + } + }); + } catch (error) { + console.error("Error fetching channels:", error); + } + return channels; +}; + +export default function ManageServerPage({ + params, +}: { + params: Promise<{ guildId: string }>; +}) { + const router = useRouter(); + const [guildData, setGuildData] = useState(null); + const [activeTab, setActiveTab] = useState("settings"); + + useEffect(() => { + const fetchParams = async () => { + if (params) { + const resolvedParams = await params; + const guildId = resolvedParams.guildId; + + const response = await fetch(`/api/discord/guilds`); + const serverSettings = await response.json(); + + const guildInfo = serverSettings.find( + (setting: ServerSetting) => setting.guildId === guildId + ); + + if (guildInfo) { + const channelIds = [ + guildInfo.countingChannelId, + guildInfo.generalChannelId, + guildInfo.logChannelId, + guildInfo.verificationChannelId, + guildInfo.actionItemsChannelId, + guildInfo.actionItemsTargetChannelId, + ]; + const channels = await getChannelNames(guildId, channelIds); + + setGuildData({ + guildId, + guildName: guildInfo.guildName, + guildIcon: guildInfo.guildIcon, + settings: guildInfo, + channels, + }); + } + } + }; + + fetchParams(); + }, [params]); + + const renderSettingsContent = () => { + if (!guildData?.settings) return
No settings found.
; + + const { + emailDomains, + countingChannelId, + generalChannelId, + logChannelId, + verificationChannelId, + verifiedRoleName, + actionItemsChannelId, + actionItemsTargetChannelId, + } = guildData.settings; + + const countingChannel = + guildData.channels?.find((c) => c.id === countingChannelId)?.name || + countingChannelId; + const generalChannel = + guildData.channels?.find((c) => c.id === generalChannelId)?.name || + generalChannelId; + const logChannel = + guildData.channels?.find((c) => c.id === logChannelId)?.name || + logChannelId; + const verificationChannel = + guildData.channels?.find((c) => c.id === verificationChannelId)?.name || + verificationChannelId; + const actionItemsChannel = + guildData.channels?.find((c) => c.id === actionItemsChannelId)?.name || + actionItemsChannelId; + const actionItemsTargetChannel = + guildData.channels?.find((c) => c.id === actionItemsTargetChannelId) + ?.name || actionItemsTargetChannelId; + + return ( +
+

Current Server Settings

+
+ Guild ID: {guildData.guildId} +
+
+ Email Domains: {emailDomains.join(", ") || "None"} +
+
+ Counting Channel: {countingChannel} +
+
+ General Channel: {generalChannel} +
+
+ Log Channel: {logChannel} +
+
+ Verification Channel: {verificationChannel} +
+
+ Verified Role Name: {verifiedRoleName} +
+
+ Action Items Channel: {actionItemsChannel} +
+
+ Action Items Target Channel:{" "} + {actionItemsTargetChannel} +
+
+ ); + }; + + const renderContent = () => { + switch (activeTab) { + case "settings": + return renderSettingsContent(); + case "roles": + return
Roles management for {guildData?.guildName}
; + case "channels": + return
Channels management for {guildData?.guildName}
; + case "logs": + return
Logs for {guildData?.guildName}
; + default: + return
Select a tab to manage.
; + } + }; + + return ( +
+ + +
+

+ Manage Server: {guildData ? guildData.guildName : "Loading..."} +

+ {guildData?.guildIcon && ( + {`${guildData.guildName} + )} + {renderContent()} + +
+
+ ); +} diff --git a/dashboard/app/admin/page.tsx b/dashboard/app/admin/page.tsx new file mode 100644 index 0000000..d04ab22 --- /dev/null +++ b/dashboard/app/admin/page.tsx @@ -0,0 +1,90 @@ +"use client"; + +import { useEffect, useState } from "react"; +import { useSession, signOut } from "next-auth/react"; +import { useRouter } from "next/navigation"; + +interface ServerSetting { + _id: string; + guildId: string; + guildName: string; + guildIcon: string | null; +} + +export default function AdminPage() { + const { data: session } = useSession(); + const [serverSettings, setServerSettings] = useState([]); + const [loading, setLoading] = useState(true); + const router = useRouter(); + + useEffect(() => { + if (!session) return; + + const fetchServerSettings = async () => { + try { + const response = await fetch("/api/discord/guilds"); + if (!response.ok) { + throw new Error("Failed to fetch server settings"); + } + const data = await response.json(); + console.log("Fetched Server Settings:", data); + setServerSettings(data); + } catch (error) { + console.error("Error fetching server settings:", error); + } finally { + setLoading(false); + } + }; + + fetchServerSettings(); + }, [session]); + + if (!session) { + return ( +
+

Access Denied

+

Please sign in to access this page.

+
+ ); + } + + const handleServerClick = (guildId: string) => { + router.push(`/admin/manage/${guildId}`); + }; + + return ( +
+

Welcome to the Admin Page!

+

You are signed in as: {session.user?.name}

+ + + {loading ? ( +

Loading server settings...

+ ) : ( +
+ {serverSettings.map((setting) => ( +
handleServerClick(setting.guildId)} + className="bg-gray-800 p-4 rounded shadow-md cursor-pointer hover:bg-gray-700 transition duration-200 flex flex-col items-center" + > + {setting.guildIcon && ( + {`${setting.guildName} + )} +

{setting.guildName}

+
+ ))} +
+ )} +
+ ); +} diff --git a/dashboard/app/api/auth/[...nextauth]/route.ts b/dashboard/app/api/auth/[...nextauth]/route.ts new file mode 100644 index 0000000..fba2cab --- /dev/null +++ b/dashboard/app/api/auth/[...nextauth]/route.ts @@ -0,0 +1,21 @@ +import NextAuth from "next-auth"; +import DiscordProvider from "next-auth/providers/discord"; + +export const authOptions = { + providers: [ + DiscordProvider({ + clientId: process.env.DISCORD_CLIENT_ID!, + clientSecret: process.env.DISCORD_CLIENT_SECRET!, + authorization: { + params: { + scope: "identify guilds", + }, + }, + }), + ], + secret: process.env.NEXTAUTH_SECRET, +}; + +const handler = NextAuth(authOptions); + +export { handler as GET, handler as POST }; diff --git a/dashboard/app/api/discord/channels/route.ts b/dashboard/app/api/discord/channels/route.ts new file mode 100644 index 0000000..4dd0b89 --- /dev/null +++ b/dashboard/app/api/discord/channels/route.ts @@ -0,0 +1,44 @@ +import { NextResponse } from "next/server"; +import axios from "axios"; +import { getServerSession } from "next-auth/next"; +import { authOptions } from "../../auth/[...nextauth]/route"; + +const DISCORD_API_BASE = "https://discord.com/api/v10"; +const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN; + +export async function GET(request: Request) { + const session = await getServerSession(authOptions); + + if (!session) { + return NextResponse.json( + { error: "Unauthorized access." }, + { status: 401 } + ); + } + + const url = new URL(request.url); + const guildId = url.searchParams.get("guildId"); + + if (!guildId || !BOT_TOKEN) { + return NextResponse.json( + { error: "Guild ID or Discord bot token not set." }, + { status: 400 } + ); + } + + try { + const response = await axios.get( + `${DISCORD_API_BASE}/guilds/${guildId}/channels`, + { + headers: { + Authorization: `Bot ${BOT_TOKEN}`, + }, + } + ); + + return NextResponse.json(response.data); + } catch (error) { + console.error("Error fetching channels from Discord API:", error); + return NextResponse.error(); + } +} diff --git a/dashboard/app/api/discord/guilds/route.ts b/dashboard/app/api/discord/guilds/route.ts new file mode 100644 index 0000000..e22980a --- /dev/null +++ b/dashboard/app/api/discord/guilds/route.ts @@ -0,0 +1,74 @@ +import { NextResponse } from "next/server"; +import clientPromise from "@/lib/mongodb"; +import axios from "axios"; + +interface ServerSetting { + _id: string; + guildId: string; + emailDomains: string[]; + countingChannelId: string; + generalChannelId: string; + logChannelId: string; + verificationChannelId: string; + verifiedRoleName: string; + actionItemsChannelId: string; + actionItemsTargetChannelId: string; +} + +const DISCORD_API_BASE = "https://discord.com/api/v10"; +const BOT_TOKEN = process.env.DISCORD_BOT_TOKEN; + +export async function GET() { + if (!BOT_TOKEN) { + return NextResponse.json( + { error: "Discord bot token not set." }, + { status: 500 } + ); + } + + try { + const client = await clientPromise; + const database = client.db("test"); + const collection = database.collection("serversettings"); + + const serverSettings = await collection.find({}).toArray(); + + const enrichedServerSettings = await Promise.all( + serverSettings.map(async (setting) => { + try { + const guildResponse = await axios.get( + `${DISCORD_API_BASE}/guilds/${setting.guildId}`, + { + headers: { + Authorization: `Bot ${BOT_TOKEN}`, + }, + } + ); + + const guildData = guildResponse.data; + + return { + ...setting, + guildName: guildData.name, + guildIcon: guildData.icon, + }; + } catch (error) { + console.error( + `Error fetching guild details for ${setting.guildId}:`, + error + ); + return { + ...setting, + guildName: "Unknown Guild", + guildIcon: null, + }; + } + }) + ); + + return NextResponse.json(enrichedServerSettings); + } catch (error) { + console.error("Error fetching server settings:", error); + return NextResponse.error(); + } +} diff --git a/dashboard/app/layout.tsx b/dashboard/app/layout.tsx index 43587da..7c459fd 100644 --- a/dashboard/app/layout.tsx +++ b/dashboard/app/layout.tsx @@ -3,6 +3,7 @@ import localFont from "next/font/local"; import "./globals.css"; import Navbar from "@/components/Navbar"; import Footer from "@/components/Footer"; +import ClientProvider from "@/components/ClientProvider"; const geistSans = localFont({ src: "./fonts/GeistVF.woff", @@ -26,14 +27,16 @@ export default function RootLayout({ children: React.ReactNode; }>) { return ( - - - - {children} -