# Image Uploader with Multi-Upload & Slideshow

A self-hosted image uploader with multi-image upload capabilities and automatic slideshow functionality.

## Features

**Multi-Image Upload**: Upload multiple images at once with batch processing  
**Telegram Notifications**: 🆕 Real-time notifications for uploads, consent changes, deletions, and daily warnings  
**Social Media Consent Management**: 🆕 GDPR-compliant consent system for workshop display and social media publishing  
**Automatic Cleanup**: 🆕 Unapproved groups are automatically deleted after 7 days  
**Deletion Log**: 🆕 Complete audit trail of automatically deleted content  
**Drag-and-Drop Reordering**: 🆕 User during upload and admins can reorder images via intuitive drag-and-drop interface  
**Slideshow Mode**: Automatic fullscreen slideshow with smooth transitions (respects custom ordering)  
**Preview Image Optimization**: Automatic thumbnail generation for faster gallery loading (96-98% size reduction)  
**Touch-Friendly Interface**: 🆕 Mobile-optimized drag handles and responsive design  
**Moderation Panel**: Dedicated moderation interface with consent filtering and export  
**Persistent Storage**: Docker volumes ensure data persistence across restarts  
**Clean UI**: Minimalist design focused on user experience  
**Self-Hosted**: Complete control over your data and infrastructure  

