From c71720454ffe1653166776f3629b6b0d870fc16f Mon Sep 17 00:00:00 2001 From: sebastjanartic <45803536-sebastjanartic@users.noreply.replit.com> Date: Tue, 2 Sep 2025 12:01:00 +0000 Subject: [PATCH] Add admin dashboard and Replit authentication integration Integrates Replit authentication using OpenID Connect, adds an admin dashboard route with video management and thumbnail upload capabilities, and updates dependencies. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 890577b1-c154-40a4-a177-a0c6d55320c3 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/8cc42625-c1f5-4e43-99bd-77f2c4dedee2/890577b1-c154-40a4-a177-a0c6d55320c3/1jMBtLj --- .replit | 1 + client/src/App.tsx | 2 + client/src/components/loading-spinner.tsx | 6 +- client/src/hooks/useAuth.ts | 16 + client/src/pages/admin.tsx | 354 ++++++++++++++++++++++ package-lock.json | 48 +-- package.json | 8 +- server/objectStorage.ts | 220 ++++++++++++++ server/replitAuth.ts | 172 +++++++++++ server/routes.ts | 109 +++++++ server/storage.ts | 52 ++++ shared/schema.ts | 18 +- 12 files changed, 975 insertions(+), 31 deletions(-) create mode 100644 client/src/hooks/useAuth.ts create mode 100644 client/src/pages/admin.tsx create mode 100644 server/objectStorage.ts create mode 100644 server/replitAuth.ts diff --git a/.replit b/.replit index c806144..10e1957 100644 --- a/.replit +++ b/.replit @@ -40,3 +40,4 @@ args = "npm run dev" waitForPort = 5000 [agent] +integrations = ["javascript_database==1.0.0", "javascript_log_in_with_replit==1.0.0", "javascript_object_storage==1.0.0"] diff --git a/client/src/App.tsx b/client/src/App.tsx index 747e541..ef9e63a 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -8,6 +8,7 @@ import VideoPage from "@/pages/VideoPage"; import FolxStadlPage from "@/pages/FolxStadlPage"; import GeschichteLiedPage from "@/pages/GeschichteLiedPage"; import GipfelstammtischPage from "@/pages/GipfelstammtischPage"; +import AdminPage from "@/pages/admin"; import NotFound from "@/pages/not-found"; function Router() { @@ -18,6 +19,7 @@ function Router() { + ); diff --git a/client/src/components/loading-spinner.tsx b/client/src/components/loading-spinner.tsx index b41497d..d0a5138 100644 --- a/client/src/components/loading-spinner.tsx +++ b/client/src/components/loading-spinner.tsx @@ -4,7 +4,7 @@ interface LoadingSpinnerProps { className?: string; } -export default function LoadingSpinner({ size = 'md', text = 'Loading...', className = '' }: LoadingSpinnerProps) { +export function LoadingSpinner({ size = 'md', text = 'Loading...', className = '' }: LoadingSpinnerProps) { const sizeClasses = { sm: 'w-6 h-6', md: 'w-10 h-10', @@ -25,4 +25,6 @@ export default function LoadingSpinner({ size = 'md', text = 'Loading...', class
{text}
); -} \ No newline at end of file +} + +export default LoadingSpinner; \ No newline at end of file diff --git a/client/src/hooks/useAuth.ts b/client/src/hooks/useAuth.ts new file mode 100644 index 0000000..d87fe27 --- /dev/null +++ b/client/src/hooks/useAuth.ts @@ -0,0 +1,16 @@ +import { useQuery } from "@tanstack/react-query"; + +export function useAuth() { + const { data: user, isLoading } = useQuery({ + queryKey: ["/api/auth/user"], + retry: false, + }); + + return { + user, + isLoading, + isAuthenticated: !!user, + isAdmin: user?.isAdmin || false, + isSuperAdmin: user?.isSuperAdmin || false, + }; +} \ No newline at end of file diff --git a/client/src/pages/admin.tsx b/client/src/pages/admin.tsx new file mode 100644 index 0000000..2c26030 --- /dev/null +++ b/client/src/pages/admin.tsx @@ -0,0 +1,354 @@ +import { useState } from "react"; +import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { useAuth } from "@/hooks/useAuth"; +import { Button } from "@/components/ui/button"; +import { Input } from "@/components/ui/input"; +import { Label } from "@/components/ui/label"; +import { Textarea } from "@/components/ui/textarea"; +import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; +import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogTrigger } from "@/components/ui/dialog"; +import { Badge } from "@/components/ui/badge"; +import { useToast } from "@/hooks/use-toast"; +import { LoadingSpinner } from "@/components/loading-spinner"; +import { apiRequest } from "@/lib/queryClient"; +import type { Video } from "@shared/schema"; +import { Shield, Edit, Upload, Search, Filter, Save, X } from "lucide-react"; + +export default function AdminPage() { + const { user, isLoading: authLoading, isAuthenticated, isAdmin } = useAuth(); + const { toast } = useToast(); + const queryClient = useQueryClient(); + + const [search, setSearch] = useState(""); + const [selectedVideo, setSelectedVideo] = useState