feat: Foundation — auth system, 9 migrations, React frontend
Backend: - Express server with JWT httpOnly cookie auth - POST /api/auth/register, /api/auth/login, /api/auth/logout, GET /api/auth/me - bcrypt 12 rounds, generic 401 errors (no email/password field disclosure) - Auth middleware protects all /api/* routes except register/login - pg Pool database connection Frontend (React + Vite + TailwindCSS + shadcn/ui): - AuthContext with session restore on page load via /api/auth/me - ProtectedRoute redirects unauthenticated users to /login - LoginPage, RegisterPage — Hebrew RTL layout (dir=rtl), inline validation - DashboardPage placeholder - shadcn/ui components: Button, Input, Label, Card Database: - 9 migrations (001-009): extensions, users, events, vendors, guests, bookings, invitations, vendor_ratings, organizer_preferences - pg_trgm for fuzzy Hebrew search, GIN indexes on style_tags - Phase 2+3 fields included: source, payment_status, contract_value, vendor ratings 6-dimension, organizer preferences - Idempotent migration runner with schema_migrations tracking table Infrastructure: - Dockerfile (multi-stage: build React → production node:20-alpine) - docker-compose.yml with PostgreSQL healthcheck, expose not ports - Migrations run automatically on container start Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
75
client/src/contexts/AuthContext.tsx
Normal file
75
client/src/contexts/AuthContext.tsx
Normal file
@@ -0,0 +1,75 @@
|
||||
import React, { createContext, useContext, useEffect, useState } from 'react';
|
||||
|
||||
interface User {
|
||||
id: string;
|
||||
email: string;
|
||||
display_name: string;
|
||||
role: 'organizer' | 'vendor';
|
||||
}
|
||||
|
||||
interface AuthContextValue {
|
||||
user: User | null;
|
||||
loading: boolean;
|
||||
login: (email: string, password: string) => Promise<void>;
|
||||
register: (email: string, password: string, displayName: string, role?: string) => Promise<void>;
|
||||
logout: () => Promise<void>;
|
||||
}
|
||||
|
||||
const AuthContext = createContext<AuthContextValue | null>(null);
|
||||
|
||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||
const [user, setUser] = useState<User | null>(null);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
||||
// Restore session on mount
|
||||
useEffect(() => {
|
||||
fetch('/api/auth/me', { credentials: 'include' })
|
||||
.then(res => res.ok ? res.json() : null)
|
||||
.then(data => {
|
||||
if (data?.user) setUser(data.user);
|
||||
})
|
||||
.catch(() => {})
|
||||
.finally(() => setLoading(false));
|
||||
}, []);
|
||||
|
||||
async function login(email: string, password: string) {
|
||||
const res = await fetch('/api/auth/login', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'התחברות נכשלה');
|
||||
setUser(data.user);
|
||||
}
|
||||
|
||||
async function register(email: string, password: string, displayName: string, role = 'organizer') {
|
||||
const res = await fetch('/api/auth/register', {
|
||||
method: 'POST',
|
||||
credentials: 'include',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({ email, password, display_name: displayName, role }),
|
||||
});
|
||||
const data = await res.json();
|
||||
if (!res.ok) throw new Error(data.error || 'הרשמה נכשלה');
|
||||
setUser(data.user);
|
||||
}
|
||||
|
||||
async function logout() {
|
||||
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
|
||||
setUser(null);
|
||||
}
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, loading, login, register, logout }}>
|
||||
{children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useAuth() {
|
||||
const ctx = useContext(AuthContext);
|
||||
if (!ctx) throw new Error('useAuth must be used within AuthProvider');
|
||||
return ctx;
|
||||
}
|
||||
Reference in New Issue
Block a user