From 7213f08b64a1aceb438f4a30de3911b75f8079f9 Mon Sep 17 00:00:00 2001 From: Christopher Date: Thu, 22 Jan 2026 04:26:23 -0800 Subject: [PATCH] feat: implement authentication context, admin node settings, and core layout components --- drizzle.config.ts | 4 + package-lock.json | 14 ++ package.json | 1 + src/app/[handle]/page.tsx | 43 +---- src/app/admin/page.tsx | 135 +++++++++++++- src/app/api/admin/node/route.ts | 47 +++++ src/app/api/node/route.ts | 32 ++++ src/app/explore/page.tsx | 43 +---- src/app/layout.tsx | 11 +- src/app/notifications/page.tsx | 176 +++---------------- src/app/page.tsx | 293 ++++++++----------------------- src/components/Icons.tsx | 103 +++++++++++ src/components/LayoutWrapper.tsx | 30 ++++ src/components/RightSidebar.tsx | 82 +++++++++ src/components/Sidebar.tsx | 71 ++++++++ src/db/schema.ts | 3 + src/lib/contexts/AuthContext.tsx | 72 ++++++++ 17 files changed, 698 insertions(+), 462 deletions(-) create mode 100644 src/app/api/admin/node/route.ts create mode 100644 src/app/api/node/route.ts create mode 100644 src/components/Icons.tsx create mode 100644 src/components/LayoutWrapper.tsx create mode 100644 src/components/RightSidebar.tsx create mode 100644 src/components/Sidebar.tsx create mode 100644 src/lib/contexts/AuthContext.tsx diff --git a/drizzle.config.ts b/drizzle.config.ts index 654e617..af7d04d 100644 --- a/drizzle.config.ts +++ b/drizzle.config.ts @@ -1,4 +1,8 @@ import { defineConfig } from 'drizzle-kit'; +import * as dotenv from 'dotenv'; + +dotenv.config({ path: '.env.local' }); +dotenv.config(); export default defineConfig({ schema: './src/db/schema.ts', diff --git a/package-lock.json b/package-lock.json index e06b1ed..ed5f52a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -30,6 +30,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "@types/uuid": "^10.0.0", + "dotenv": "^17.2.3", "drizzle-kit": "^0.31.8", "eslint": "^9", "eslint-config-next": "16.1.4", @@ -3835,6 +3836,19 @@ "node": ">=0.10.0" } }, + "node_modules/dotenv": { + "version": "17.2.3", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.2.3.tgz", + "integrity": "sha512-JVUnt+DUIzu87TABbhPmNfVdBDt18BLOWjMUFJMSi/Qqg7NTYtabbvSNJGOJ7afbRuv9D/lngizHtP7QyLQ+9w==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, "node_modules/drizzle-kit": { "version": "0.31.8", "resolved": "https://registry.npmjs.org/drizzle-kit/-/drizzle-kit-0.31.8.tgz", diff --git a/package.json b/package.json index 2567eb2..c0545b9 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,7 @@ "@types/react": "^19", "@types/react-dom": "^19", "@types/uuid": "^10.0.0", + "dotenv": "^17.2.3", "drizzle-kit": "^0.31.8", "eslint": "^9", "eslint-config-next": "16.1.4", diff --git a/src/app/[handle]/page.tsx b/src/app/[handle]/page.tsx index 552a3f5..5e8507b 100644 --- a/src/app/[handle]/page.tsx +++ b/src/app/[handle]/page.tsx @@ -3,6 +3,7 @@ import { useState, useEffect } from 'react'; import Link from 'next/link'; import { useParams } from 'next/navigation'; +import { ArrowLeftIcon, CalendarIcon, HeartIcon, RepeatIcon, MessageIcon, FlagIcon } from '@/components/Icons'; interface User { id: string; @@ -43,49 +44,7 @@ interface Post { } // Icons -const ArrowLeftIcon = () => ( - - - - -); -const CalendarIcon = () => ( - - - - - - -); - -const HeartIcon = ({ filled }: { filled?: boolean }) => ( - - - -); - -const RepeatIcon = () => ( - - - - - - -); - -const MessageIcon = () => ( - - - -); - -const FlagIcon = () => ( - - - - -); function UserRow({ user }: { user: UserSummary }) { return ( diff --git a/src/app/admin/page.tsx b/src/app/admin/page.tsx index 8e523d8..4d516d3 100644 --- a/src/app/admin/page.tsx +++ b/src/app/admin/page.tsx @@ -49,12 +49,20 @@ const formatDate = (value: string) => { export default function AdminPage() { const [isAdmin, setIsAdmin] = useState(null); - const [tab, setTab] = useState<'reports' | 'posts' | 'users'>('reports'); + const [tab, setTab] = useState<'reports' | 'posts' | 'users' | 'settings'>('reports'); const [reports, setReports] = useState([]); const [posts, setPosts] = useState([]); const [users, setUsers] = useState([]); const [loading, setLoading] = useState(false); const [reportStatus, setReportStatus] = useState<'open' | 'resolved' | 'all'>('open'); + const [nodeSettings, setNodeSettings] = useState({ + name: '', + description: '', + longDescription: '', + rules: '', + bannerUrl: '', + }); + const [savingSettings, setSavingSettings] = useState(false); useEffect(() => { fetch('/api/admin/me') @@ -102,11 +110,31 @@ export default function AdminPage() { } }; + const loadNodeSettings = async () => { + setLoading(true); + try { + const res = await fetch('/api/node'); + const data = await res.json(); + setNodeSettings({ + name: data.name || '', + description: data.description || '', + longDescription: data.longDescription || '', + rules: data.rules || '', + bannerUrl: data.bannerUrl || '', + }); + } catch { + // error + } finally { + setLoading(false); + } + }; + useEffect(() => { if (!isAdmin) return; if (tab === 'reports') loadReports(); if (tab === 'posts') loadPosts(); if (tab === 'users') loadUsers(); + if (tab === 'settings') loadNodeSettings(); }, [tab, isAdmin, reportStatus]); const handleReportResolve = async (id: string, status: 'open' | 'resolved') => { @@ -133,6 +161,26 @@ export default function AdminPage() { } }; + const handleSaveSettings = async () => { + setSavingSettings(true); + try { + const res = await fetch('/api/admin/node', { + method: 'PATCH', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(nodeSettings), + }); + if (res.ok) { + alert('Settings saved!'); + } else { + alert('Failed to save settings.'); + } + } catch { + alert('Failed to save settings.'); + } finally { + setSavingSettings(false); + } + }; + const handleUserAction = async (id: string, action: 'suspend' | 'unsuspend' | 'silence' | 'unsilence') => { const needsReason = action === 'suspend' || action === 'silence'; const reason = needsReason ? window.prompt('Reason (optional):') || '' : ''; @@ -195,6 +243,9 @@ export default function AdminPage() { + {tab === 'reports' && ( @@ -383,6 +434,88 @@ export default function AdminPage() { )} )} + {tab === 'settings' && ( +
+
Node Settings
+ + {loading ? ( +
Loading settings...
+ ) : ( +
+
+ + setNodeSettings({ ...nodeSettings, name: e.target.value })} + placeholder="My Synapsis Node" + /> +
+ +
+ +