Add complete frontend implementation and Docker deployment setup
- Implement React frontend with TypeScript and Tailwind CSS - Add scrappbook.de-inspired UI design with photo galleries - Implement authentication, photo viewing, and download features - Add Docker Swarm configuration with Traefik reverse proxy - Set up Drone CI/CD pipeline for automated deployments - Add monitoring stack with Prometheus and Grafana - Create comprehensive deployment documentation - Add simple local development setup with docker-compose.local.yml Features: - Password-protected galleries with expiration warnings - Responsive photo grid with lightbox viewer - Bulk download functionality - Hot reload development environment - Email testing with Mailhog - Production-ready deployment scripts 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { getAuthToken } from '../config/api';
|
||||
import { authService } from '../services';
|
||||
import type { AdminUser } from '../types';
|
||||
|
||||
interface AdminAuthContextType {
|
||||
isAuthenticated: boolean;
|
||||
user: AdminUser | null;
|
||||
login: (username: string, password: string) => Promise<void>;
|
||||
logout: () => void;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const AdminAuthContext = createContext<AdminAuthContextType | undefined>(undefined);
|
||||
|
||||
export const useAdminAuth = () => {
|
||||
const context = useContext(AdminAuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useAdminAuth must be used within an AdminAuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface AdminAuthProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const AdminAuthProvider: React.FC<AdminAuthProviderProps> = ({ children }) => {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [user, setUser] = useState<AdminUser | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has a valid token on mount
|
||||
const token = getAuthToken(true);
|
||||
if (token) {
|
||||
// TODO: Validate token with backend and get user info
|
||||
setIsAuthenticated(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
const login = async (username: string, password: string) => {
|
||||
try {
|
||||
setError(null);
|
||||
setIsLoading(true);
|
||||
const response = await authService.adminLogin(username, password);
|
||||
setUser(response.user);
|
||||
setIsAuthenticated(true);
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Invalid credentials');
|
||||
throw err;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
authService.adminLogout();
|
||||
setIsAuthenticated(false);
|
||||
setUser(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<AdminAuthContext.Provider
|
||||
value={{
|
||||
isAuthenticated,
|
||||
user,
|
||||
login,
|
||||
logout,
|
||||
isLoading,
|
||||
error,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</AdminAuthContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
import React, { createContext, useContext, useState, useEffect } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import { getAuthToken } from '../config/api';
|
||||
import { authService } from '../services';
|
||||
|
||||
interface GalleryEvent {
|
||||
id: number;
|
||||
event_name: string;
|
||||
event_type: string;
|
||||
event_date: string;
|
||||
welcome_message?: string;
|
||||
color_theme?: string;
|
||||
expires_at: string;
|
||||
}
|
||||
|
||||
interface GalleryAuthContextType {
|
||||
isAuthenticated: boolean;
|
||||
event: GalleryEvent | null;
|
||||
login: (slug: string, password: string) => Promise<void>;
|
||||
logout: () => void;
|
||||
isLoading: boolean;
|
||||
error: string | null;
|
||||
}
|
||||
|
||||
const GalleryAuthContext = createContext<GalleryAuthContextType | undefined>(undefined);
|
||||
|
||||
export const useGalleryAuth = () => {
|
||||
const context = useContext(GalleryAuthContext);
|
||||
if (!context) {
|
||||
throw new Error('useGalleryAuth must be used within a GalleryAuthProvider');
|
||||
}
|
||||
return context;
|
||||
};
|
||||
|
||||
interface GalleryAuthProviderProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
|
||||
export const GalleryAuthProvider: React.FC<GalleryAuthProviderProps> = ({ children }) => {
|
||||
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
||||
const [event, setEvent] = useState<GalleryEvent | null>(null);
|
||||
const [isLoading, setIsLoading] = useState(true);
|
||||
const [error, setError] = useState<string | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
// Check if user has a valid token on mount
|
||||
const token = getAuthToken(false);
|
||||
if (token) {
|
||||
// TODO: Validate token with backend
|
||||
setIsAuthenticated(true);
|
||||
}
|
||||
setIsLoading(false);
|
||||
}, []);
|
||||
|
||||
const login = async (slug: string, password: string) => {
|
||||
try {
|
||||
setError(null);
|
||||
setIsLoading(true);
|
||||
const response = await authService.verifyGalleryPassword(slug, password);
|
||||
setEvent(response.event);
|
||||
setIsAuthenticated(true);
|
||||
} catch (err: any) {
|
||||
setError(err.response?.data?.error || 'Invalid password');
|
||||
throw err;
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
authService.galleryLogout();
|
||||
setIsAuthenticated(false);
|
||||
setEvent(null);
|
||||
};
|
||||
|
||||
return (
|
||||
<GalleryAuthContext.Provider
|
||||
value={{
|
||||
isAuthenticated,
|
||||
event,
|
||||
login,
|
||||
logout,
|
||||
isLoading,
|
||||
error,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</GalleryAuthContext.Provider>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,2 @@
|
||||
export { GalleryAuthProvider, useGalleryAuth } from './GalleryAuthContext';
|
||||
export { AdminAuthProvider, useAdminAuth } from './AdminAuthContext';
|
||||
Reference in New Issue
Block a user