Skip to content

Repository files navigation

Haze API

Go PostgreSQL Docker Zero Dependencies CI/CD

Admin scheduling & reminder backend API — built with zero framework dependencies. Pure Go stdlib net/http + pgx only.

🚀 Live: alpardfm.my.id/api/haze 📖 Swagger: alpardfm.my.id/api/haze/swagger


Highlights

  • Zero framework — pure net/http + pgx/v5, no Gin/Echo/Chi
  • Background workers — reminder scheduler + auto status updater
  • Overlap validation — prevents double-booking with time range checks
  • Computed statusscheduled → on_going → done based on real time
  • Unit tested — service layer, handlers, workers all tested with fake stores
  • Production deployed — running on VPS with Docker + CI/CD

Architecture

┌─────────────────────────────────────────────────────────┐
│                     HTTP Layer                            │
│  net/http Router → Auth Middleware → Handler              │
├─────────────────────────────────────────────────────────┤
│                   Module Layer                            │
│  auth │ appointment │ publicschedule │ health             │
│  Each module: handler → service (interface) → store       │
├─────────────────────────────────────────────────────────┤
│                  Worker Layer                             │
│  reminder-worker │ status-worker (one-shot commands)      │
├─────────────────────────────────────────────────────────┤
│                  Platform Layer                           │
│              database │ config │ shared/response           │
├─────────────────────────────────────────────────────────┤
│                   PostgreSQL                              │
└─────────────────────────────────────────────────────────┘

Design decisions:

  • Interface-based Store pattern — services depend on interfaces, not implementations
  • Dependency injection via struct fields — no DI container, no magic
  • Workers as separate binaries — can run independently or in Docker
  • Computed status — never stored stale, always derived from start_at/end_at vs now()

Tech Stack

Layer Technology
Language Go 1.24
HTTP stdlib net/http (no framework)
Database PostgreSQL via pgx/v5
Auth HMAC token (crypto/hmac)
Password bcrypt (golang.org/x/crypto)
Migrations Raw SQL files
Container Docker multi-stage build
CI/CD GitHub Actions → VPS deploy

Total direct dependencies: 2 (pgx/v5 + golang.org/x/crypto)


Project Structure

haze-api/
├── cmd/
│   ├── api/              # HTTP server entrypoint
│   ├── migrate/          # Database migration runner
│   ├── seed-admin/       # Initial admin seeder
│   ├── reminder-worker/  # Reminder notification scheduler
│   └── status-worker/    # Auto status updater (scheduled→on_going→done)
├── internal/
│   ├── appointment/      # Core domain: CRUD + overlap + computed status
│   ├── auth/             # Login, token, middleware, password hashing
│   ├── publicschedule/   # Public schedule checker (no sensitive data)
│   ├── worker/           # Reminder + status worker logic
│   ├── notification/     # Notification log model
│   ├── health/           # Health check handler
│   ├── config/           # Environment-based configuration
│   ├── database/         # PostgreSQL connection + migration runner
│   └── shared/response/  # JSON response helpers
├── migrations/           # SQL migration files
├── docs/                 # OpenAPI spec + business rules
├── scripts/              # Deploy scripts
├── Dockerfile            # Multi-stage build (all 5 binaries)
└── docker-compose.yml

API Endpoints

Auth

Method Path Description
POST /auth/login Admin login, returns HMAC token

Appointments (Protected)

Method Path Description
GET /appointments List appointments (filter: date, status)
POST /appointments Create appointment (validates overlap)
GET /appointments/:id Get appointment detail
PUT /appointments/:id Update appointment
PATCH /appointments/:id/cancel Cancel appointment (idempotent)

Public

Method Path Description
GET /public/schedules?date=YYYY-MM-DD Occupied time slots (no sensitive data)

System

Method Path Description
GET /health Liveness + DB ping

Quick Start

Prerequisites

  • Go 1.24+
  • Docker & Docker Compose

1. Clone & Setup

git clone https://github.com/alpardfm/haze-api.git
cd haze-api
cp .env.example .env

2. Start Database

docker compose up -d postgres

3. Run Migrations

go run ./cmd/migrate

4. Seed Admin

go run ./cmd/seed-admin

5. Run API

go run ./cmd/api

API available at http://localhost:8080. Health check: GET /health.

6. Run Workers (optional)

go run ./cmd/reminder-worker  # Run once: process due reminders
go run ./cmd/status-worker    # Run once: update stale statuses

Testing

go test ./...

Tests use fake store implementations (no database required). Covered:

  • Appointment service: create, update, cancel (idempotent), list with computed status, overlap rejection
  • Auth handler & middleware
  • Public schedule service & handler
  • Worker: reminder slot calculation, anti-double-send
  • Shared response helpers

Business Rules

  • Fixed duration: 120 minutes per appointment (v1)
  • Overlap prevention: Create/update rejected if time range conflicts
  • Computed status: scheduledon_goingdone based on current time vs start_at/end_at
  • Cancel is idempotent: Cancelling an already-cancelled appointment returns success
  • Reminder: Configurable per appointment (reminder_start_at + interval_hours), anti-double-send via notification logs
  • Public checker: Only exposes occupied time slots, never client names or notes

Deployment

Production runs at alpardfm.my.id/api/haze via:

  1. Push/merge to master → GitHub Actions runs tests
  2. Code synced to VPS via rsync
  3. Docker containers rebuilt + restarted
  4. Migrations + admin seed run automatically

Related

  • haze-ui — React + TypeScript frontend for this API

License

MIT License. See LICENSE for details.

About

Admin scheduling & reminder API — zero framework, pure Go stdlib + pgx only.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages