- Display app version dynamically from window._env_.APP_VERSION - Credit original author (vallezw) with link to original repo - Credit extended version (lotzm) with link to Gitea repo - Update package.json version to 1.1.0 Footer styling: - Position fixed at bottom-right corner of viewport - Unobtrusive design with small font size (11px) - Semi-transparent background with subtle shadow - Stays visible while scrolling - Hover effect on links for better UX Changes: - frontend/package.json: version 0.1.0 → 1.1.0 - Footer.js: Dynamic version display, attribution links - Footer.css: Fixed positioning, responsive styling |
||
|---|---|---|
| .copilot | ||
| .github/ISSUES | ||
| backend | ||
| docker | ||
| docs | ||
| frontend | ||
| scripts | ||
| test_photos | ||
| tests | ||
| .dockerignore | ||
| .gitignore | ||
| CHANGELOG.md | ||
| dev.sh | ||
| LICENSE | ||
| prod.sh | ||
| README.dev.md | ||
| README.md | ||
| TODO.md | ||
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
Automatic Cleanup: 🆕 Unapproved groups are automatically deleted after 7 days
Deletion Log: 🆕 Complete audit trail of automatically deleted content
Drag-and-Drop Reordering: 🆕 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
Admin Panel: Dedicated moderation interface for content management and organization
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
Lightweight: Built with modern web technologies for optimal performance
What's New
This project extends the original Image-Uploader by vallezw with enhanced multi-upload and slideshow capabilities.
🆕 Latest Features (November 2025)
- 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
# Start production environment
./prod.sh
# Or manually:
docker compose -f docker/prod/docker-compose.yml up -d
Development Environment
# 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`
- Admin 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. Click "Upload Images" to process the batch
5. Images are automatically grouped for slideshow viewing
### Slideshow Mode
- **Automatic Access**: Navigate to `http://localhost/slideshow`
- **Features**:
- Fullscreen presentation
- 4-second display per image
- Automatic progression through all slideshow collections
- Random selection of next slideshow after completing current one
- 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 authentication)
- **Authentication**: HTTP Basic Auth (username: admin, password: set during setup)
- **Features**:
- Review pending image groups before public display
- Visual countdown showing days until automatic deletion (7 days for unapproved groups)
- 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:
docker/ ├── .env.backend.example # Backend environment variables documentation ├── .env.frontend.example # Frontend environment variables documentation ├── dev/ # Development environment │ ├── docker-compose.yml # Development services configuration │ ├── backend/ │ │ ├── config/.env # Development backend configuration │ │ └── Dockerfile # Development backend container │ └── frontend/ │ ├── config/.env # Development frontend configuration │ ├── config/env.sh # Runtime configuration script │ ├── Dockerfile # Development frontend container │ ├── nginx.conf # Development nginx configuration │ └── start.sh # Development startup script └── prod/ # Production environment ├── docker-compose.yml # Production services configuration ├── backend/ │ ├── config/.env # Production backend configuration │ └── Dockerfile # Production backend container └── frontend/ ├── config/.env # Production frontend configuration ├── config/env.sh # Runtime configuration script ├── config/htpasswd # HTTP Basic Auth credentials ├── Dockerfile # Production frontend container └── nginx.conf # Production nginx configuration
### Environment Configuration
- **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:
``` sql
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,
created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
CREATE TABLE sqlite_sequence(name,seq);
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,
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
);
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_images_group_id ON images(group_id);
CREATE INDEX idx_images_upload_order ON images(upload_order);
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;
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
/uploaddirectory (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 descriptionGET /api/groups- Retrieve all slideshow groupsGET /api/groups/:id- Get specific slideshow group
Moderation Operations (Protected)
GET /moderation/groups- Get all groups pending moderationPATCH /groups/:id/approve- Approve/unapprove a group for public displayDELETE /groups/:id- Delete an entire groupDELETE /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 historyGET /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/downloadinstead)GET /api/download/:filename- Download original full-resolution imagesGET /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:
# 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:
- Upload a test group (don't approve it)
- Use test script to backdate it by 8 days
- Preview what would be deleted
- Execute cleanup and verify deletion log
For detailed testing instructions, see: tests/TESTING-CLEANUP.md
Configuration
Environment Variables
| Variable | Default | Description |
|---|---|---|
API_URL |
http://localhost:5000 |
Backend API endpoint |
CLIENT_URL |
http://localhost |
Frontend application URL |
Volume Configuration
- Upload Limits: 100MB maximum file size for batch uploads
- Supported Formats: JPG, JPEG, PNG, GIF, WebP
Backup & Restore
Backup slideshow data
docker cp image-uploader-backend:/usr/src/app/src/data/ ./image-uploader-backup-data
Restore slideshow data
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.
Development Setup
- Fork the repository
- Create feature branch:
git checkout -b feature/amazing-feature - Commit changes:
git commit -m 'Add amazing feature' - Push to branch:
git push origin feature/amazing-feature| Field | Type | Description |#### Changing the URL - 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