Files
2026-10-23 19:01:14 +02:00

109 lines
3.9 KiB
Markdown

# 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".
## 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
- Docker not fully tested yet, so no guarantee it will work
## Enjoy! :3