photojs - sctruong.com
A small, filesystem-backed photo gallery running on Node.js and Express. The frontend is built with React and Vite and is designed for a simple personal gallery deployment.
- Responsive three-column photo gallery.
- Full-size image viewer with keyboard escape support.
- Upload images with live upload progress.
- Add, edit, filter, and remove tags.
- Drag-and-drop photo reordering on desktop.
- Automatic thumbnails for faster browsing.
- EXIF orientation handling through Sharp.
- HEIC/HEIF upload conversion through
heif-convert. - Persistent photo metadata stored in
photos.json. - 50 MB upload limit.
- Optional admin controls protected by the frontend admin password.
- React 19 and TypeScript
- Vite
- Express 5
- Multer for multipart uploads
- Sharp for image processing
- Nginx and systemd for the production deployment
- Node.js 20 or newer
- pnpm or npm
heif-convertinstalled and available onPATHif HEIC/HEIF uploads are needed
On Ubuntu, the HEIF converter is provided by libheif-examples:
sudo apt install libheif-examplesInstall dependencies:
pnpm installStart the Express API and production-style static server:
pnpm startThe server listens on http://localhost:8000 by default. Set PORT to use a different port:
PORT=9000 pnpm startFor frontend development with Vite hot reload, run both processes in separate terminals:
pnpm start
pnpm devVite serves the frontend at http://localhost:5173 and proxies /api and /photos to the Express server on port 8000.
Build the frontend into dist/:
pnpm buildThen start the server with:
NODE_ENV=production pnpm startThe Express server serves the built frontend, the API, and stored photos. The included deployment files provide the production configuration:
deploy/photoserver.serviceruns the Node server with systemd.deploy/sctruong.nginxserves the frontend and proxies API/photo requests to port 8000.
Typical systemd setup:
sudo cp deploy/photoserver.service /etc/systemd/system/photoserver.service
sudo systemctl daemon-reload
sudo systemctl enable --now photoserverUploaded originals are stored in photos/, generated thumbnails are stored in photos/thumbnails/, and photo metadata is stored in photos.json. Back up all three locations before moving or reinstalling the server.
The application creates photos/, photos/thumbnails/, and photos.json when they are missing. The Node process must have write access to the project directory.
GET /api/photos- list photos and generated image URLsPOST /api/photos- upload a photo, tags, and a column positionPATCH /api/photos/:id- update a photo's tagsPUT /api/photos/order- save the gallery orderDELETE /api/photos/:id- delete a photo and its thumbnail
Format the project with:
pnpm format