Initial commit - Project start (July 17, 2025)
Mirror to GitHub / mirror (push) Successful in 26s
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m28s
Version and Release / version-bump (push) Successful in 32s
Version and Release / trigger-drone (push) Has been skipped

Original: feat: enhance security logging and ensure rate limit blocks are properly tracked

- Add comprehensive logging for rate limit blocks with full request details
  - IP address (with proper proxy detection), user agent, headers, timestamps
  - Rate limit info (current count, limit, remaining, reset time)
  - Separate tracking for auth vs general endpoints

- Enhance authentication failure logging
  - JWT validation failures with detailed error info
  - Admin auth attempts without token
  - Failed token validation with user context
  - All events include IP, path, method, user agent

- Improve Winston logger configuration for production
  - Add automatic log rotation (10MB errors, 50MB combined)
  - Create separate security.log for auth/rate limit events
  - Ensure logs directory exists automatically
  - Add structured JSON format for log aggregation
  - Support container logging with LOG_TO_CONSOLE env var

- Create comprehensive documentation
  - Security logging guide with examples
  - Monitoring recommendations
  - Configuration reference

- Add test script to verify logging functionality

All rate limit settings remain configurable via admin panel:
- Window duration, max requests, auth limits
- Skip authenticated requests option
- Public endpoints only option

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-07-18 19:25:15 +02:00
commit 1773ed5f95
354 changed files with 61508 additions and 0 deletions
+127
View File
@@ -0,0 +1,127 @@
// Event/Gallery types
export interface Event {
id: number;
slug: string;
event_type: string;
event_name: string;
event_date: string;
host_name?: string;
host_email: string;
admin_email: string;
welcome_message?: string;
color_theme?: string;
share_link: string;
created_at: string;
expires_at: string;
is_active: boolean;
is_archived: boolean;
archive_path?: string;
archived_at?: string;
photo_count?: number;
total_size?: number;
recent_photos?: Array<{
filename: string;
type: string;
size_bytes: number;
uploaded_at: string;
}>;
allow_user_uploads?: boolean;
upload_category_id?: number | null;
hero_photo_id?: number | null;
total_views?: number;
total_downloads?: number;
unique_visitors?: number;
}
export interface GalleryInfo {
event_name: string;
event_type: string;
event_date: string;
expires_at: string;
is_active: boolean;
is_expired: boolean;
requires_password?: boolean;
color_theme?: string;
}
export interface Photo {
id: number;
filename: string;
url: string;
thumbnail_url?: string;
type: 'collage' | 'individual';
category_id?: number;
category_name?: string;
category_slug?: string;
size: number;
uploaded_at: string;
}
export interface PhotoCategory {
id: number;
name: string;
slug: string;
is_global: boolean;
}
export interface GalleryData {
event: {
id: number;
event_name: string;
event_type: string;
event_date: string;
welcome_message?: string;
color_theme?: string;
expires_at: string;
allow_user_uploads?: boolean;
upload_category_id?: number | null;
hero_photo_id?: number | null;
};
categories?: PhotoCategory[];
photos: Photo[];
}
export interface GalleryStats {
total_photos: number;
total_views: number;
total_downloads: number;
unique_visitors: number;
}
// Auth types
export interface AdminUser {
id: number;
username: string;
email: string;
}
export interface LoginResponse {
token: string;
user: AdminUser;
}
export interface GalleryAuthResponse {
token: string;
event: {
id: number;
event_name: string;
event_type: string;
event_date: string;
welcome_message?: string;
color_theme?: string;
expires_at: string;
allow_user_uploads?: boolean;
upload_category_id?: number | null;
};
}
// API Error type
export interface ApiError {
error: string;
errors?: Array<{
type: string;
msg: string;
path: string;
location: string;
}>;
}
+222
View File
@@ -0,0 +1,222 @@
// Gallery Layout Types
export type GalleryLayoutType = 'grid' | 'masonry' | 'carousel' | 'timeline' | 'hero' | 'mosaic';
export interface GalleryLayoutSettings {
// Common settings
spacing?: 'tight' | 'normal' | 'relaxed';
photoAnimation?: 'none' | 'fade' | 'scale' | 'slide';
photoShape?: 'square' | 'rounded' | 'circle';
// Grid specific
gridColumns?: {
mobile: number;
tablet: number;
desktop: number;
};
// Masonry specific
masonryGutter?: number;
// Carousel specific
carouselAutoplay?: boolean;
carouselInterval?: number;
carouselShowThumbnails?: boolean;
// Timeline specific
timelineGrouping?: 'day' | 'week' | 'month';
timelineShowDates?: boolean;
// Hero specific
heroImageId?: number;
heroImagePosition?: 'top' | 'center' | 'bottom';
heroOverlayOpacity?: number;
// Mosaic specific
mosaicPattern?: 'random' | 'structured' | 'alternating';
}
export interface ThemeConfig {
// Colors
primaryColor?: string;
accentColor?: string;
backgroundColor?: string;
textColor?: string;
// Typography
fontFamily?: string;
headingFontFamily?: string;
fontSize?: 'small' | 'normal' | 'large';
// Styling
borderRadius?: 'none' | 'sm' | 'md' | 'lg';
buttonStyle?: 'solid' | 'outline' | 'ghost';
shadowStyle?: 'none' | 'subtle' | 'normal' | 'dramatic';
// Gallery Layout
galleryLayout?: GalleryLayoutType;
gallerySettings?: GalleryLayoutSettings;
// Header/Footer
headerStyle?: 'minimal' | 'standard' | 'full';
footerStyle?: 'minimal' | 'standard' | 'full';
showEventInfo?: boolean;
showBranding?: boolean;
// Advanced
logoUrl?: string;
customCss?: string;
backgroundPattern?: 'none' | 'dots' | 'grid' | 'waves';
}
export interface EventTheme {
id?: string;
name: string;
description?: string;
thumbnail?: string;
config: ThemeConfig;
isPreset?: boolean;
}
// Preset theme definitions
export const GALLERY_THEME_PRESETS: Record<string, EventTheme> = {
default: {
name: 'Classic Grid',
description: 'Clean and organized grid layout',
config: {
primaryColor: '#5C8762',
accentColor: '#22c55e',
backgroundColor: '#fafafa',
textColor: '#171717',
borderRadius: 'md',
galleryLayout: 'grid',
gallerySettings: {
spacing: 'normal',
photoAnimation: 'fade',
gridColumns: { mobile: 2, tablet: 3, desktop: 4 }
},
headerStyle: 'standard',
footerStyle: 'standard'
},
isPreset: true
},
elegantWedding: {
name: 'Elegant Wedding',
description: 'Sophisticated layout with hero image and timeline',
config: {
primaryColor: '#c9a961',
accentColor: '#e6ddd4',
backgroundColor: '#fdfcfb',
textColor: '#3f3f3f',
fontFamily: 'Playfair Display, serif',
headingFontFamily: 'Playfair Display, serif',
borderRadius: 'lg',
shadowStyle: 'subtle',
galleryLayout: 'hero',
gallerySettings: {
spacing: 'relaxed',
photoAnimation: 'scale',
photoShape: 'rounded',
heroOverlayOpacity: 0.3
},
headerStyle: 'full',
footerStyle: 'minimal'
},
isPreset: true
},
modernMasonry: {
name: 'Modern Masonry',
description: 'Pinterest-style dynamic layout',
config: {
primaryColor: '#3b82f6',
accentColor: '#1e40af',
backgroundColor: '#ffffff',
textColor: '#0f172a',
fontFamily: 'Inter, sans-serif',
borderRadius: 'sm',
galleryLayout: 'masonry',
gallerySettings: {
spacing: 'tight',
photoAnimation: 'fade',
masonryGutter: 16
},
headerStyle: 'minimal',
footerStyle: 'minimal',
shadowStyle: 'normal'
},
isPreset: true
},
birthdayFun: {
name: 'Birthday Celebration',
description: 'Vibrant carousel with playful animations',
config: {
primaryColor: '#ec4899',
accentColor: '#fbbf24',
backgroundColor: '#fef3c7',
textColor: '#451a03',
fontFamily: 'Comic Neue, cursive',
borderRadius: 'lg',
galleryLayout: 'carousel',
gallerySettings: {
spacing: 'normal',
photoAnimation: 'slide',
carouselAutoplay: true,
carouselInterval: 5000,
carouselShowThumbnails: true
},
headerStyle: 'full',
footerStyle: 'standard',
backgroundPattern: 'dots'
},
isPreset: true
},
corporateTimeline: {
name: 'Corporate Timeline',
description: 'Professional chronological layout',
config: {
primaryColor: '#1f2937',
accentColor: '#059669',
backgroundColor: '#f9fafb',
textColor: '#111827',
fontFamily: 'IBM Plex Sans, sans-serif',
borderRadius: 'sm',
galleryLayout: 'timeline',
gallerySettings: {
spacing: 'normal',
photoAnimation: 'none',
timelineGrouping: 'day',
timelineShowDates: true
},
headerStyle: 'standard',
footerStyle: 'full',
buttonStyle: 'outline'
},
isPreset: true
},
artisticMosaic: {
name: 'Artistic Mosaic',
description: 'Creative layout with varied photo sizes',
config: {
primaryColor: '#7c3aed',
accentColor: '#f59e0b',
backgroundColor: '#faf5ff',
textColor: '#1e1b4b',
fontFamily: 'Montserrat, sans-serif',
borderRadius: 'none',
galleryLayout: 'mosaic',
gallerySettings: {
spacing: 'tight',
photoAnimation: 'scale',
mosaicPattern: 'structured'
},
headerStyle: 'minimal',
footerStyle: 'minimal',
shadowStyle: 'dramatic'
},
isPreset: true
}
};