2.1 KiB
2.1 KiB
Picta
Image sharing app: dark-mode masonry gallery, drag-and-drop / click / clipboard paste uploads, 4K re-encoding, voting, and threaded comments.
Stack
- Backend: Node 20 (Alpine), Express, multer (multipart), sharp (image pipeline), cors
- Frontend: vanilla HTML/CSS/JS, CSS masonry grid, no build step
- Storage: local
public/uploads/(in-memory + file persistence indata/) - Image processing: any upload is re-encoded to JPEG q92, downscaled to max 3840px (4K) while preserving aspect ratio
API
| Method | Path | Description |
|---|---|---|
| GET | /api/health |
health + counts |
| GET | /api/posts?sort=new|viral|user|top |
list posts |
| GET | /api/posts/:id |
single post |
| POST | /api/upload |
multipart (image, title, tags, author) |
| POST | /api/posts/:id/vote |
JSON {direction, userId} |
| GET | /api/comments/:postId |
list comments (flat, with parentId) |
| POST | /api/comments/:postId |
JSON {author, text, parentId?} |
Frontend
- Masonry gallery with staggered card entrance animation
- Filter tabs: New, Most Viral, User Submitted, Top Today
- Upload via: file picker, drag-and-drop, or Ctrl+V paste (opens modal with preview, auto-submits)
- Floating action button (FAB) as the persistent upload CTA
- New posts scroll into view with a highlight pulse
- Post detail modal: voting, views, tags, threaded comments
- Focus-visible states and reduced-motion support
Run locally
npm ci
node server.js # http://localhost:3000
Docker
docker build -t imgur-clone:latest .
docker run -d --name imgur-clone --restart unless-stopped -p 3001:3000 imgur-clone:latest
Note: The Docker image and container names remain
imgur-clonefor deployment compatibility.
Security
- Runtime image strips npm/corepack (only
node server.jsruns at runtime) apk upgradeon every build for base-image CVE coverage- Verified with Trivy: 0 vulnerabilities across all OS + node packages
Production
Deployed on Unraid (192.168.0.123), Caddy reverse proxy with auto-TLS → https://imgur.krisforbes.ca