mirror of
https://github.com/freedbygrace/ActiveDirectoryManager.git
synced 2026-08-28 19:47:21 +00:00
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
This commit is contained in:
+46
@@ -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"]
|
||||||
+3
-1
@@ -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 { Toaster } from "@/components/ui/toaster";
|
||||||
import NotFound from "@/pages/not-found";
|
import NotFound from "@/pages/not-found";
|
||||||
import AuthPage from "@/pages/auth-page";
|
import AuthPage from "@/pages/auth-page";
|
||||||
@@ -13,6 +14,7 @@ import LdapConnectionsPage from "@/pages/ldap-connections-page";
|
|||||||
import SettingsPage from "@/pages/settings-page";
|
import SettingsPage from "@/pages/settings-page";
|
||||||
import UserManagementPage from "@/pages/user-management-page";
|
import UserManagementPage from "@/pages/user-management-page";
|
||||||
import { ProtectedRoute } from "./lib/protected-route";
|
import { ProtectedRoute } from "./lib/protected-route";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
|
||||||
function Router() {
|
function Router() {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
import React, { useState } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
import { Link, useLocation } from "wouter";
|
import { Link, useLocation } from "wouter";
|
||||||
import { useAuth } from "@/hooks/use-auth";
|
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
|
||||||
import { Separator } from "@/components/ui/separator";
|
import { Separator } from "@/components/ui/separator";
|
||||||
@@ -31,6 +30,7 @@ import {
|
|||||||
ShieldAlert,
|
ShieldAlert,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useMobile } from "@/hooks/use-mobile";
|
import { useMobile } from "@/hooks/use-mobile";
|
||||||
|
import { useToast } from "@/hooks/use-toast";
|
||||||
|
|
||||||
type MenuItem = {
|
type MenuItem = {
|
||||||
title: string;
|
title: string;
|
||||||
@@ -43,6 +43,14 @@ type MenuSection = {
|
|||||||
items: MenuItem[];
|
items: MenuItem[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type UserType = {
|
||||||
|
id: number;
|
||||||
|
username: string;
|
||||||
|
fullName?: string;
|
||||||
|
email?: string;
|
||||||
|
role?: string;
|
||||||
|
};
|
||||||
|
|
||||||
const menuSections: MenuSection[] = [
|
const menuSections: MenuSection[] = [
|
||||||
{
|
{
|
||||||
title: "Active Directory",
|
title: "Active Directory",
|
||||||
@@ -72,13 +80,60 @@ interface DashboardLayoutProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export function DashboardLayout({ children, title, description }: DashboardLayoutProps) {
|
export function DashboardLayout({ children, title, description }: DashboardLayoutProps) {
|
||||||
const [location] = useLocation();
|
const [location, navigate] = useLocation();
|
||||||
const { user, logoutMutation } = useAuth();
|
|
||||||
const isMobile = useMobile();
|
const isMobile = useMobile();
|
||||||
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
|
const [sidebarOpen, setSidebarOpen] = useState(!isMobile);
|
||||||
|
const [user, setUser] = useState<UserType | null>(null);
|
||||||
|
const { toast } = useToast();
|
||||||
|
|
||||||
const handleLogout = () => {
|
// Fetch user data on component mount
|
||||||
logoutMutation.mutate();
|
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
|
// Get user initials for avatar
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useAuth } from "@/hooks/use-auth";
|
import { useState, useEffect } from "react";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import { Redirect, Route } from "wouter";
|
import { Redirect, Route } from "wouter";
|
||||||
|
|
||||||
@@ -9,9 +9,30 @@ export function ProtectedRoute({
|
|||||||
path: string;
|
path: string;
|
||||||
component: () => React.JSX.Element;
|
component: () => React.JSX.Element;
|
||||||
}) {
|
}) {
|
||||||
const { user, isLoading } = useAuth();
|
const [isAuthenticated, setIsAuthenticated] = useState<boolean | null>(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 (
|
return (
|
||||||
<Route path={path}>
|
<Route path={path}>
|
||||||
<div className="flex items-center justify-center min-h-screen">
|
<div className="flex items-center justify-center min-h-screen">
|
||||||
@@ -21,7 +42,7 @@ export function ProtectedRoute({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!user) {
|
if (!isAuthenticated) {
|
||||||
return (
|
return (
|
||||||
<Route path={path}>
|
<Route path={path}>
|
||||||
<Redirect to="/auth" />
|
<Redirect to="/auth" />
|
||||||
|
|||||||
Reference in New Issue
Block a user