This commit is contained in:
Dmitri 2026-04-15 17:56:12 +02:00
parent 356cae89b2
commit aaedeaa450
Signed by: kanopo
GPG Key ID: 759ADD40E3132AC7
3 changed files with 111 additions and 12 deletions

View File

@ -1,6 +1,18 @@
# Rhythm Backend (Go) # Rhythm Backend (Go)
Lean Go API backend structure for ISeeU Tracker. Lean Go API backend for ISeeU Tracker.
## Current Project State
- [x] Go module initialized (`go.mod`)
- [x] Entry point created (`cmd/api/main.go`)
- [x] Env config package created (`internal/config/config.go`)
- [x] `.env` loading added with required DB variables
- [x] Local Postgres service available in `compose.yaml` (dev profile)
- [ ] Database connection package (`internal/db`) not implemented yet
- [ ] Goose migrations folder/files not created yet
- [ ] HTTP router and handlers not implemented yet
- [ ] User DTO/model/repository/service not implemented yet
## General Folder Structure ## General Folder Structure
@ -11,9 +23,9 @@ cmd/
internal/ internal/
config/ # env and app config config/ # env and app config
db/ # postgres/sqlx connection + tx helpers db/ # postgres/sqlx connection + migrations
http/ # router, middleware, handlers http/ # router, middleware, handlers
auth/ # JWT, password hashing, auth middleware auth/ # jwt, password hashing, auth middleware
service/ # business logic service/ # business logic
repository/ # SQLx queries (no ORM) repository/ # SQLx queries (no ORM)
model/ # domain models + request/response DTOs model/ # domain models + request/response DTOs
@ -22,9 +34,69 @@ migrations/ # Goose SQL migrations
scripts/ # optional local/dev scripts scripts/ # optional local/dev scripts
``` ```
## Roadmap Checklist (Do Not Delete)
### Chapter 1 - Bootstrap and Config
- [x] Create `cmd/api/main.go`
- [x] Create `internal/config` package
- [x] Load `.env` and validate required DB env vars
- [ ] Add `APP_PORT` env var with default fallback
- [ ] Improve startup logs (without printing secrets)
### Chapter 2 - Database and Goose
- [ ] Add `internal/db/postgres.go` with `sqlx` connection
- [ ] Add `internal/db/migrate.go` to run Goose at startup
- [ ] Create `migrations/` directory
- [ ] Create first migration for `users` table
- [ ] Wire migration call into app startup (before HTTP server)
- [ ] Add `goose status` and rollback notes in README
### Chapter 3 - User Vertical Slice (First Feature)
- [ ] Add user DTO (`username`, `password`) in `internal/model`
- [ ] Add user DB model (`id`, `username`, `password_hash`, timestamps)
- [ ] Add user repository with SQLx (`CreateUser`, `GetByUsername`)
- [ ] Add user service with bcrypt hashing
- [ ] Add `POST /users/register` handler
- [ ] Add input validation and proper error responses
### Chapter 4 - HTTP Layer and Health
- [ ] Add router setup in `internal/http/router.go`
- [ ] Add `GET /health` endpoint
- [ ] Add JSON response helpers
- [ ] Add request logging middleware
- [ ] Add panic recovery middleware
### Chapter 5 - Security Foundation
- [ ] Add password hashing and compare helpers
- [ ] Add JWT generation/verification package
- [ ] Add auth middleware for protected routes
- [ ] Add `POST /auth/login`
- [ ] Add `GET /auth/me`
### Chapter 6 - Developer Experience
- [ ] Add `Makefile` targets (`run`, `db-up`, `migrate-up`, `migrate-down`)
- [ ] Add graceful shutdown in `main.go`
- [ ] Add structured logging (`log/slog`)
- [ ] Add `.env.example` updates for all required vars
- [ ] Add basic project usage section in README
### Chapter 7 - Testing
- [ ] Add unit tests for config package
- [ ] Add unit tests for user service
- [ ] Add repository integration test setup (test DB)
- [ ] Add handler tests for register endpoint
- [ ] Add CI step to run tests
## Notes ## Notes
- Keep handlers thin, business logic in `service`, SQL in `repository`. - Keep handlers thin, business logic in `service`, SQL in `repository`.
- Use `sqlx` for explicit SQL and scan helpers. - Use `sqlx` for explicit SQL and scan helpers.
- Use `goose` for schema versioning (`up`, `down`, `status`). - Use `goose` for schema versioning and run migrations automatically at startup.
- Prefer small packages and avoid over-abstraction early. - Never store plain passwords; always use `password_hash`.

View File

@ -1,9 +1,13 @@
package config package config
import ( import (
"github.com/joho/godotenv" "fmt"
"log" "log"
"net"
"net/url"
"os" "os"
"github.com/joho/godotenv"
) )
type Config struct { type Config struct {
@ -12,24 +16,47 @@ type Config struct {
DBName string DBName string
DBUser string DBUser string
DBPassword string DBPassword string
DBSchema string
} }
func mustEnv(key string) string { func getEnv(key string) string {
v := os.Getenv(key) v := os.Getenv(key)
if v == "" { if v == "" {
log.Fatalf("missing required env var: %s", key) log.Fatalf("missing required env var: %s", key)
} }
return v return v
} }
func getEnvOrDefault(key, default_string string) string {
v := os.Getenv(key)
if v == "" {
return default_string
}
return v
}
func Load() Config { func Load() Config {
_ = godotenv.Load() _ = godotenv.Load()
return Config{ return Config{
DBHost: mustEnv("DB_HOST"), DBHost: getEnv("DB_HOST"),
DBPort: mustEnv("DB_PORT"), DBPort: getEnv("DB_PORT"),
DBName: mustEnv("DB_NAME"), DBName: getEnv("DB_NAME"),
DBUser: mustEnv("DB_USERNAME"), DBUser: getEnv("DB_USERNAME"),
DBPassword: mustEnv("DB_PASSWORD"), DBPassword: getEnv("DB_PASSWORD"),
DBSchema: getEnvOrDefault("DB_SCHEMA", "public"),
} }
} }
func (cfg Config) DatabaseURL() string {
u := &url.URL{
Scheme: "postgres",
User: url.UserPassword(cfg.DBUser, cfg.DBPassword),
Host: net.JoinHostPort(cfg.DBHost, cfg.DBPort),
Path: cfg.DBName,
}
q := u.Query()
q.Set("sslmode", "false")
q.Set("search_path", cfg.DBSchema)
u.RawQuery = q.Encode()
return u.String()
}

0
internal/db/db.go Normal file
View File