Skip to content

Application overview

Serhii Herasymov edited this page Apr 6, 2026 · 3 revisions

Project Structure

  .
  ├── cmd/
  │   └── server/
  │       └── main.go               # Entry point — loads config, runs migrations, starts Fiber
  │
  ├── src/
  │   ├── config/
  │   │   └── config.go             # Environment-based configuration (envconfig)
  │   │
  │   ├── database/
  │   │   ├── database.go           # Opens and configures the SQLite connection (Bun + WAL mode)
  │   │   ├── migrate.go            # Discovers and applies embedded SQL migrations on startup
  │   │   └── migrations/
  │   │       ├── 20240101000001_create_users.up.sql
  │   │       ├── 20240101000001_create_users.down.sql
  │   │       ├── 20240102000001_create_sessions.up.sql
  │   │       ├── 20240102000001_create_sessions.down.sql
  │   │       ├── 20240103000001_create_settings.up.sql   # seeds setup_complete = false
  │   │       └── 20240103000001_create_settings.down.sql
  │   │
  │   ├── handlers/
  │   │   ├── auth.go               # RequireAuth middleware — validates session cookie
  │   │   ├── health.go             # GET /api/health
  │   │   ├── sessions.go           # POST /api/sessions (login), DELETE /api/sessions (logout)
  │   │   ├── settings.go           # CRUD /api/settings — key-value store, all routes protected
  │   │   ├── users.go              # CRUD /api/users — Create conditionally protected by setup_complete
  │   │   ├── validate.go           # Shared go-playground/validator instance + error formatting
  │   │   ├── health_test.go
  │   │   ├── sessions_test.go
  │   │   ├── settings_test.go
  │   │   ├── users_test.go
  │   │   └── suite_test.go         # Ginkgo suite bootstrap + shared test constants
  │   │
  │   ├── models/
  │   │   ├── id.go                 # NewID() — Sqid-based URL-safe unique IDs
  │   │   ├── password.go           # HashPassword / VerifyPassword — argon2id (PHC format)
  │   │   ├── session.go            # Session model + NewSessionToken() (32-byte random)
  │   │   ├── setting.go            # Setting model (key / value)
  │   │   └── user.go               # User model
  │   │
  │   └── server/
  │       └── server.go             # Fiber app factory — wires middleware, handlers, SPA fallback
  │
  ├── web/                          # React + Vite + TypeScript frontend
  │   ├── src/
  │   │   ├── App.tsx
  │   │   ├── App.css
  │   │   └── main.tsx
  │   ├── embed.go                  # //go:embed dist — bakes the built frontend into the binary
  │   ├── index.html
  │   ├── vite.config.ts            # Dev proxy → Go API; polling HMR for Docker
  │   ├── tsconfig.json
  │   ├── package.json
  │   └── package-lock.json
  │
  ├── docs/
  │   ├── docs.go                   # Auto-generated by swag — imported for Swagger UI side-effect
  │   └── swagger.json              # Auto-generated OpenAPI 2.0 spec
  │
  ├── http-client/                  # JetBrains HTTP Client request files
  │   ├── http-client.env.json      # Environment definitions (local, docker)
  │   ├── sessions/
  │   │   └── sessions.http         # Login / logout requests
  │   ├── settings/
  │   │   └── settings.http         # Settings CRUD requests
  │   └── users/
  │       └── users.http            # Users CRUD requests
  │
  ├── .github/
  │   └── workflows/
  │       └── docker-publish.yml    # CI: test → codacy-analysis + codacy-coverage + build-and-push + publish-openapi
  │
  ├── .air.toml                     # Air hot-reload config (Go 1.26.1, watches .go and .sql)
  ├── .codacy.yml                   # Codacy — excludes web/ from analysis
  ├── .dockerignore
  ├── .gitignore
  ├── docker-compose.yml            # API (published image) + React dev server with HMR
  ├── Dockerfile                    # Multi-stage: build → strip → scratch final image
  ├── go.mod
  ├── go.sum
  ├── Makefile                      # run, build, test, coverage, openapi, web, docker, clean
  └── README.md

Key packages

Package Responsibility
cmd/server Binary entry point
src/config Typed env config via envconfig
src/database DB connection + auto-migration runner
src/handlers HTTP handlers, auth middleware, request validation
src/models Domain types, ID generation, password hashing
src/server Fiber app wiring (middleware stack, route registration, SPA fallback)
web React/Vite SPA embedded into the binary at compile time
docs Auto-generated Swagger/OpenAPI docs (do not edit manually)

Configuration

All options are set via environment variables. Defaults are suitable for local development.

Variable Default Description
ADDR :3000 Listen address
DATABASE_DSN file:data.db?cache=shared&_pragma=journal_mode(WAL) SQLite DSN
DEBUG false Enable Bun query logging
SESSION_COOKIE_NAME c3_session Name of the HTTP session cookie

Database migrations

Migrations live in src/database/migrations/ as plain SQL files and are applied automatically on every startup using Bun's migration runner. Applied migrations are recorded in bun_migrations and are never re-run.

Migration Description
20240101000001_create_users users table
20240102000001_create_sessions sessions table with ON DELETE CASCADE from users
20240103000001_create_settings settings key-value table; seeds setup_complete = false

Authentication model

  • Sessions are created via POST /api/sessions and stored in the sessions table with a 7-day TTL.
  • The session token is a 32-byte cryptographically random value sent and read as an HttpOnly; SameSite=Lax cookie.
  • The RequireAuth middleware rejects requests with a missing, unknown, or expired token.
  • POST /api/users (user creation) is conditionally protected: open while setup_complete = false, enforces auth once setup_complete = true. This enables first-run setup without a bootstrapped admin account.

Clone this wiki locally