Initial commit: TicketingSystem
Internal ticketing app with CTI routing, severity levels, and n8n integration. Stack: Express + TypeScript + Prisma + PostgreSQL / React + Vite + Tailwind - JWT auth for users, API key auth for service accounts (Goddard/n8n) - CTI hierarchy (Category > Type > Item) for ticket routing - Severity 1-5, auto-close resolved tickets after 14 days - Gitea Actions CI/CD building separate server/client images - Production docker-compose.yml with Traefik integration Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
213
README.md
Normal file
213
README.md
Normal file
@@ -0,0 +1,213 @@
|
||||
# TicketingSystem
|
||||
|
||||
Internal ticketing system with CTI-based routing, severity levels, and n8n/automation integration.
|
||||
|
||||
## Features
|
||||
|
||||
- **CTI routing** — tickets are categorised by Category → Type → Item, reroutable at any time
|
||||
- **Severity 1–5** — SEV 1 (critical) through SEV 5 (minimal); dashboard sorts by severity
|
||||
- **Status lifecycle** — Open → In Progress → Resolved → Closed; resolved tickets auto-close after 14 days
|
||||
- **Comments** — threaded comments per ticket with author attribution
|
||||
- **Roles** — Admin, Agent, Service (API key auth for automation accounts)
|
||||
- **Admin panel** — manage users and the full CTI hierarchy via UI
|
||||
- **n8n ready** — service accounts authenticate via `X-Api-Key` header
|
||||
|
||||
---
|
||||
|
||||
## Production Deployment
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Docker + Docker Compose
|
||||
- Traefik running with a `proxy` Docker network
|
||||
- Access to your Gitea container registry
|
||||
|
||||
### 1. Copy files to your server
|
||||
|
||||
```bash
|
||||
scp docker-compose.yml .env.example user@your-server:~/ticketing/
|
||||
```
|
||||
|
||||
### 2. Configure environment
|
||||
|
||||
```bash
|
||||
cd ~/ticketing
|
||||
cp .env.example .env
|
||||
```
|
||||
|
||||
Edit `.env`:
|
||||
|
||||
```env
|
||||
REGISTRY=gitea.thewrightserver.net
|
||||
TAG=latest
|
||||
POSTGRES_PASSWORD=<strong password>
|
||||
JWT_SECRET=<output of: openssl rand -hex 64>
|
||||
DOMAIN=tickets.thewrightserver.net
|
||||
```
|
||||
|
||||
### 3. Create the initial database migration
|
||||
|
||||
Run this **once** on your local dev machine before first deploy, then commit the result:
|
||||
|
||||
```bash
|
||||
cd server
|
||||
npm install
|
||||
# point at your local postgres or use DATABASE_URL from .env
|
||||
npm run db:migrate # creates prisma/migrations/
|
||||
```
|
||||
|
||||
Commit the generated `server/prisma/migrations/` folder — `prisma migrate deploy` in the container will apply it on startup.
|
||||
|
||||
### 4. Deploy
|
||||
|
||||
```bash
|
||||
docker compose pull
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
### 5. Seed (first deploy only)
|
||||
|
||||
```bash
|
||||
docker compose exec server npm run db:seed
|
||||
```
|
||||
|
||||
This creates:
|
||||
- `admin` user (password: `admin123`) — **change this immediately**
|
||||
- `goddard` service account — API key is printed to the console; copy it now
|
||||
|
||||
---
|
||||
|
||||
## Development
|
||||
|
||||
### Requirements
|
||||
|
||||
- Node.js 22+
|
||||
- PostgreSQL (local or via Docker)
|
||||
|
||||
### Start Postgres
|
||||
|
||||
```bash
|
||||
docker run -d \
|
||||
--name ticketing-pg \
|
||||
-e POSTGRES_DB=ticketing \
|
||||
-e POSTGRES_USER=postgres \
|
||||
-e POSTGRES_PASSWORD=postgres \
|
||||
-p 5432:5432 \
|
||||
postgres:16-alpine
|
||||
```
|
||||
|
||||
### Server
|
||||
|
||||
```bash
|
||||
cd server
|
||||
cp .env.example .env # set DATABASE_URL and JWT_SECRET
|
||||
npm install
|
||||
npm run db:migrate # creates tables + migration files
|
||||
npm run db:seed # seeds admin + Goddard + sample CTI
|
||||
npm run dev # http://localhost:3000
|
||||
```
|
||||
|
||||
### Client
|
||||
|
||||
```bash
|
||||
cd client
|
||||
npm install
|
||||
npm run dev # http://localhost:5173 (proxies /api to :3000)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## n8n Integration (Goddard)
|
||||
|
||||
The `goddard` service account authenticates via API key — no login flow needed.
|
||||
|
||||
**Create a ticket from n8n:**
|
||||
|
||||
```
|
||||
POST https://tickets.thewrightserver.net/api/tickets
|
||||
X-Api-Key: sk_<goddard api key>
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"title": "[Plex] Backup - 2026-03-30T02:00:00",
|
||||
"overview": "Automated nightly Plex backup completed.",
|
||||
"severity": 5,
|
||||
"categoryId": "<TheWrightServer category ID>",
|
||||
"typeId": "<Automation type ID>",
|
||||
"itemId": "<Backup item ID>",
|
||||
"assigneeId": "<Goddard user ID>"
|
||||
}
|
||||
```
|
||||
|
||||
CTI IDs can be fetched from:
|
||||
- `GET /api/cti/categories`
|
||||
- `GET /api/cti/types?categoryId=<id>`
|
||||
- `GET /api/cti/items?typeId=<id>`
|
||||
|
||||
To regenerate the Goddard API key: Admin → Users → refresh icon next to Goddard.
|
||||
|
||||
---
|
||||
|
||||
## CI/CD
|
||||
|
||||
Push to `main` triggers `.gitea/workflows/build.yml`, which builds and pushes two images in parallel:
|
||||
|
||||
| Image | Tag |
|
||||
|---|---|
|
||||
| `$REGISTRY/josh/ticketing-server` | `latest`, `<git sha>` |
|
||||
| `$REGISTRY/josh/ticketing-client` | `latest`, `<git sha>` |
|
||||
|
||||
**Gitea repository secrets/variables required:**
|
||||
|
||||
| Name | Type | Value |
|
||||
|---|---|---|
|
||||
| `REGISTRY` | Variable | `gitea.thewrightserver.net` |
|
||||
| `REGISTRY_TOKEN` | Secret | Gitea personal access token with `write:packages` |
|
||||
|
||||
Set these under **Repository → Settings → Actions → Variables / Secrets**.
|
||||
|
||||
To deploy a specific commit SHA instead of latest:
|
||||
|
||||
```bash
|
||||
TAG=<sha> docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Required | Description |
|
||||
|---|---|---|
|
||||
| `DATABASE_URL` | Yes | PostgreSQL connection string |
|
||||
| `JWT_SECRET` | Yes | Secret for signing JWTs — use `openssl rand -hex 64` |
|
||||
| `CLIENT_URL` | Yes | Allowed CORS origin (your domain) |
|
||||
| `PORT` | No | Server port (default: `3000`) |
|
||||
| `REGISTRY` | Deploy | Container registry hostname |
|
||||
| `POSTGRES_PASSWORD` | Deploy | Postgres password |
|
||||
| `DOMAIN` | Deploy | Public domain for Traefik routing |
|
||||
| `TAG` | Deploy | Image tag to deploy (default: `latest`) |
|
||||
|
||||
---
|
||||
|
||||
## Ticket Severity
|
||||
|
||||
| Level | Label | Meaning |
|
||||
|---|---|---|
|
||||
| 1 | SEV 1 | Critical — immediate action required |
|
||||
| 2 | SEV 2 | High — significant impact |
|
||||
| 3 | SEV 3 | Medium — standard priority |
|
||||
| 4 | SEV 4 | Low — minor issue |
|
||||
| 5 | SEV 5 | Minimal — informational / automated |
|
||||
|
||||
Tickets are sorted SEV 1 → SEV 5 on the dashboard. Paging by severity is planned for a future release.
|
||||
|
||||
---
|
||||
|
||||
## Ticket Status Lifecycle
|
||||
|
||||
```
|
||||
OPEN → IN_PROGRESS → RESOLVED ──(14 days)──→ CLOSED
|
||||
↑
|
||||
re-opens reset
|
||||
the 14-day timer
|
||||
```
|
||||
Reference in New Issue
Block a user