API Backend (bme-konza-api)

The backbone of the platform.

Stack

  • Go (module declares go 1.23; CI builds with 1.25), Fiber v2 HTTP framework.
  • PostgreSQL. Two access paths:
  • GORM (gorm.io/driver/postgres) for application queries.
  • River (riverqueue/river over riverpgxv5, i.e. a pgx/v5 pool) for the background job queue — see Background workers.
  • Schema via goose migrations (db/migrations/, embedded through db/embed.go).
  • Firebase Admin SDK (Auth, FCM push, Cloud Storage).
  • Cobra CLI (konza <command>) + Viper/godotenv config.
  • Build: nixpacks.tomlgo build -o konza main.go; run: ./konza server.

Repo layout

main.go                 # entrypoint → cmd.Execute(); also holds Swagger annotations
cmd/                    # Cobra commands (see below)
pkg/
  api/                  # HTTP handlers + route registration (routes_v1.go, routes_v2.go)
  services/             # business logic (one *_service.go per domain)
  models/               # data models / DB structs
  middleware/           # auth_middleware, jwt_middleware, role_middleware
  config/               # config.go (Config struct)
  firebase/             # Firebase Admin SDK init
  logging/              # logger
  Reports/              # report generation
  schema/, validation/, utils/, ProblemDetail/, testutils/
internal/workers/       # background workers (see below)
db/migrations/          # goose SQL migrations (50 files)
docs/                   # swagger.yaml + docs.go (generated OpenAPI)
public/                 # static assets served by the API
logs/                   # logfile.log (LOGGING_FILE)

CLI commands (konza ...)

From cmd/root.go:

Command Purpose
server Run the HTTP API
migrate Run DB migrations
user --email --name --role --password [--deviceToken] Create a user (e.g. first admin)
update_data & one-off scripts Data backfills: update question/response counts, sync resource stats, mark notifications read, create/delete reputation data, backfill Firebase UIDs

