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
- 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 status —
scheduled → on_going → donebased on real time - Unit tested — service layer, handlers, workers all tested with fake stores
- Production deployed — running on VPS with Docker + CI/CD
┌─────────────────────────────────────────────────────────┐
│ 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_atvsnow()
| 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)
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
| Method | Path | Description |
|---|---|---|
POST |
/auth/login |
Admin login, returns HMAC token |
| 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) |
| Method | Path | Description |
|---|---|---|
GET |
/public/schedules?date=YYYY-MM-DD |
Occupied time slots (no sensitive data) |
| Method | Path | Description |
|---|---|---|
GET |
/health |
Liveness + DB ping |
- Go 1.24+
- Docker & Docker Compose
git clone https://github.com/alpardfm/haze-api.git
cd haze-api
cp .env.example .envdocker compose up -d postgresgo run ./cmd/migratego run ./cmd/seed-admingo run ./cmd/apiAPI available at http://localhost:8080. Health check: GET /health.
go run ./cmd/reminder-worker # Run once: process due reminders
go run ./cmd/status-worker # Run once: update stale statusesgo 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
- Fixed duration: 120 minutes per appointment (v1)
- Overlap prevention: Create/update rejected if time range conflicts
- Computed status:
scheduled→on_going→donebased on current time vsstart_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
Production runs at alpardfm.my.id/api/haze via:
- Push/merge to
master→ GitHub Actions runs tests - Code synced to VPS via rsync
- Docker containers rebuilt + restarted
- Migrations + admin seed run automatically
- haze-ui — React + TypeScript frontend for this API
MIT License. See LICENSE for details.