# Val-Friendbook A small self-hosted friendbook for the lovely peeps I meet on there. Everyone can submit their own page (name, fun fact, favorite color, mood, badge images, ...). New entries stay hidden until an admin approves them. Plain Express + Multer, with a JSON file as the "database". No native modules, no external services. ## Features - Public gallery of approved entries, newest first - Submission form with up to 5 badge images per entry (PNG, JPG, GIF, WEBP, max. 3 MB each; easily my favourite feature lol) - Moderation: new entries are `pending` until approved in the admin panel - Denying an entry deletes it and its uploaded images - Admin panel at `/admin`, protected by a password ## Requirements - Node.js 18 or newer ## Quick start ```bash git clone https://gitea.nkelabs.xyz/nke4/Val-Friendbook.git cd Val-Friendbook npm install ADMIN_PASSWORD='choose-a-strong-password' npm start ``` The app runs on `http://localhost:3030` by default. ## Configuration | Variable | Default | Description | | ---------------- | ---------- | --------------------------------------------------------- | | `ADMIN_PASSWORD` | `changeme` | Password for the admin panel. **Always set this.** | | `PORT` | `3030` | Port the server listens on (the Dockerfile sets `3000`). | If `ADMIN_PASSWORD` is not set, the server falls back to `changeme` and prints a warning in the log on startup. ## Deployment ### pm2 ```bash ADMIN_PASSWORD='choose-a-strong-password' PORT=3000 pm2 start server.js --name friendbook pm2 save ``` To change the environment of an existing process: ```bash ADMIN_PASSWORD='new-password' pm2 restart friendbook --update-env pm2 save ``` ### Docker ```bash docker build -t val-friendbook . docker run -d --name val-friendbook \ -p 3000:3000 \ -e ADMIN_PASSWORD='choose-a-strong-password' \ -v "$(pwd)/data:/app/data" \ -v "$(pwd)/uploads:/app/uploads" \ val-friendbook ``` ## Data | Path | Contents | | ------------------- | ------------------------------ | | `data/entries.json` | All entries (pending + approved) | | `uploads/` | Uploaded badge images | Both are created automatically on first start and are excluded from git. Back them up or mount them as volumes so entries survive restarts and redeploys. ## Admin panel Open `/admin` and log in with `ADMIN_PASSWORD`. From there you can approve or deny pending entries. ## API | Method | Route | Auth | Description | | ------ | ---------------------------------- | ----- | ---------------------------------------- | | GET | `/api/entries` | - | List approved entries | | POST | `/api/entries` | - | Submit a new entry (multipart, `badges`) | | POST | `/api/admin/login` | admin | Check the admin password | | GET | `/api/admin/entries` | admin | List all entries | | POST | `/api/admin/entries/:id/approve` | admin | Approve an entry | | POST | `/api/admin/entries/:id/deny` | admin | Delete an entry and its images | Admin routes expect the password in the `x-admin-password` header. ## Project structure ``` . ├── server.js # Express backend, JSON storage, admin routes ├── public/ # Frontend (index, admin panel, styles, scripts) ├── Dockerfile └── package.json ``` ## Notes - The admin password is sent in a header on every admin request. Run the app behind HTTPS (reverse proxy or tunnel) when exposing it publicly. - Entries are stored in a single JSON file, which is fine for a small friend group but not meant for large scale, any help with this is appreciated, since I can't code for shit ## Enjoy! :3