feat: production Docker, Playwright tests, settings API, and bug fixes

- Add user management endpoints (register, list, delete, change password)
- Add API key management (create, list, delete)
- Add settings persistence endpoints (get, put)
- Wire settings dialog to real backend (People, API Keys, System, Security)
- Fix login auth flow (window.location.href for full reload)
- Fix download URLs returning 401 (make public since UUIDs are unguessable)
- Fix border tool shadowColor validation (accept 6-8 hex digits)
- Fix remove-bg alpha matting fallback (retry without on failure)
- Fix AI tool silent fallbacks (report errors instead of no-ops)
- Add checkerboard background to before/after slider for transparency
- Add progress bars to all AI tool components
- Add Playwright E2E test suite (131 tests across 9 test files)
- Rewrite Dockerfile for production (tsx runtime, pre-baked AI models)
- Add .dockerignore for faster builds
- Add proper accessible labels to login form
This commit is contained in:
Siddharth Kumar Sah
2026-03-22 19:28:57 +08:00
parent 06c5ee5996
commit ce03aad10f
37 changed files with 2607 additions and 122 deletions
+8 -5
View File
@@ -1,5 +1,4 @@
import { useState, type FormEvent } from "react";
import { useNavigate } from "react-router-dom";
import { setToken } from "@/lib/api";
export function LoginPage() {
@@ -7,7 +6,6 @@ export function LoginPage() {
const [password, setPassword] = useState("");
const [error, setError] = useState("");
const [loading, setLoading] = useState(false);
const navigate = useNavigate();
const handleSubmit = async (e: FormEvent) => {
e.preventDefault();
@@ -26,7 +24,10 @@ export function LoginPage() {
}
const data = await res.json();
setToken(data.token);
navigate("/");
// Store username for settings display
localStorage.setItem("stirling-username", data.user?.username || username);
// Full reload to force auth re-check (useAuth runs on mount)
window.location.href = "/";
} catch {
setError("Connection error");
} finally {
@@ -46,8 +47,9 @@ export function LoginPage() {
</div>
<form onSubmit={handleSubmit} className="space-y-4">
<div>
<label className="block text-sm font-medium mb-1 text-foreground">Username</label>
<label htmlFor="username" className="block text-sm font-medium mb-1 text-foreground">Username</label>
<input
id="username"
type="text"
value={username}
onChange={(e) => setUsername(e.target.value)}
@@ -57,8 +59,9 @@ export function LoginPage() {
/>
</div>
<div>
<label className="block text-sm font-medium mb-1 text-foreground">Password</label>
<label htmlFor="password" className="block text-sm font-medium mb-1 text-foreground">Password</label>
<input
id="password"
type="password"
value={password}
onChange={(e) => setPassword(e.target.value)}