These backfill commands are one-shot maintenance scripts tied to specific migrations. [TODO: note which have already been run in prod so they aren't re-run by mistake.]

Configuration

Config is bound from environment variables (.env) — see .env.example — or konza.yml (konza.example.yml). Key groups:

Group Keys Notes
Server SERVER_HOST, SERVER_PORT (8000), SERVER_CORS_ALLOW_ORIGINS/HEADERS CORS origins must include the web/admin domains
Database DATABASE_HOST/PORT/NAME/USERNAME/PASSWORD/SSLMODE see 06
Auth AUTH_JWT_SECRET_KEY 🔑, AUTH_JWT_EXPIRY_TIMEOUT_SECONDS, AUTH_SESSION_LIFETIME, AUTH_MAX_LOGIN_ATTEMPTS (7), AUTH_REQUIRE_VERIFIED_ACCOUNTS JWT-based; secret in vault
Firebase FIREBASE_FILE_NAME (<PROD_FIREBASE_PROJECT>.json), FIREBASE_SERVICE_FILE (remote link to the service file), FIREBASE_PROJECT_ID, FIREBASE_BUCKET_NAME 🔑 see 11
Email (SMTP) SMTP_HOST/PORT/PASSWORD/FROM_EMAIL/TO_EMAIL/REPORTS_EMAIL 🔑 reports + notifications
Reputation points NEW_QUESTION_POINTS (5), UPVOTE_ANSWER_POINTS (10), ANSWER_ACCEPTED_POINTS (15), NEW_RESPONSE_POINTS (5) tune scoring here
Logging LOGGING_LEVEL (debug), LOGGING_FILE see 13
Timeouts RESPONSE_TIMEOUT, DATABASE_TIMEOUT, SOFT_STOP_TIMEOUT, HARD_STOP_TIMEOUT graceful shutdown

Authentication & authorization

The API has two route groups (pkg/api/api.go mounts both):

  • v2 (current) — authenticates via Firebase Auth and applies role-based authorization. Roles (pkg/models/user.go): Member, Admin, Superuser.
  • v1 (legacy) — an older token-based version, being retired in favour of v2.

New work should target v2. The migration links users to Firebase identities (UpdateUserRecordsWithFirebaseUid backfill).

Detailed auth internals and the v1 retirement plan are tracked in the private operations notes (not published here).

HTTP surface

Base paths api/v1/ and api/v2/ (mounted in pkg/api/api.go). Static files are served at /static (from ./public) and the full, authoritative API spec is the Swagger UI at /swagger/*.

🔒 = authentication required. [A] = Admin/Superuser role, [S] = Superuser role (v2).

v1 — /api/v1/* (routes_v1.go)

Domain Endpoints
Equipment GET equipments, GET equipments/:id, 🔒POST/PUT/DELETE equipments
Brands GET brands, GET brands/:id, GET brands/:equipmentID/filter-by-equipment, 🔒POST/PUT/DELETE brands
Models GET models, GET models/:brandID/filter-by-brand, GET models/:equipmentID/filter-by-equipment, GET models/:equipmentID/filter-by-equipment-brand/:brandID, 🔒POST/PUT/DELETE models
Resources GET resources, GET resources/:id, GET resources/:id/models, GET resources/filter/:type, GET resources/filter/limited/:type, GET resources/categories/:type, 🔒POST/PUT/DELETE resources
Q&A GET questions, GET questions/:id, GET questions/filter/limit, GET questions/unanswered/get, GET questions/category/:categoryId, 🔒POST/PUT/DELETE questions, 🔒GET questions/user/get, 🔒GET questions/user/interests[/unanswered]
Responses GET responses/:questionID, 🔒POST responses, 🔒GET/PUT/DELETE responses/:id, 🔒POST responses/:id (upvote), 🔒GET responses/upvotes/:questionID, 🔒PUT responses/:id/accept, 🔒PUT responses/:id/unmark-as-solution
Users 🔒GET/DELETE users/:id, 🔒POST users, 🔒GET users/view/:id (public view), 🔒admin-users, 🔒regular-users, 🔒 bookmarks (get/create/:id), 🔒 preferences (new/get/delete/:id), 🔒PATCH users/update-title, 🔒PATCH user/country/update, 🔒PATCH users/volunteer-status/toggle, 🔒GET users/:id/points, 🔒GET peers/interest/filter
Experience 🔒GET/POST/PATCH experiences, 🔒GET experiences/get/:id, 🔒DELETE experiences/:id, 🔒PATCH user/experiences/change-visibility
Tiers / Leaderboard 🔒GET/POST/PUT/DELETE tiers, 🔒GET tiers/:id, 🔒GET leaderboard/:tierID, 🔒GET leaderboard/:userID/user
Volunteers 🔒GET volunteers
Notifications 🔒GET notifications, 🔒GET notifications/unread, 🔒PATCH notifications/:id/mark-read, 🔒PATCH notifications/mark-all-read
Announcements 🔒GET/POST announcements, 🔒DELETE announcements/:id
Feedback POST feedback, 🔒GET feedback, 🔒GET feedback/:id
Tags GET tags, 🔒POST/PATCH/DELETE tags
Categories GET categories, 🔒POST/PUT/DELETE categories
Requests POST requests, 🔒GET requests
Device IDs POST device-id/save, GET device-id
Analytics GET analytics, GET analytics/rank-data, PATCH analytics/add-view-count/:questionID, PATCH analytics/add-resource-view-count/:resourceID, PATCH analytics/update-download-count/:resourceID, POST /pdf/generate
Search GET search, GET search-with-filters, GET filter/:modelID

v2 — /api/v2/* (routes_v2.go)

v2 redefines the write/admin side with Firebase auth + roles, and adds new features. Public reads (questions, tags, search, active banners) need no auth.

Domain Endpoints
Equipment/Brands/Models 🔒[A]POST/PUT/DELETE for each
Resources 🔒[A]POST/PUT resources, 🔒[S]DELETE resources/:id
Q&A GET questions, GET questions/:id, GET questions/filter/limit, GET questions/unanswered/get, GET questions/category/:id, 🔒POST/PUT/DELETE questions, 🔒POST responses, 🔒PUT/DELETE responses/:id, 🔒GET questions/user/get, 🔒GET questions/user/interests[/unanswered]
Users 🔒[S]DELETE users/:id, 🔒[S]POST users, 🔒[S]GET admin-users, 🔒[A]GET regular-users, 🔒 bookmarks (get/create/:id)
Tiers / Leaderboard 🔒[A]GET/POST/PUT/DELETE tiers, 🔒[A]GET leaderboard/...
Announcements 🔒[A]GET/POST/DELETE announcements
Feedback 🔒[A]GET feedback, 🔒[A]GET feedback/:id
Tags GET tags, 🔒[A]POST/PATCH tags, 🔒[S]DELETE tags/:id
Categories 🔒[A]POST/PUT categories, 🔒[S]DELETE categories/:id
Banners (new) GET banners/active, 🔒[A]GET/POST/PUT/DELETE banners
Reports (new) 🔒[S]GET reports, 🔒[S]DELETE reports/:id
Requests 🔒[A]GET requests
Search GET search-with-filters
Admin group admin/ 🔒[A]POST admin/remove-question/:id, 🔒[A]POST admin/remove-response/:id, 🔒[A]GET admin/reports, 🔒[A]DELETE admin/reports/:id

API docs (Swagger / OpenAPI)

Swagger UI is served at /swagger/* (gofiber/swagger). Annotations live in main.go and handlers; the generated spec is docs/swagger.yaml (+ docs.go). Regenerate with swag init after changing annotations.

Background workers

Background jobs run on River (riverqueue/river), a Postgres-backed job queue. The River client and workers are wired up in-process in cmd/server.go — so they run inside the konza server process, not as a separate deployment. Recurring work is registered via River periodic jobs (RiverClient.PeriodicJobs().Add(...)), which replaces the old "cron" model. Default queue, MaxWorkers: 100.

Worker implementations live in internal/workers/:

Worker (River) Purpose
MonthlyReportWorker Generate/email monthly reports
ProcessReputationPointsWorker Award/recompute reputation points
QaReminderWorker Remind about unanswered questions
QuestionNotificationWorker Notify on new questions/answers (FCM)
SaveSearchWorker Persist search queries (search_state_log)
UserInteractionReportWorker User-activity reporting

River keeps its own tables in Postgres (job state, queues, leadership). The periodic-job schedules are defined in cmd/server.go. [TODO: list each periodic job's interval.]

Tests

go test ./... (CI runs this on every PR). Helpers in pkg/testutils. [TODO: note any tests needing a live DB/Firebase and how to run them locally.]