Add local user authentication and admin capabilities
Implement local user authentication with password hashing, add admin roles for user management and audit log viewing, and introduce audit logging for critical actions. Replit-Commit-Author: Agent Replit-Commit-Session-Id: 776963d0-f75d-42e2-a57b-cc36bdff8495 Replit-Commit-Checkpoint-Type: full_checkpoint Replit-Commit-Event-Id: 832a44ff-12ae-4096-8a0d-666ec083d536 Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/0683fb79-a27c-485c-9333-5f4b288c4567/776963d0-f75d-42e2-a57b-cc36bdff8495/1p7jhzu Replit-Helium-Checkpoint-Created: true
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
import { useState } from "react";
|
||||
import { useLocation, useSearch } from "wouter";
|
||||
import { useLocalLogin } from "@workspace/api-client-react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Wrench, AlertCircle } from "lucide-react";
|
||||
|
||||
export default function Login() {
|
||||
const [, setLocation] = useLocation();
|
||||
const search = useSearch();
|
||||
const params = new URLSearchParams(search);
|
||||
const returnTo = params.get("returnTo") || "/";
|
||||
|
||||
const [username, setUsername] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
const queryClient = useQueryClient();
|
||||
const localLogin = useLocalLogin();
|
||||
|
||||
const handleSubmit = (e: React.FormEvent) => {
|
||||
e.preventDefault();
|
||||
setError(null);
|
||||
localLogin.mutate(
|
||||
{ data: { username, password } },
|
||||
{
|
||||
onSuccess: () => {
|
||||
queryClient.invalidateQueries();
|
||||
setLocation(returnTo);
|
||||
},
|
||||
onError: (err) => {
|
||||
setError(err.data?.error || err.message || "Invalid username or password");
|
||||
},
|
||||
},
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex items-center justify-center p-4">
|
||||
<div className="w-full max-w-sm space-y-6">
|
||||
<div className="flex flex-col items-center gap-2 text-center">
|
||||
<div className="flex items-center gap-2 text-primary font-bold text-2xl">
|
||||
<Wrench className="w-7 h-7" />
|
||||
<span>ToolRate</span>
|
||||
</div>
|
||||
<p className="text-muted-foreground text-sm">Sign in to your account</p>
|
||||
</div>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="pb-4">
|
||||
<CardTitle className="text-lg">Sign in</CardTitle>
|
||||
<CardDescription>Enter your credentials to continue</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<form onSubmit={handleSubmit} className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="username">Username</Label>
|
||||
<Input
|
||||
id="username"
|
||||
type="text"
|
||||
value={username}
|
||||
onChange={(e) => setUsername(e.target.value)}
|
||||
placeholder="admin"
|
||||
autoFocus
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="password">Password</Label>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
{error && (
|
||||
<div className="flex items-center gap-2 text-destructive text-sm rounded-md bg-destructive/10 px-3 py-2">
|
||||
<AlertCircle className="w-4 h-4 shrink-0" />
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<Button
|
||||
type="submit"
|
||||
className="w-full"
|
||||
disabled={localLogin.isPending}
|
||||
>
|
||||
{localLogin.isPending ? "Signing in…" : "Sign in"}
|
||||
</Button>
|
||||
</form>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user