## What's New
This project extends the original [Image-Uploader by vallezw](https://github.com/vallezw/Image-Uploader) with enhanced multi-upload and slideshow capabilities.

### 🆕 Latest Features (November 2025)

- **📱 Telegram Bot Notifications** (Nov 30):
  - Real-time notifications for all critical events
  - 4 notification types: Upload, Consent Changes, Group Deletion, Daily Warnings
  - Upload notifications with name, year, title, image count, and consent status
  - Consent change tracking (workshop display + social media platforms)
  - Group deletion confirmations with uploader and statistics
  - Daily deletion warnings (09:00) for groups pending auto-cleanup (24h notice)
  - Cron-scheduled automation via node-cron
  - Admin endpoint for manual trigger: `POST /api/admin/telegram/warning`
  - Optional feature via `TELEGRAM_ENABLED` environment variable
  - Complete setup guide in `scripts/README.telegram.md`

- **🌐 Public/Internal Host Separation** (Nov 25):
  - Subdomain-based feature separation for production deployment
  - Public host (`deinprojekt.hobbyhimmel.de`): Upload + UUID Management only
  - Internal host (`deinprojekt.lan.hobbyhimmel.de`): Full admin access
  - Frontend code splitting with React.lazy() for optimized bundle size
  - Backend API protection via hostGate middleware
  - Rate limiting: 20 uploads/hour on public host
  - Audit log tracking with source host information
  - Complete local testing support via /etc/hosts entries
  - Zero configuration overhead for single-host deployments

- **🧪 Comprehensive Test Suite** (Nov 16):
  - 45 automated tests covering all API endpoints (100% passing)
  - Jest + Supertest integration testing framework
  - Unit tests for authentication middleware
  - API tests for admin, consent, migration, and upload endpoints
  - In-memory SQLite database for isolated testing
  - Coverage: 26% statements, 15% branches (realistic starting point)
  - Test execution time: ~10 seconds for full suite
  - CI/CD ready with proper teardown and cleanup

- **🔒 Admin Session Authentication** (Nov 16):
  - Server-managed HTTP sessions for all admin/system endpoints
  - CSRF protection on every mutating request via `X-CSRF-Token`
  - Secure `ADMIN_SESSION_SECRET` configuration keeps cookies tamper-proof
  - Protected routes: `/api/admin/*`, `/api/system/migration/migrate`, `/api/system/migration/rollback`
  - Session-aware moderation UI with login + first-admin setup wizard
  - Complete authentication documentation in `AUTHENTICATION.md`

- **📋 API Route Documentation** (Nov 16):
  - Single Source of Truth: `backend/src/routes/routeMappings.js`
  - Comprehensive route overview in `backend/src/routes/README.md`
  - Critical Express routing order documented (specific before generic)
  - Frontend-ready route reference with authentication requirements
  - OpenAPI specification auto-generation integrated

- **🔐 Social Media Consent Management** (Phase 1 Complete - Nov 9-10):
  - GDPR-compliant consent system for image usage
  - Mandatory workshop display consent (no upload without approval)
  - Optional per-platform consents (Facebook, Instagram, TikTok)
  - Consent badges and filtering in moderation panel
  - CSV/JSON export for legal documentation
  - Group ID tracking for consent withdrawal requests
- **🔑 Self-Service Management Portal** (Phase 2 Complete - Nov 11-15):
  - Secure UUID-based management tokens for user self-service
  - Frontend portal at `/manage/:token` for consent management
  - Revoke/restore consents for workshop and social media
  - Edit metadata (title, description) after upload
  - Add/delete images after upload (with moderation re-approval)
  - Complete group deletion with audit trail
  - IP-based rate limiting (10 requests/hour)
  - Brute-force protection (20 failed attempts → 24h ban)
  - Management audit log for security tracking
- **🎨 Modular UI Architecture** (Nov 15):
  - Reusable components: ConsentManager, GroupMetadataEditor, ImageDescriptionManager
  - Multi-mode support: upload/edit/moderate modes for maximum reusability
  - Code reduction: 62% in ModerationGroupImagesPage (281→107 lines)
  - Consistent design: HTML buttons, Paper boxes, Material-UI Alerts
  - Individual save/discard per component section
  - Zero code duplication between pages
- **� Slideshow Optimization**: Intelligent image preloading eliminates loading delays and duplicate images
- **📅 Chronological Display**: Slideshows now play in chronological order (year → upload date)
- **Automatic Cleanup**: Unapproved groups are automatically deleted after 7 days
- **Deletion Log**: Complete audit trail with statistics (groups, images, storage freed)
- **Countdown Display**: Visual indicator showing days until automatic deletion
- **Approval Feedback**: SweetAlert2 notifications for moderation actions
- **Manual Cleanup Trigger**: Admin API endpoints for testing and manual cleanup
- **Image Descriptions**: Add optional descriptions to individual images (max 200 characters)
- **Edit Mode**: Edit descriptions for uploaded images in upload preview and moderation interface
- **Slideshow Display**: Image descriptions shown as overlays during slideshow presentation
- **Public Display**: Descriptions visible in public group views and galleries

### Previous Features (October 2025)
- **Drag-and-Drop Image Reordering**: Admins can now reorder images using intuitive drag-and-drop
- **Touch-Friendly Interface**: Mobile-optimized controls with always-visible drag handles  
- **Slideshow Integration**: Custom image order automatically applies to slideshow mode
- **Optimistic UI Updates**: Immediate visual feedback with error recovery
- **Comprehensive Admin Panel**: Dedicated moderation interface for content curation

### Core Features
- Multi-image batch upload with progress tracking
- Automatic slideshow presentation mode
- Image grouping with descriptions and metadata
- Random slideshow rotation with custom ordering support
- Keyboard navigation support (Slideshow: Space/Arrow keys, Escape to exit)
- Mobile-responsive design with touch-first interactions

## Quick Start

### Docker Deployment (Recommended)

#### Production Environment

```bash
# Start production environment
./prod.sh

# Or manually:
docker compose -f docker/prod/docker-compose.yml up -d
```

#### Development Environment

```bash
# Start development environment  
./dev.sh

# Or manually:
docker compose -f docker/dev/docker-compose.yml up -d
### Access URLs

#### Production (Port 80):
- Upload Interface: `http://localhost`
- Slideshow Mode: `http://localhost/slideshow`
- Groups Overview: `http://localhost/groups`
- Moderation Panel: `http://localhost/moderation` (requires authentication)

#### Development (Port 3000):
- Upload Interface: `http://localhost:3000`
- Backend API: `http://localhost:5001`
- Slideshow Mode: `http://localhost:3000/slideshow`

### Multi-Image Upload

1. Visit `http://localhost`
2. Drag & drop multiple images or click to select
3. Add an optional description for your image collection
4. **Grant Consent** (mandatory):
   - ✅ **Workshop Display**: Required consent to display images on local monitor
   - ☐ **Social Media** (optional): Per-platform consent for Facebook, Instagram, TikTok
5. Click "Upload Images" to process the batch
6. Receive your **Group ID** and **Management Link** as reference
7. Images are grouped and await moderation approval

### Self-Service Management Portal

After upload, users receive a unique management link (`/manage/:token`) to:

- **View Upload**: See all images and metadata
- **Manage Consents**: Revoke or restore workshop/social media consents
- **Edit Metadata**: Update title, description, year (triggers re-moderation)
- **Manage Images**: Add new images or delete existing ones
- **Delete Group**: Complete removal with double-confirmation
- **Email Contact**: Request deletion of already published social media posts

**Security Features**:
- No authentication required (token-based access)
- Rate limiting: 10 requests per hour per IP
- Brute-force protection: 20 failed attempts → 24h ban
- Complete audit trail of all management actions

### Slideshow Mode

- **Automatic Access**: Navigate to `http://localhost/slideshow`  
- **Features**:
  - Fullscreen presentation
  - 4-second display per image            
  - Automatic progression through all slideshow collections
  - **🆕 Chronological order**: Groups play from oldest to newest (year → upload date)
  - **🆕 Intelligent preloading**: Next images load in background for seamless transitions
  - **🆕 Zero loading delays**: Pre-cached images for instant display
  - Smooth fade transitions (0.5s)  

- **Keyboard Controls**:
  - **ESC**: Exit slideshow / Return to upload page
  - **Spacebar / Arrow Right**: Manually advance to next image
  - **Home Button**: Return to main upload interface

### Preview Image Optimization

The application automatically generates optimized preview thumbnails for all uploaded images to significantly improve gallery loading performance.

- **Automatic Generation**:
  - Preview images are created automatically on server startup
  - Existing images without previews are processed on-demand
  - New uploads generate previews immediately during upload

- **Technical Specifications**:
  - **Max Width**: 800px (maintains aspect ratio)
  - **Format**: JPEG with 85% quality
  - **Size Reduction**: 96-98% smaller than originals (e.g., 2076KB → 58.5KB)
  - **Performance**: ~30x faster gallery loading times

- **Smart Image Loading**:
  - **Galleries & Overview**: Load lightweight preview images (~50-100KB)
  - **Slideshow Mode**: Uses full-resolution originals for best quality
  - **Fallback**: Automatically uses originals if preview generation fails

- **Storage**:
  - Originals: `backend/src/data/images/` (~2-4MB per image)
  - Previews: `backend/src/data/previews/` (~50-100KB per image)
  - Database: `preview_path` column stores preview filename

### Moderation Interface (Protected)

- **Access**: `http://localhost/moderation` (requires admin session)
- **Authentication Flow**:
  - Built-in login form establishes a server session stored in HttpOnly cookies
  - First-time setup wizard creates the initial admin user once `ADMIN_SESSION_SECRET` is configured
  - CSRF token must be included (header `X-CSRF-Token`) for any mutating admin API call
  - `AUTHENTICATION.md` documents CLI/cURL examples for managing sessions and CSRF tokens
- **Protected Endpoints**: All `/api/admin/*` routes require authentication
- **Features**:
  - Review pending image groups before public display
  - Visual countdown showing days until automatic deletion (7 days for unapproved groups)
  - **Consent Management**:
    - Visual consent badges showing social media platforms
    - Filter by consent status (All / Workshop-only / Facebook / Instagram / TikTok)
    - Export consent data as CSV/JSON for legal compliance
    - Consent timestamp tracking
  - Approve or reject submitted collections with instant feedback
  - Delete individual images from approved groups  
  - View group details (title, creator, description, image count)
  - **Deletion Log** (bottom of moderation page):
    - Statistics: Total groups/images deleted, storage freed
    - Detailed history table with timestamps and reasons
    - Toggle between last 10 entries and complete history
  - Bulk moderation actions

- **Automatic Cleanup**:
  - Unapproved groups are automatically deleted after 7 days
  - Daily cleanup runs at 10:00 AM (Europe/Berlin timezone)
  - Complete removal: Database entries + physical files (originals + previews)
  - Full audit trail logged for compliance
  - **Note**: Approved groups are NEVER automatically deleted

- **Security Features**:
  - Password protected access via nginx HTTP Basic Auth
  - Hidden from search engines (`robots.txt` + `noindex` meta tags)
  - No public links or references in main interface    

### Public Overview of all approved slideshows 
- **Group Management**: Navigate to `http://localhost/groups`
  - Overview of all approved slideshow collections
  - Launch slideshow mode from any group
  - View group statistics and metadata
     

## Docker Structure

The application uses separate Docker configurations for development and production with **simplified environment variable management**:

```
docker/
├── .env.backend.example     # Backend environment variables documentation
├── .env.frontend.example    # Frontend environment variables documentation
├── dev/                     # Development environment
│   ├── .env                 # 🆕 Central dev secrets (gitignored)
│   ├── .env.example         # Dev environment template
│   ├── docker-compose.yml   # All ENV vars defined here
│   ├── backend/
│   │   └── Dockerfile       # Development backend container
│   └── frontend/
│       ├── config/env.sh    # Generates window._env_ from ENV
│       ├── Dockerfile       # Development frontend container
│       ├── nginx.conf       # Development nginx configuration
│       └── start.sh         # Development startup script
└── prod/                    # Production environment
    ├── .env                 # 🆕 Central prod secrets (gitignored)
    ├── .env.example         # Production environment template
    ├── docker-compose.yml   # All ENV vars defined here
    ├── backend/
    │   └── Dockerfile       # Production backend container
    └── frontend/
        ├── config/env.sh    # Generates window._env_ from ENV
        ├── config/htpasswd  # HTTP Basic Auth credentials
        ├── Dockerfile       # Production frontend container
        └── nginx.conf       # Production nginx configuration
```

### Environment Configuration

**🆕 Simplified ENV Structure (Nov 2025):**
- **2 central `.env` files** (down from 16 files!)
  - `docker/dev/.env` - All development secrets
  - `docker/prod/.env` - All production secrets
- **docker-compose.yml** - All environment variables defined in `environment:` sections
- **No .env files in Docker images** - All configuration via docker-compose
- **Frontend env.sh** - Generates `window._env_` JavaScript object from ENV variables at runtime

**How it works:**
1. Docker Compose automatically reads `.env` from the same directory
2. Variables are injected into containers via `environment:` sections using `${VAR}` placeholders
3. Frontend `env.sh` script reads ENV variables and generates JavaScript config at container startup
4. Secrets stay in gitignored `.env` files, never in code or images

- **Development**: Uses `docker/dev/` configuration with live reloading
- **Production**: Uses `docker/prod/` configuration with optimized builds
- **Scripts**: Use `./dev.sh` or `./prod.sh` for easy deployment

## Data Structure 

Data are stored in SQLite database. The structure is as follows:

### Core Tables

``` sql
-- Groups table (extended with consent fields)
CREATE TABLE groups (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id TEXT UNIQUE NOT NULL,
    year INTEGER NOT NULL,
    title TEXT NOT NULL,
    description TEXT,
    name TEXT,
    upload_date DATETIME NOT NULL,
    approved BOOLEAN DEFAULT FALSE,
    display_in_workshop BOOLEAN NOT NULL DEFAULT 0,  -- Consent for workshop display
    consent_timestamp DATETIME,                       -- When consent was granted
    management_token TEXT,                            -- For Phase 2: Self-service portal
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Images table
CREATE TABLE images (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id TEXT NOT NULL,
    file_name TEXT NOT NULL,
    original_name TEXT NOT NULL,
    file_path TEXT NOT NULL,
    preview_path TEXT,                               -- Optimized thumbnail path
    image_description TEXT,                          -- Individual image description
    upload_order INTEGER NOT NULL,
    file_size INTEGER,
    mime_type TEXT,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE CASCADE
);

-- Deletion log for audit trail
CREATE TABLE deletion_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id TEXT NOT NULL,
    title TEXT,
    name TEXT,
    upload_date DATETIME,
    image_count INTEGER,
    total_size INTEGER,
    deletion_reason TEXT,
    deleted_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```

### Social Media Consent Tables

``` sql
-- Configurable social media platforms
CREATE TABLE social_media_platforms (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    platform_name TEXT UNIQUE NOT NULL,    -- e.g., 'facebook', 'instagram', 'tiktok'
    display_name TEXT NOT NULL,             -- e.g., 'Facebook', 'Instagram', 'TikTok'
    icon_name TEXT,                         -- Material-UI Icon name
    is_active BOOLEAN DEFAULT 1,
    sort_order INTEGER DEFAULT 0,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP
);

-- Per-group, per-platform consent tracking
CREATE TABLE group_social_media_consents (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id TEXT NOT NULL,
    platform_id INTEGER NOT NULL,
    consented BOOLEAN NOT NULL DEFAULT 0,
    consent_timestamp DATETIME NOT NULL,
    revoked BOOLEAN DEFAULT 0,               -- For Phase 2: Consent revocation
    revoked_timestamp DATETIME,              -- When consent was revoked
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE CASCADE,
    FOREIGN KEY (platform_id) REFERENCES social_media_platforms(id) ON DELETE CASCADE,
    UNIQUE(group_id, platform_id)
);

-- Management audit log (Phase 2)
CREATE TABLE management_audit_log (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    group_id TEXT,
    management_token TEXT,                   -- First 8 characters only (masked)
    action TEXT NOT NULL,                     -- validate_token, revoke_consent, edit_metadata, add_images, delete_image, delete_group
    success BOOLEAN NOT NULL,
    error_message TEXT,
    ip_address TEXT,
    user_agent TEXT,
    request_data TEXT,                        -- JSON of request body
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE SET NULL
);

-- Indexes for performance
CREATE INDEX IF NOT EXISTS idx_audit_group_id ON management_audit_log(group_id);
CREATE INDEX IF NOT EXISTS idx_audit_action ON management_audit_log(action);
CREATE INDEX IF NOT EXISTS idx_audit_success ON management_audit_log(success);
CREATE INDEX IF NOT EXISTS idx_audit_created_at ON management_audit_log(created_at);
CREATE INDEX IF NOT EXISTS idx_audit_ip_address ON management_audit_log(ip_address);
    revoked_timestamp DATETIME,
    created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    updated_at DATETIME DEFAULT CURRENT_TIMESTAMP,
    FOREIGN KEY (group_id) REFERENCES groups(group_id) ON DELETE CASCADE,
    FOREIGN KEY (platform_id) REFERENCES social_media_platforms(id) ON DELETE CASCADE,
    UNIQUE(group_id, platform_id)
);

-- Migration tracking
CREATE TABLE schema_migrations (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    migration_name TEXT UNIQUE NOT NULL,
    applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
```

### Indexes

``` sql
-- Groups indexes
CREATE INDEX idx_groups_group_id ON groups(group_id);
CREATE INDEX idx_groups_year ON groups(year);
CREATE INDEX idx_groups_upload_date ON groups(upload_date);
CREATE INDEX idx_groups_display_consent ON groups(display_in_workshop);
CREATE UNIQUE INDEX idx_groups_management_token ON groups(management_token) WHERE management_token IS NOT NULL;

-- Images indexes
CREATE INDEX idx_images_group_id ON images(group_id);
CREATE INDEX idx_images_upload_order ON images(upload_order);

-- Consent indexes
CREATE INDEX idx_consents_group_id ON group_social_media_consents(group_id);
CREATE INDEX idx_consents_platform_id ON group_social_media_consents(platform_id);
CREATE INDEX idx_consents_consented ON group_social_media_consents(consented);
```

### Triggers

``` sql
-- Update timestamp on groups modification
CREATE TRIGGER update_groups_timestamp 
AFTER UPDATE ON groups
FOR EACH ROW
BEGIN
    UPDATE groups SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;

-- Update timestamp on consent modification
CREATE TRIGGER update_consents_timestamp 
AFTER UPDATE ON group_social_media_consents
FOR EACH ROW
BEGIN
    UPDATE group_social_media_consents SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
END;
```

## Architecture
### Backend (Node.js + Express) 
- **Multi-upload API**: `/api/upload/batch` - Handles batch file processing
- **Groups API**: `/api/groups` - Retrieves slideshow collections
- **Preview Generation**: Automatic thumbnail creation using Sharp (800px JPEG, 85% quality)
- **File Storage**: Organized in `/upload` directory (originals) and `/data/previews` (thumbnails)
- **Database Storage**: sqlite database in `/app/src/data/db/image_uploader.db`

### Frontend (React + Material-UI) 

- **Multi-Upload Interface**: Drag & drop with preview gallery
- **Progress Tracking**: Real-time upload status
- **Spacebar / Arrow Right**: Manually advance to next image
- **Slideshow Engine**: Fullscreen presentation with automatic progression
- **Responsive Design**: Mobile and desktop optimized
- **Home Button**: Return to main upload interface


### Storage Architecture

```
Docker Volume (app-data)
src
└── app
    ├── src
        ├── upload (originals, ~2-4MB each)
        │   ├── ZMmHXzHbqw.jpg
        │   ├── tjjnngOmXS.jpg
        │   └── ...
        └── data
            ├── previews (thumbnails, ~50-100KB each)
            │   ├── ZMmHXzHbqw.jpg
            │   ├── tjjnngOmXS.jpg
            │   └── ...
            └── db
                └── image_uploader.db

```

### Hosting it with Docker

- **Frontend**: React 17, Material-UI, React Router
- **Backend**: Node.js, Express, Multer (file handling)
- **Containerization**: Docker, Docker Compose
- **Reverse Proxy**: nginx (routing & file serving)[In order to host the project you will need to create a docker-compose file. These files are combining multiple docker images to interact with each other.
- **File Upload**: Drag & drop with react-dropzone
- **Notifications**: SweetAlert2  

## API Endpoints    
### Upload Operations

- `POST /api/upload/batch` - Upload multiple images with description and consent data
- `GET /api/groups` - Retrieve all slideshow groups   
- `GET /api/groups/:id` - Get specific slideshow group

### Consent Management

- `GET /api/social-media/platforms` - Get list of active social media platforms
- `POST /api/groups/:groupId/consents` - Save consent data for a group
- `GET /api/groups/:groupId/consents` - Get consent data for a group
- `GET /api/admin/groups/by-consent` - Filter groups by consent status (query params: `?workshopConsent=true&platform=facebook`)
- `GET /api/admin/consents/export` - Export all consent data as CSV/JSON

### User Self-Service Management Portal (Phase 2 - Backend Complete)

**Management Portal APIs** (Token-based authentication):
- `GET /api/manage/:token` - Validate management token and retrieve group data
- `PUT /api/manage/:token/consents` - Revoke or restore consents (workshop & social media)
- `PUT /api/manage/:token/metadata` - Edit group title and description (resets approval status)
- `POST /api/manage/:token/images` - Add new images to existing group (max 50 total, resets approval)
- `DELETE /api/manage/:token/images/:imageId` - Delete individual image (prevents deleting last image)
- `DELETE /api/manage/:token` - Delete entire group with all images and data

**Management Audit Log APIs** (Admin access only):
- `GET /api/admin/management-audit?limit=N` - Retrieve recent management actions (default: 10)
- `GET /api/admin/management-audit/stats` - Get statistics (total actions, success rate, unique IPs)
- `GET /api/admin/management-audit/group/:groupId` - Get audit log for specific group

**Security Features**:
- IP-based rate limiting: 10 requests per hour per IP
- Brute-force protection: 20 failed token validations → 24-hour IP ban
- Complete audit trail: All management actions logged with IP, User-Agent, timestamp
- Token masking: Only first 8 characters stored in audit log for privacy
- Automatic file cleanup: Physical deletion of images when removed via API

### Moderation Operations (Protected)

- `GET /moderation/groups` - Get all groups pending moderation (includes consent info)
- `PATCH /groups/:id/approve` - Approve/unapprove a group for public display
- `DELETE /groups/:id` - Delete an entire group
- `DELETE /groups/:id/images/:imageId` - Delete individual image from group

### Admin Operations (Protected by /moderation access)

- `GET /api/admin/deletion-log?limit=N` - Get recent deletion log entries (default: 10)
- `GET /api/admin/deletion-log/all` - Get complete deletion history
- `GET /api/admin/deletion-log/stats` - Get deletion statistics (total groups/images deleted, storage freed)
- `POST /api/admin/cleanup/trigger` - Manually trigger cleanup (for testing)
- `GET /api/admin/cleanup/preview` - Preview which groups would be deleted (dry-run)

### File Access
- `GET /api/upload/:filename` - Access uploaded image files (legacy, use `/api/download` instead)
- `GET /api/download/:filename` - Download original full-resolution images
- `GET /api/previews/:filename` - Access optimized preview thumbnails (~100KB, 800px width)

## Testing

### Automatic Cleanup Testing

The application includes comprehensive testing tools for the automatic cleanup feature:

```bash
# Run interactive test helper (recommended)
./tests/test-cleanup.sh

# Available test operations:
# 1. View unapproved groups with age
# 2. Backdate groups for testing (simulate 7+ day old groups)
# 3. Preview cleanup (dry-run)
# 4. Execute cleanup manually
# 5. View deletion log history
```

**Testing Workflow:**
1. Upload a test group (don't approve it)
2. Use test script to backdate it by 8 days
3. Preview what would be deleted
4. Execute cleanup and verify deletion log

For detailed testing instructions, see: [`tests/TESTING-CLEANUP.md`](tests/TESTING-CLEANUP.md)

## Configuration

### Environment Variables

**Simplified ENV Management (Nov 2025):**
All environment variables are now managed through **2 central `.env` files** and `docker-compose.yml`:

**Core Variables:**
| Variable | Default | Description | 
|----------|---------|-------------|
| `API_URL` | `http://localhost:5001` | Backend API endpoint (frontend → backend) |
| `PUBLIC_HOST` | `public.test.local` | Public upload subdomain (no admin access) |
| `INTERNAL_HOST` | `internal.test.local` | Internal admin subdomain (full access) |
| `ADMIN_SESSION_SECRET` | - | Secret for admin session cookies (required) |

**Telegram Notifications (Optional):**
| Variable | Default | Description | 
|----------|---------|-------------|
| `TELEGRAM_ENABLED` | `false` | Enable/disable Telegram notifications |
| `TELEGRAM_BOT_TOKEN` | - | Telegram Bot API token (from @BotFather) |
| `TELEGRAM_CHAT_ID` | - | Telegram chat/group ID for notifications |
| `TELEGRAM_SEND_TEST_ON_START` | `false` | Send test message on service startup (dev only) |

**Configuration Files:**
- `docker/dev/.env` - Development secrets (gitignored)
- `docker/prod/.env` - Production secrets (gitignored)
- `docker/dev/.env.example` - Development template (committed)
- `docker/prod/.env.example` - Production template (committed)

**How to configure:**
1. Copy `.env.example` to `.env` in the respective environment folder
2. Edit `.env` and set your secrets (ADMIN_SESSION_SECRET, Telegram tokens, etc.)
3. Docker Compose automatically reads `.env` and injects variables into containers
4. Never commit `.env` files (already in `.gitignore`)

**Telegram Setup:** See `scripts/README.telegram.md` for complete configuration guide.

### Volume Configuration
- **Upload Limits**: 100MB maximum file size for batch uploads
- **Supported Formats**: JPG, JPEG, PNG, GIF, WebP


### Backup & Restore
#### Backup slideshow data
```sh
docker cp image-uploader-backend:/usr/src/app/src/data/ ./image-uploader-backup-data
```

#### Restore slideshow data  
```sh
docker cp ./image-uploader-backup-data image-uploader-backend:/usr/src/app/src/data
```

## Contributing
Contributions are welcome!  
This project extends the original work by [vallezw](https://github.com/vallezw/Image-Uploader). 

### Development Setup
1. Fork the repository
2. Create feature branch: `git checkout -b feature/amazing-feature`
3. Commit changes: `git commit -m 'Add amazing feature'`
4. Push to branch: `git push origin feature/amazing-feature`| Field | Type | Description |#### Changing the URL
5. Open a Pull Request

## License
This project is distributed under the MIT License. See `LICENSE` for more information.

## Acknowledgments
- Original project: [Image-Uploader by vallezw](https://github.com/vallezw/Image-Uploader)




