From f23f169ddbf11623cf1a1515e6d0a9f04527e001 Mon Sep 17 00:00:00 2001 From: alphaeusmote <41258468-alphaeusmote@users.noreply.replit.com> Date: Tue, 8 Apr 2025 02:06:32 +0000 Subject: [PATCH] Improve user authentication and add Docker support. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 705f2157-ef97-4fbd-89e4-8c7f2ecaea90 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7ed01c5f-a82d-405a-b728-b2e3d127c60c/3de0b07e-5376-463b-b11e-ad41b6fbed26.jpg --- Dockerfile | 46 +++++++++++++++++ client/src/App.tsx | 4 +- client/src/layouts/dashboard-layout.tsx | 67 ++++++++++++++++++++++--- client/src/lib/protected-route.tsx | 29 +++++++++-- 4 files changed, 135 insertions(+), 11 deletions(-) create mode 100644 Dockerfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..8838ae6 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,46 @@ +FROM node:20-slim AS builder + +WORKDIR /app + +# Copy package files +COPY package*.json ./ + +# Install dependencies +RUN npm ci + +# Copy the rest of the source code +COPY . . + +# Build the application +RUN npm run build + +# Production stage +FROM node:20-slim AS runner + +WORKDIR /app + +# Set environment to production +ENV NODE_ENV=production + +# Copy package files and install production dependencies +COPY package*.json ./ +RUN npm ci --production + +# Copy build artifacts from the builder stage +COPY --from=builder /app/dist ./dist + +# Copy schema files for Drizzle +COPY ./shared/schema.ts ./shared/ +COPY ./drizzle.config.ts ./ + +# Set the database URL environment variable (this will be overridden at runtime) +ENV DATABASE_URL=postgres://postgres:postgres@localhost:5432/postgres + +# Set session secret (should be overridden at runtime) +ENV SESSION_SECRET=changeme + +# Expose the port the app runs on +EXPOSE 5000 + +# Start the application +CMD ["node", "dist/index.js"] \ No newline at end of file diff --git a/client/src/App.tsx b/client/src/App.tsx index 0b24e82..5393fbf 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,4 +1,5 @@ -import { Switch, Route } from "wouter"; +import { useState, useEffect } from "react"; +import { Switch, Route, useLocation } from "wouter"; import { Toaster } from "@/components/ui/toaster"; import NotFound from "@/pages/not-found"; import AuthPage from "@/pages/auth-page"; @@ -13,6 +14,7 @@ import LdapConnectionsPage from "@/pages/ldap-connections-page"; import SettingsPage from "@/pages/settings-page"; import UserManagementPage from "@/pages/user-management-page"; import { ProtectedRoute } from "./lib/protected-route"; +import { Loader2 } from "lucide-react"; function Router() { return ( diff --git a/client/src/layouts/dashboard-layout.tsx b/client/src/layouts/dashboard-layout.tsx index cb754dd..53c9e95 100644 --- a/client/src/layouts/dashboard-layout.tsx +++ b/client/src/layouts/dashboard-layout.tsx @@ -1,6 +1,5 @@ -import React, { useState } from "react"; +import React, { useState, useEffect } from "react"; import { Link, useLocation } from "wouter"; -import { useAuth } from "@/hooks/use-auth"; import { Button } from "@/components/ui/button"; import { Avatar, AvatarFallback } from "@/components/ui/avatar"; import { Separator } from "@/components/ui/separator"; @@ -31,6 +30,7 @@ import { ShieldAlert, } from "lucide-react"; import { useMobile } from "@/hooks/use-mobile"; +import { useToast } from "@/hooks/use-toast"; type MenuItem = { title: string; @@ -43,6 +43,14 @@ type MenuSection = { items: MenuItem[]; }; +type UserType = { + id: number; + username: string; + fullName?: string; + email?: string; + role?: string; +}; + const menuSections: MenuSection[] = [ { title: "Active Directory", @@ -72,13 +80,60 @@ interface DashboardLayoutProps { } export function DashboardLayout({ children, title, description }: DashboardLayoutProps) { - const [location] = useLocation(); - const { user, logoutMutation } = useAuth(); + const [location, navigate] = useLocation(); const isMobile = useMobile(); const [sidebarOpen, setSidebarOpen] = useState(!isMobile); + const [user, setUser] = useState(null); + const { toast } = useToast(); - const handleLogout = () => { - logoutMutation.mutate(); + // Fetch user data on component mount + useEffect(() => { + const fetchUserData = async () => { + try { + const response = await fetch('/api/user', { + credentials: 'include' + }); + + if (response.ok) { + const userData = await response.json(); + setUser(userData); + } + } catch (error) { + console.error('Error fetching user data:', error); + } + }; + + fetchUserData(); + }, []); + + const handleLogout = async () => { + try { + const response = await fetch('/api/logout', { + method: 'POST', + credentials: 'include' + }); + + if (response.ok) { + toast({ + title: "Logged out", + description: "You have been successfully logged out.", + }); + navigate('/auth'); + } else { + toast({ + title: "Logout failed", + description: "An error occurred during logout.", + variant: "destructive", + }); + } + } catch (error) { + console.error('Error during logout:', error); + toast({ + title: "Logout failed", + description: "An error occurred during logout.", + variant: "destructive", + }); + } }; // Get user initials for avatar diff --git a/client/src/lib/protected-route.tsx b/client/src/lib/protected-route.tsx index d2b7312..a2b91f3 100644 --- a/client/src/lib/protected-route.tsx +++ b/client/src/lib/protected-route.tsx @@ -1,4 +1,4 @@ -import { useAuth } from "@/hooks/use-auth"; +import { useState, useEffect } from "react"; import { Loader2 } from "lucide-react"; import { Redirect, Route } from "wouter"; @@ -9,9 +9,30 @@ export function ProtectedRoute({ path: string; component: () => React.JSX.Element; }) { - const { user, isLoading } = useAuth(); + const [isAuthenticated, setIsAuthenticated] = useState(null); + + useEffect(() => { + const checkAuth = async () => { + try { + const response = await fetch('/api/user', { + credentials: 'include' + }); + + if (response.ok) { + setIsAuthenticated(true); + } else { + setIsAuthenticated(false); + } + } catch (error) { + console.error('Error checking auth status:', error); + setIsAuthenticated(false); + } + }; + + checkAuth(); + }, []); - if (isLoading) { + if (isAuthenticated === null) { return (
@@ -21,7 +42,7 @@ export function ProtectedRoute({ ); } - if (!user) { + if (!isAuthenticated) { return (