initial commit from original sev version

This commit is contained in:
Bartal Læarsson
2026-06-25 12:03:26 +01:00
commit 9c157cc5a0
31 changed files with 468875 additions and 0 deletions
+211
View File
@@ -0,0 +1,211 @@
# Abuse Registration API POC
A stripped-down, in-memory API proof-of-concept. It keeps the important authentication shape from the original API while removing backend/database business logic.
The demo data is synthetic, stored in memory, and reset to the base dataset every 10 minutes. The base dataset contains 546 rows with `registered_at` dates from 1988 through 1991.
The HTML template, CSS, JavaScript, and favicon are embedded with Go `embed`. A built binary can be copied and run by itself; it does not need a neighboring `static/`, `templates/`, or `favicon.ico` file.
The JSON model is intentionally small:
```json
{
"id": "bb8d39a6-8fef-48af-9f9c-27a41c8f8baf",
"registered_at": "1989-04-13T15:22:00Z",
"gender": "female",
"location": "Tórshavn",
"abuse_type": "psychological",
"status": "new"
}
```
`id` is generated by the server as a UUID. `registered_at` defaults to `time.Now().UTC()` when omitted on create or update.
## Run locally
```bash
go run ./cmd/api
```
Or with Task:
```bash
task run
```
Then open:
```text
http://localhost:8080/
```
Optional environment variables:
```bash
PORT=:9999 JWT_SECRET=change-me RESET_INTERVAL=10m go run ./cmd/api
```
## Build
The Taskfile follows the FLÓ-style build version format:
```text
version [<git-sha>-<branch>/<random-word>-<random-word>]. git is [clean|dirty].
```
`build/words.txt` is copied from the FLÓ website project.
```bash
task build
./bin/abuse-registration-poc
```
The resulting `./bin/abuse-registration-poc` is self-contained. You can copy just that file to another Linux host and run it.
## Project layout
The POC is intentionally small, but it is no longer a single-file prototype. The code is split in the same broad shape as the original API:
```text
cmd/api/main.go binary entry point and flags
internal/config environment/default configuration
internal/models user and registration models plus allowed values
internal/auth static POC users replacing the auth database
internal/utils JWT and UUID helpers
internal/store in-memory registration store and reset logic
internal/handlers HTTP handlers for login, health, page, and data
internal/middlewares authentication, authorization, and CORS
internal/routes route registration and protected route wiring
internal/web/templates/index.html single HTML page template, embedded into the binary
internal/web/static embedded CSS and browser JavaScript
internal/web/favicon.ico embedded favicon
```
The real database/repository layer is deliberately replaced by `internal/store`, but the login response shape, raw `Authorization` JWT usage, role split, and route-permission middleware are kept close to the original API. Web assets are separate source files, but are compiled into the binary at build time.
## Demo users
| User | Password | Role | Access |
|---|---|---|---|
| `reader` | `reader-password` | `Reader` | Read protected endpoints |
| `admin` | `admin-password` | `Admin` | Full create/read/update/delete and manual reset |
## Auth workflow
The site does not contain a login form and does not acquire a token in the browser. Use curl or PowerShell only.
Reader with curl:
```bash
READER_TOKEN=$(curl -s -X POST http://localhost:8080/login \
-H 'Content-Type: application/json' \
-d '{"user_name":"reader","password":"reader-password"}' \
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
curl -s 'http://localhost:8080/api/v1/registrations?location=Tórshavn&limit=5' \
-H "Authorization: $READER_TOKEN"
curl -s 'http://localhost:8080/api/v1/registrations?gender=female&abuse_type=psychological&status=open&limit=10' \
-H "Authorization: $READER_TOKEN"
curl -s 'http://localhost:8080/api/v1/registrations?from=1989-01-01&to=1990-01-01&offset=20&limit=10' \
-H "Authorization: $READER_TOKEN"
```
Reader with PowerShell:
```powershell
$reader = Invoke-RestMethod `
-Method Post `
-Uri "http://localhost:8080/login" `
-ContentType "application/json" `
-Body '{"user_name":"reader","password":"reader-password"}'
$READER_TOKEN = $reader.token
Invoke-RestMethod `
-Uri "http://localhost:8080/api/v1/registrations?location=Tórshavn&limit=5" `
-Headers @{Authorization=$READER_TOKEN}
```
Admin workflow:
```bash
ADMIN_TOKEN=$(curl -s -X POST http://localhost:8080/login \
-H 'Content-Type: application/json' \
-d '{"user_name":"admin","password":"admin-password"}' \
| sed -n 's/.*"token":"\([^"]*\)".*/\1/p')
CREATED_ID=$(curl -s -X POST http://localhost:8080/api/v1/registrations \
-H "Authorization: $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"gender":"unknown","location":"Tórshavn","abuse_type":"psychological","status":"new"}' \
| sed -n 's/.*"id":"\([^"]*\)".*/\1/p')
curl -s -X PUT "http://localhost:8080/api/v1/registrations/$CREATED_ID" \
-H "Authorization: $ADMIN_TOKEN" \
-H 'Content-Type: application/json' \
-d '{"gender":"female","location":"Skopun","abuse_type":"digital","status":"referred"}'
curl -s -X DELETE "http://localhost:8080/api/v1/registrations/$CREATED_ID" \
-H "Authorization: $ADMIN_TOKEN"
```
## Endpoint behavior
Public:
- `GET /` returns the single HTML page only. It does not embed all 546 registrations into the HTML.
- `GET /health`
- `POST /login`
- `GET /demo/registrations` is the public demo snapshot used by the page JSON viewer.
Protected read:
- `GET /api/v1/categories`
- `GET /api/v1/locations`
- `GET /api/v1/registrations`
- `GET /api/v1/registrations/{uuid}`
Admin create/read/update/delete:
- `POST /api/v1/registrations`
- `PUT /api/v1/registrations/{uuid}`
- `DELETE /api/v1/registrations/{uuid}`
- `POST /api/v1/reset`
Unknown normal pages return a plain text message. Unknown `/api/...` routes return JSON.
## Allowed values
Allowed `gender` values:
- `female`
- `male`
- `non_binary`
- `unknown`
Allowed `abuse_type` values:
- `physical`
- `psychological`
- `sexual`
- `economic`
- `material`
- `digital`
- `stalking`
- `threats`
- `honor_related`
Allowed `location` values are fixed to the included Faroese town/village list in `internal/models/registration.go`. The browser filter dropdown uses this same list, and create/update rejects anything else.
Filters for `GET /api/v1/registrations` and `/demo/registrations`:
- `abuse_type`
- `gender`
- `location` — exact match against the fixed list
- `status`
- `from` and `to`, matched against `registered_at`, accepting `YYYY-MM-DD` or RFC3339 values
- `search`
- `limit`
- `offset`