In the moment.
Snapsera is a photo- and video-first social platform where you share what’s happening now. Post photos and short clips, follow friends, explore hashtags, and get a personalized feed—plus staff-curated news with threaded discussions, direct messages with read receipts, and real-time notifications. Built for creators and communities who want a focused, modern experience without the noise.
This project is released under the MIT License. You are free to use, modify, and distribute it—including running your own instance, rebranding, or commercial use—subject to the terms in the LICENSE file.
New to this stack? Use the Setup Guide for Noobs for a step-by-step install walkthrough (Node.js, MySQL, Cloudinary, env config, and running the app).
- Showcase
- License
- Setup for beginners
- Overview
- Features
- Tech Stack
- Project Structure
- Getting Started
- Development
- API Endpoints
- Scripts
- Deployment
Snapsera is a full-stack social media platform centered on photos and videos. Users create posts (upload or external URLs), add captions and hashtags, and choose public or private visibility. A personalized feed (“For You”, Most Recent, Photos, Videos) surfaces content with infinite scroll and ranking driven by likes, views, and watch-time. News is staff-authored: rich articles with images, threaded comments, likes, and shares. Direct messages support one-on-one conversations with read receipts and real-time delivery. Hashtags power discovery and dedicated feeds. Real-time notifications and optional desktop app (Tauri) keep users connected.
Moderation & operations: Staff can assign verified badges, toggle maintenance mode (non-staff see a blurred overlay with login; staff see a dismissible banner), and optionally use TikTok sync when enabled. Bans and privacy settings (profile visibility, default post visibility) are supported.
Post detail (/post/:id): When opened via a shared link (no browser history), the close button goes to home; otherwise it goes back.
- Auth & profiles: JWT login/register, display names, avatars, bios, verified badges (gold for staff), privacy settings, “how we refer to you” (her/his/their), staff roles
- Content: Photo/video uploads (Cloudinary), external media URLs, captions, portrait/landscape, AI-generated label, public/private visibility
- Feed: “For You” (seeded shuffle, may include news), Most Recent, Photos, Videos; infinite scroll, cursor pagination; post detail at
/post/:id - Hashtags: Feeds at
/hashtag/:tag, suggestions by prefix with counts - News: Staff-authored posts (title, body, images), like/comment (threaded)/share, News page and share modal
- Direct messages: One-on-one DMs with read receipts and real-time delivery
- Engagement: Like/unlike, view and watch-time tracking, real-time updates, feed ranking
- UX: Responsive layout, mobile theme option, real-time notifications, audio preferences, pan/zoom for images, Privacy/Terms/Sign-in modals, optional desktop app (Download page)
- Staff panel: Verification (set/remove verified badge), maintenance mode (toggle in panel; when on, non-staff see overlay, staff see overlay banner with “Hide”)
- TikTok sync (optional, off by default): Manage syncs, trigger syncs; see
TIKTOK_INTEGRATION_ENABLEDinfrontend/src/lib/site.ts
- Backend: Node.js, TypeScript, Express, MySQL 2, JWT, bcryptjs, Cloudinary, Socket.IO
- Frontend: React 18, TypeScript, Vite, React Router, Socket.IO client, custom CSS with variables
loop2/
├── backend/
│ ├── src/
│ │ ├── app.ts, index.ts, db.ts
│ │ ├── config/env.ts
│ │ ├── middleware/auth.ts, requireStaff.ts
│ │ ├── routes/ # auth, users, posts, feed, engagement, hashtags, news, notifications, messages, staff, site
│ │ ├── services/
│ │ └── realtime/socket.ts
│ ├── scripts/ # migrations, copy-frontend-build, syncTikTok
│ └── package.json, tsconfig.json
├── frontend/
│ ├── src/
│ │ ├── App.tsx, main.tsx
│ │ ├── components/ # PostCard, ShareModal, MaintenanceOverlay, MaintenanceBanner, …
│ │ ├── pages/ # Feed, Profile, Login, PostDetail, News, Messages, StaffPanel, …
│ │ ├── lib/ # api, auth, socket, site
│ │ └── styles.css
│ └── package.json, vite.config.ts
├── database.sql
└── README.md
- Node.js (v18+), MySQL (v8+), npm or yarn
- Cloudinary account (media storage)
- TikTok API (optional; only if enabling TikTok sync)
Create .env in the backend directory:
NODE_ENV=development
PORT=4000
JWT_SECRET=your-secret-key-here
JWT_EXPIRES_IN=7d
MYSQL_HOST=127.0.0.1
MYSQL_PORT=3306
MYSQL_USER=your-mysql-user
MYSQL_PASSWORD=your-mysql-password
MYSQL_DATABASE=snapsera
CLOUDINARY_CLOUD_NAME=your-cloud-name
CLOUDINARY_API_KEY=your-api-key
CLOUDINARY_API_SECRET=your-api-secret
UPLOAD_MAX_MB=50
# SEED_MASTER_PASSWORD=optional-master-password-for-staff-panel-seed-fake-usersJWT_SECRET can be auto-generated and stored in ~/.snapsera_env.json if omitted.
Optional: SEED_MASTER_PASSWORD — when set, staff can create fake user accounts from the staff panel (Seed fake users). Requires entering this password to run.
- Create database:
CREATE DATABASE snapsera; - Load schema:
mysql -u user -p snapsera < database.sql
For existing deployments, run the site_settings table and default row if adding maintenance mode; see comments in database.sql.
- Backend:
cd backend && npm run dev(defaulthttp://localhost:4000) - Frontend:
cd frontend && npm run dev(defaulthttp://localhost:5173)
cd backend
npm run build:all # builds backend + frontend, copies frontend into backend/public
npm start- Auth:
POST /api/auth/register,POST /api/auth/login - Users:
GET/PATCH /api/users/:username,GET /api/users/:username/posts,POST/DELETE /api/users/:username/follow - Posts:
GET /api/posts/:id,POST /api/posts,PATCH /api/posts/:id/visibility,DELETE /api/posts/:id - Feed:
GET /api/feed(query:sort,media,cursor,limit,anchor) - Hashtags:
GET /api/hashtags/suggest?q=...,GET /api/hashtags/:tag/posts - News:
GET/POST /api/news,GET /api/news/:id, upload-image, like/unlike, share, comments (list, add, like/unlike) - Notifications:
GET /api/notifications,GET /api/notifications/count,PATCH :id/read,PATCH /read-all(auth) - Messages:
GET /api/messages/unread-count,GET /api/messages/conversations,GET /api/messages/conversations/:id,POST /api/messages/conversations,POST /api/messages/conversations/:id/messages,POST /api/messages/conversations/:id/read - Engagement:
POST /api/engagement/like,POST /api/engagement/unlike,POST /api/engagement/view,POST /api/engagement/watch,GET /api/engagement/:postId - Site:
GET /api/site/maintenance(public),PATCH /api/site/maintenance(staff; body{ enabled: boolean }) - Staff (auth required):
PATCH /api/staff/users/:username/verified, TikTok sync routes when enabled
Backend: npm run dev, npm run build, npm run build:all, npm start, npm run migrate:usernames-remove-spaces, npm run sync:tiktok (if TikTok enabled). See backend/scripts/ and database.sql for migration scripts.
Frontend: npm run dev, npm run build, npm run preview
- Set
NODE_ENV=productionand configure env vars. - Ensure MySQL is set up; run schema and any migrations (see
database.sql). - From
backend:npm run build:all && npm start.
Optional: If TikTok sync is enabled, schedule npm run sync:tiktok (e.g. cron) or use the staff panel to trigger syncs.


