A production-ready, self-hosted photo gallery platform for photographers to deliver client galleries with password protection, favorites, and download capabilities.
- JWT-based authentication
- Create and manage galleries
- Upload multiple photos (supports RAW/HEIC conversion)
- Password-protect galleries
- Control download and favorite permissions
- Delete galleries and photos
- Password-protected gallery access
- Responsive photo grid
- Fullscreen image viewer with keyboard navigation
- Favorite photos (session-based, no account needed)
- Download photos (if enabled)
- Automatic image processing (4 sizes: thumbnail, preview, web, original)
- Nginx serves images directly (not through Node.js)
- Async upload processing
- Image caching with long expiry
Backend:
- Node.js + TypeScript
- Fastify (web framework)
- PostgreSQL (database)
- Sharp (image processing)
- JWT authentication
- bcrypt (password hashing)
Frontend:
- React + TypeScript
- TailwindCSS
- Vite
- Zustand (state management)
- React Router
- Axios
Deployment:
- Docker + Docker Compose
- Nginx (reverse proxy + static file serving)
- Node.js 18+
- PostgreSQL 14+
- npm or yarn
cd backend
npm install
cp .env.example .env
# Edit .env with your database credentials
npm run migrate:up
npm run create-admin
npm run devcd frontend
npm install
npm run devThe frontend will be available at http://localhost:5173 and will proxy API requests to the backend at http://localhost:3000.
git clone https://github.com/zwroee/GalleryStudio
cd GalleryStudio
cp .env.example .env
# Edit .env with secure passwordsdocker compose up -ddocker compose exec backend npm run migrate:updocker compose exec backend npm run create-admin- Frontend:
http://your-server - Admin:
http://your-server/admin
The easiest way to install Gallery Studio on CasaOS is using the "Custom Install" feature.
Important: Since CasaOS runs on a remote server, it cannot build the code from source. You must use the "Release" version which uses pre-built Docker images.
-
Prepare the File:
- Download the
casaos-docker-compose.ymlfile from this repository. - (Alternatively, copy its content).
- Download the
-
Open CasaOS Dashboard:
- Click the + button in the top left or "App Store".
- Click Custom Install (top right corner).
-
Import Configuration:
- Click the Import icon (top right of the overlay).
- Select "Docker Compose" and upload
casaos-docker-compose.yml.
-
Verify Settings:
- CasaOS will now correctly see the
ghcr.io/zwroee/gallerystudio-backendandfrontendimages. - Ensure the "Main App" is set to the nginx service.
- Check that port 8080 is not in use.
- CasaOS will now correctly see the
-
Install:
- Click Install.
-
Post-Install Setup:
- Open the app terminal (click the terminal icon on the app card).
- Run the following commands to set up the database:
# Enter the backend container docker exec -it gallery-studio-backend sh # Run migrations npm run migrate:up # Create default admin user npm run create-admin
- Default login:
admin/password123(Change this immediately!)
A static demo of the frontend is available. To deploy:
- Enable GitHub Pages in your repository settings (Source: GitHub Actions).
- Push to the
mainbranch. - The "Deploy Demo" workflow will automatically build and deploy the demo to your Pages URL.
- Access the demo at
https://<username>.github.io/gallery-studio/.
gallery-studio/
βββ backend/ # Node.js backend
β βββ src/
β β βββ config/ # Database & env config
β β βββ middleware/ # Auth & validation
β β βββ routes/ # API endpoints
β β βββ services/ # Business logic
β β βββ types/ # TypeScript types
β βββ migrations/ # Database migrations
β βββ scripts/ # Utility scripts
βββ frontend/ # React frontend
β βββ src/
β β βββ components/ # React components
β β βββ pages/ # Page components
β β βββ services/ # API client
β β βββ store/ # State management
β βββ public/
βββ storage/ # Image storage (volume mount)
β βββ galleries/
β βββ {gallery_id}/
β βββ original/
β βββ preview/
β βββ thumbnail/
β βββ web/
βββ docker-compose.yml
βββ nginx.conf
βββ README.md
POST /api/auth/login- Admin loginGET /api/auth/verify- Verify JWT token
GET /api/galleries- List all galleriesPOST /api/galleries- Create galleryGET /api/galleries/:id- Get gallery detailsPATCH /api/galleries/:id- Update galleryDELETE /api/galleries/:id- Delete galleryPOST /api/galleries/:id/photos- Upload photosDELETE /api/galleries/:galleryId/photos/:photoId- Delete photo
POST /api/client/galleries/:id/verify- Verify gallery passwordGET /api/client/galleries/:id- View galleryPOST /api/client/photos/:id/favorite- Toggle favoriteGET /api/client/galleries/:id/favorites- Get favorited photos
Uploaded images are automatically processed into:
- Thumbnail (400px) - Grid view
- Preview (1920px) - Fullscreen viewing
- Web (2048px) - Downloads
- Original - Preserved
RAW and HEIC formats are automatically converted to JPEG.
PORT=3000
DATABASE_URL=postgresql://user:password@localhost:5432/gallery_studio
JWT_SECRET=your-secret-key
STORAGE_PATH=/storage/galleries
MAX_UPLOAD_SIZE=524288000 # 500MBDB_PASSWORD=your-db-password
JWT_SECRET=your-jwt-secret- Change default passwords in
.env - Use strong JWT secret (generate with
openssl rand -base64 32) - Set up SSL/TLS certificates for production
- Configure firewall rules
- Regular backups of PostgreSQL database and storage directory
MIT
For issues and questions, please open an issue on GitHub.