Moderation Update

This commit is contained in:
2025-10-09 06:13:48 -04:00
parent 2ae7202445
commit ff10bb3183
20 changed files with 2056 additions and 381 deletions

407
README.md
View File

@@ -1,320 +1,161 @@
# ECS Full Stack
A full-stack example project that integrates a React frontend, an Express backend, and a Discord bot. The app provides a dashboard for server admins to manage bot settings and invites, plus Discord moderation/integration features via a bot running with discord.js.
A full-stack Discord bot management dashboard with React frontend, Express backend, and Discord.js bot integration. Server admins can manage bot settings, invites, moderation, and live notifications through a modern web interface.
This README documents how to get the project running, what environment variables are required, where to get Discord keys, how the invite token flow works, and basic troubleshooting tips.
## Features
Note: The backend has been updated to support Postgres persistence (see `CHANGELOG.md`). The backend now requires `DATABASE_URL` to run in the default configuration; if you prefer the legacy encrypted file store, see the notes under "Developer notes".
- **Dashboard**: View Discord servers and manage per-server settings
- **Invite Management**: Create, list, and revoke server invites with custom options
- **Moderation**: Direct ban/kick/timeout actions from web interface with user autocomplete
- **Live Notifications**: Twitch stream notifications with rich embeds
- **Admin Logs**: Complete moderation action logging with real-time updates
- **Theme Support**: Light, dark, and Discord-themed UI options
## Repository layout
## Quick Start
- `frontend/` — React (Create React App) frontend. Uses `REACT_APP_API_BASE` to communicate with the backend in dev and production.
- `backend/` — Express backend and API server that also coordinates with the `discord-bot` library to manage guilds, invites, and settings. Uses environment variables for configuration.
- `discord-bot/` — small wrapper that logs the bot in and exposes the discord.js client used by the backend.
- `checklist.md`, `README.md`, other docs and small scripts at repo root.
### Prerequisites
- Node.js 18+
- PostgreSQL database
- Discord application with bot user
## What this project does
### Setup
- Provides a React dashboard where a user can view servers the bot is connected to and manage per-server settings (welcome/leave messages, autorole, toggling commands, invite creation/listing/deletion).
- Runs a Discord bot (discord.js) that performs moderation and server features. The backend and bot are closely integrated: the backend hosts the API and the bot client is shared to fetch guild data and manipulate invites/channels/roles.
- Uses a short-lived token flow to authorize invite deletions from the frontend without embedding long-lived secrets in the client.
1. **Clone and install dependencies:**
```bash
git clone <repository-url>
cd ECS-FullStack
npm install # Run in both frontend/ and backend/ directories
```
Expanded: what this app does
2. **Configure Discord App:**
- Go to [Discord Developer Portal](https://discord.com/developers/applications)
- Create new application and bot user
- Copy Client ID, Client Secret, and Bot Token
- Hosts a dashboard (React) that lists Discord guilds where the bot is present and lets server admins:
- create and manage invites (create invites with options, view persisted invites, copy and revoke)
- configure Welcome and Leave messages and channels
- enable/disable bot commands per server
- set autorole behavior for new members
- Provides a backend API (Express) that coordinates with a discord.js bot to perform live guild operations (fetch channels/roles, create invites, leave guilds)
- Stores configuration and invites in Postgres (recommended) or a legacy encrypted `db.json`
3. **Database Setup:**
```sql
CREATE DATABASE ecs_fullstack;
CREATE USER ecs_user WITH PASSWORD 'your_password';
GRANT ALL PRIVILEGES ON DATABASE ecs_fullstack TO ecs_user;
```
## Quickstart — prerequisites
4. **Environment Configuration:**
- Node.js (recommended 18.x or later) and npm
- A Discord application with a Bot user (to get `DISCORD_CLIENT_ID` and `DISCORD_CLIENT_SECRET`) — see below for setup steps
- Optional: a VPS or Tailscale IP if you want to run the frontend/backend on a non-localhost address
**backend/.env:**
```env
DATABASE_URL=postgres://ecs_user:password@localhost:5432/ecs_fullstack
DISCORD_CLIENT_ID=your_client_id
DISCORD_CLIENT_SECRET=your_client_secret
DISCORD_BOT_TOKEN=your_bot_token
ENCRYPTION_KEY=your_32_byte_secret
BACKEND_BASE=http://localhost:3002
FRONTEND_BASE=http://localhost:3001
```
## Environment configuration (.env)
**frontend/.env:**
```env
REACT_APP_API_BASE=http://localhost:3002
```
There are env files used by the backend and frontend. Create `.env` files in the `backend/` and `frontend/` folders for local development. Examples follow.
5. **Start the application:**
```bash
# Backend (includes Discord bot)
cd backend && npm start
### backend/.env (example)
# Frontend (separate terminal)
cd frontend && npm start
```
PORT=3002
HOST=0.0.0.0
BACKEND_BASE=http://your-server-or-ip:3002
FRONTEND_BASE=http://your-server-or-ip:3001
CORS_ORIGIN=http://your-server-or-ip:3001
DISCORD_CLIENT_ID=your_discord_client_id
DISCORD_CLIENT_SECRET=your_discord_client_secret
ENCRYPTION_KEY=a-32-byte-or-longer-secret
INVITE_TOKEN_SECRET=optional-second-secret-for-invite-tokens
6. **Invite Bot to Server:**
- Use OAuth2 URL Generator in Discord Developer Portal
- Select `bot` and `applications.commands` scopes
- Choose appropriate permissions
- Visit generated URL to invite bot
# Postgres example (optional but recommended)
# DATABASE_URL=postgres://dbuser:dbpass@100.111.50.59:5432/ecs_db
## Project Structure
- `PORT` / `HOST`: where the backend listens.
- `BACKEND_BASE` and `FRONTEND_BASE`: used for constructing OAuth redirect URIs and links.
- `CORS_ORIGIN`: optional; set to your frontend origin to restrict CORS.
- `DISCORD_CLIENT_ID` / `DISCORD_CLIENT_SECRET`: from the Discord Developer Portal (see below).
- `ENCRYPTION_KEY` or `INVITE_TOKEN_SECRET`: used to sign short-lived invite tokens. Keep this secret.
Note: This project previously supported an `INVITE_API_KEY` static secret; that requirement has been removed. Invite deletes are authorized via short-lived invite tokens by default.
### Twitch Live Notifications (optional)
This project can detect when watched Twitch users go live and post notifications to a configured Discord channel for each guild. To enable this feature, add the following to `backend/.env`:
- `TWITCH_CLIENT_ID` — your Twitch app client id
- `TWITCH_CLIENT_SECRET` — your Twitch app client secret
- `TWITCH_POLL_INTERVAL_MS` — optional, poll interval in milliseconds (default 30000)
When configured, the backend exposes:
- GET /api/twitch/streams?users=user1,user2 — returns stream info for the listed usernames (used by the frontend and bot watcher)
The bot includes a watcher that polls watched usernames per-guild and posts a message to the configured channel when a streamer goes live. The message includes the stream title and a link to the Twitch stream.
If you run the backend and the bot on separate hosts, you can configure the backend to push setting updates to the bot so toggles and watched users propagate immediately:
- `BOT_PUSH_URL` — the URL the bot will expose for the backend to POST setting updates to (e.g., http://bot-host:4002)
- `BOT_SECRET` — a shared secret used by the backend and bot to secure push requests
- `BOT_PUSH_PORT` — optional, the port the bot listens on for push requests (if set the bot starts a small HTTP receiver)
### frontend/.env (example)
HOST=0.0.0.0
PORT=3001
REACT_APP_API_BASE=http://your-server-or-ip:3002
Set `REACT_APP_API_BASE` to point at the backend so the frontend can call API endpoints.
## Create a Discord Application and Bot (short)
1. Go to the Discord Developer Portal: https://discord.com/developers/applications
2. Create a new application.
3. Under "OAuth2" -> "General", add your redirect URI:
- For dev: `http://your-server-or-ip:3002/auth/discord/callback`
- Make sure `BACKEND_BASE` matches the host/port you set in `backend/.env`.
4. Under "Bot" create a Bot user and copy the Bot token (NOT committed to source).
5. Under "OAuth2" -> "URL Generator" select scopes `bot` and `applications.commands` and select permissions (e.g., Administrator if you want full access for testing). Use the generated URL to invite the bot to your guild during testing.
Store the Client ID / Client Secret in your `backend/.env` as `DISCORD_CLIENT_ID` and `DISCORD_CLIENT_SECRET`.
## Invite token flow (why and how)
- To avoid embedding long-lived secrets in a web client, invite deletions are authorized with a short-lived HMAC-signed token.
- The frontend requests a token with:
GET /api/servers/:guildId/invite-token
- The backend returns `{ token: '...' }`. The frontend then calls
DELETE /api/servers/:guildId/invites/:code
with header `x-invite-token: <token>`
- Token TTL is short (default 5 minutes) and is signed using `INVITE_TOKEN_SECRET` or `ENCRYPTION_KEY` from backend `.env`.
Security note: Currently the `/invite-token` endpoint issues tokens to any caller. For production you should restrict this endpoint by requiring OAuth authentication and checking that the requesting user is authorized for the target guild.
## Run the app locally
1. Backend
```powershell
cd backend
npm install
# create backend/.env from the example above
npm start
```
ECS-FullStack/
├── frontend/ # React dashboard
├── backend/ # Express API + Discord bot
├── discord-bot/ # Bot wrapper
├── checklist.md # Feature tracking
└── README.md
```
Optional: using Postgres (recommended)
## API Endpoints
1. Create a Postgres database and user (pgAdmin or psql)
2. Set `DATABASE_URL` in `backend/.env`, e.g.:
DATABASE_URL=postgres://dbuser:dbpass@100.111.50.59:5432/ecs_db
3. Start the backend; on startup the backend will create simple tables if missing.
### Server Management
- `GET /api/servers/:guildId` - Server info and settings
- `GET /api/servers/:guildId/members` - Server member list
- `GET /api/servers/:guildId/channels` - Text channels
- `GET /api/servers/:guildId/roles` - Server roles
Migration note:
- If you have existing data in `backend/db.json`, a migration script is planned to import invites and server settings into Postgres. I can add that script on request.
### Invites
- `GET /api/servers/:guildId/invites` - List invites
- `POST /api/servers/:guildId/invites` - Create invite
- `DELETE /api/servers/:guildId/invites/:code` - Delete invite
2. Frontend
### Moderation
- `POST /api/servers/:guildId/moderate` - Ban/kick/timeout users
- `GET /api/servers/:guildId/admin-logs` - View moderation logs
```powershell
cd frontend
npm install
# create frontend/.env with REACT_APP_API_BASE pointing to the backend
npm run start
### Live Notifications
- `GET/POST /api/servers/:guildId/live-notifications` - Settings
- `GET/POST /api/servers/:guildId/twitch-users` - Watched users
## Environment Variables
### Required
- `DATABASE_URL` - PostgreSQL connection string
- `DISCORD_CLIENT_ID` - Discord app client ID
- `DISCORD_CLIENT_SECRET` - Discord app client secret
- `DISCORD_BOT_TOKEN` - Bot token
### Optional
- `TWITCH_CLIENT_ID` - Twitch app client ID
- `TWITCH_CLIENT_SECRET` - Twitch app client secret
- `BOT_PUSH_URL` - For separate bot/backend deployment
- `CORS_ORIGIN` - Restrict API access
## Development
### Running Tests
```bash
cd frontend && npm test
cd backend && npm test
```
3. Discord bot
- The backend boots the bot client (see `discord-bot/`), so if the backend is started and credentials are correct, the bot will log in and register slash commands. You can also run the `discord-bot` project separately if you prefer.
### Building for Production
```bash
cd frontend && npm run build
cd backend && npm run build # If applicable
```
## Troubleshooting
- Backend refuses to start or missing package.json: ensure you run `npm install` in the `backend` folder and run `npm start` from that folder.
- If the backend exits with "DATABASE_URL is not set": either set `DATABASE_URL` in `backend/.env` pointing to your Postgres DB, or restore the legacy behavior by editing `backend/index.js` to re-enable the encrypted `db.json` fallback (not recommended for production).
- CORS errors: verify `CORS_ORIGIN` and `REACT_APP_API_BASE` match your frontend origin.
- Invite delete unauthorized: ensure backend `INVITE_TOKEN_SECRET` or `ENCRYPTION_KEY` is present and token TTL has not expired. Check the backend logs for validation details.
- Token issues: clock skew can cause tokens to appear expired — ensure server and client clocks are reasonably in sync.
### Common Issues
- **Database connection failed**: Verify `DATABASE_URL` format and credentials
- **CORS errors**: Check `CORS_ORIGIN` matches your frontend URL
- **Bot not responding**: Ensure bot has proper permissions in server
- **Invite deletion fails**: Check `ENCRYPTION_KEY` is set
## Developer notes
### Logs
- Backend logs Discord bot status and API requests
- Frontend console shows API calls and errors
- Check browser Network tab for failed requests
- The dashboard UI is in `frontend/src/components/` (notable files: `Dashboard.js`, `ServerSettings.js`, `Login.js`).
- The Express API is in `backend/index.js` and uses `discord-bot` (discord.js client) to operate on guilds, invites, channels and roles.
- Invite delete flow: frontend fetches a short-lived token then requests DELETE with header `x-invite-token`.
## Contributing
## Next steps / suggestions
1. Fork the repository
2. Create feature branch
3. Make changes with tests
4. Submit pull request
- Harden `/api/servers/:guildId/invite-token` to require an authenticated user and verify the user has admin permissions for the guild.
- Add rate-limiting to token issuance and optionally keep the old `INVITE_API_KEY` option for server-to-server automation.
- Updated docs: the README and CHANGELOG were updated to reflect Postgres integration and recent frontend/backend changes. See `CHANGELOG.md` and `checklist.md` for details.
## License
If you want, I can add step-by-step instructions to create the `.env` files from templates, or implement the production safe option of authenticating `/invite-token` requests. Tell me which you'd prefer.
MIT License - see LICENSE file for details.
---
Updated: Oct 4, 2025
## Full setup guide (detailed)
This section walks through the exact steps to get the project running locally or on a machine reachable via Tailscale/Nginx Proxy Manager.
Prerequisites
1. Node.js 18+ and npm
2. Postgres (local or remote) or use an existing Postgres server reachable over your network/Tailscale
3. Discord application + Bot credentials and (optional) Twitch app credentials
Database (Postgres) setup
1. Create a Postgres database and user. Example psql commands:
```bash
sudo -u postgres psql
CREATE DATABASE ecs_fullstack;
CREATE USER ecs_user WITH PASSWORD 'supersecret';
GRANT ALL PRIVILEGES ON DATABASE ecs_fullstack TO ecs_user;
\q
```
2. Set the `DATABASE_URL` in `backend/.env`:
```
DATABASE_URL=postgres://ecs_user:supersecret@127.0.0.1:5432/ecs_fullstack
```
3. Start the backend (it will run migrations / ensure tables at startup):
```powershell
cd backend
npm install
npm start
```
Backend configuration (.env)
- `DATABASE_URL` - required for Postgres persistence
- `DISCORD_CLIENT_ID`, `DISCORD_CLIENT_SECRET`, `DISCORD_BOT_TOKEN` - from Discord Developer Portal
- `FRONTEND_BASE` - public frontend URL (used for OAuth redirect)
- `PORT`, `HOST` - where backend listens
- `CORS_ORIGIN` - optional restrict origin to your frontend URL
- `TWITCH_CLIENT_ID`, `TWITCH_CLIENT_SECRET` - optional for Twitch integration
Frontend configuration
1. In `frontend/.env` set:
```
REACT_APP_API_BASE=https://your-domain-or-ip:3002
```
2. For development behind an HTTPS domain (Nginx Proxy Manager), ensure the CRA dev client uses `wss` by setting the `WDS_SOCKET_*` variables in `frontend/.env` (see docs if using a TLS domain)
Start the frontend dev server:
```powershell
cd frontend
npm install
npm start
```
Bot behaviour and deployment
- The `backend` process will boot the Discord bot client when valid `DISCORD_BOT_TOKEN` is present. The bot registers slash commands per guild on startup and responds to backend pushes for setting updates.
- If you prefer to run the bot separately, you can run the `discord-bot` module separately; ensure `BOT_PUSH_URL`/`BOT_SECRET` are configured if backend and bot are on different hosts.
Useful endpoints
- `GET /api/servers/:guildId/commands` — returns the authoritative list of commands and per-guild enabled/locked status.
- `GET/POST /api/servers/:guildId/live-notifications` — get/update live notification settings
- `GET /api/twitch/streams?users=user1,user2` — proxy to twitch helix for streams (backend caches app-token)
- `GET /api/events?guildId=...` — Server-Sent Events for real-time updates (ServerSettings subscribes to this)
### Twitch Live Notification Settings (Detailed)
Endpoint: `GET/POST /api/servers/:guildId/live-notifications`
Shape returned by GET:
```json
{
"enabled": true,
"channelId": "123456789012345678",
"twitchUser": "deprecated-single-user-field",
"message": "🔴 {user} is now live!",
"customMessage": "Custom promo text with link etc"
}
```
Notes:
- `twitchUser` is a legacy single-user field retained for backward compatibility. The active watched users list lives under `/api/servers/:guildId/twitch-users`.
- `message` (default message) and `customMessage` (override) are persisted. If `customMessage` is non-empty it is used when announcing a live stream; otherwise `message` is used. If both are empty the bot falls back to `🔴 {user} is now live!`.
- Update by POSTing the same shape (omit fields you don't change is okay; unspecified become empty unless preserved on server).
### Discord Bot Twitch Embed Layout
When a watched streamer goes live the bot posts a standardized embed. The layout is fixed to keep consistency:
Embed fields:
1. Title: Stream title (hyperlinked to Twitch URL) or fallback "{user} is live".
2. Author: Twitch display name with avatar and link.
3. Thumbnail: Stream thumbnail (or profile image fallback).
4. Fields:
- Category: Game / category name (or "Unknown").
- Viewers: Current viewer count.
5. Description: Twitch user bio (if available via Helix `users` endpoint) else truncated stream description (200 chars).
6. Footer: `ehchadservices • Started: <localized start time>`.
Pre-Embed Message (optional):
- If `customMessage` is set it is posted as normal message content above the embed.
- Else if `message` is set it is posted above the embed.
- Else no prefix content is posted (embed alone).
Variables:
- `{user}` in messages will not be auto-replaced server-side yet; include the username manually if desired. (Can add template replacement in a future iteration.)
### Watched Users
- Add/remove watched Twitch usernames via `POST /api/servers/:guildId/twitch-users` and `DELETE /api/servers/:guildId/twitch-users/:username`.
- Frontend polls `/api/twitch/streams` every ~15s to refresh live status and renders a "Watch Live" button per user.
- The watcher announces a stream only once per live session; when a user goes offline the session marker clears so a future live event re-announces.
### SSE Event Types Relevant to Twitch
- `twitchUsersUpdate`: `{ users: ["user1", "user2"], guildId: "..." }`
- `liveNotificationsUpdate`: `{ enabled, channelId, twitchUser, message, customMessage, guildId }`
Consume these to live-update UI without refresh (the `BackendContext` exposes an `eventTarget`).
### Customizing Messages
- In the dashboard under Live Notifications you can set both a Default Message and a Custom Message.
- Clear Custom to fall back to Default.
- Save persists to backend and pushes an SSE `liveNotificationsUpdate`.
### Future Improvements
- Template variable replacement: support `{user}`, `{title}`, `{category}`, `{viewers}` inside message strings.
- Per-user custom messages (different prefix for each watched streamer).
- Embed image improvements (dynamic preview resolution trimming for Twitch thumbnails).
Notes about Postgres requirement
- The backend now assumes Postgres persistence (via `DATABASE_URL`). If `DATABASE_URL` is not set the server will exit and complain. This change makes server settings authoritative and persistent across restarts.
Logs and verbosity
- The bot and watcher log messages have been reduced to avoid per-guild spam. You will see concise messages like "🔁 TwitchWatcher started" and "✅ ECS - Full Stack Bot Online!" rather than one-line-per-guild spam.
Troubleshooting
- If you see mixed-content errors in the browser when using a TLS domain with the CRA dev server, configure Nginx to proxy websockets and set CRA `WDS_SOCKET_*` env vars (see docs/nginx-proxy-manager.md)
**Updated**: October 9, 2025