Stand up the Go project alongside the Python backend so both run
independently during migration. `make web-go` builds and serves on :8080;
`make web-py` (alias: `make web`) keeps the Python side on :5001.
- go/: new module `fuj-management/go` (Go 1.26)
- cmd/fuj: stdlib-flag dispatcher; `server` + `version` work,
fees/reconcile/sync/infer stubbed for M2/M4
- internal/config: env loader mirroring scripts/config.py
- internal/logging: slog setup, level taken from config
- internal/web: net/http ServeMux + request-timer middleware
- build/Dockerfile: golang:1.26 → alpine:3 multi-stage image
- .golangci.yml: govet, staticcheck, errcheck, gofumpt, unused
- Makefile: web→web-py alias; go-build/go-test/go-run/go-lint/web-go
- CI: parallel build-go job in .gitea/workflows/build.yaml (<tag>-go image)
- docs/plans/: M1 kickoff plan + progress tracker (M1 complete)
- .claude/settings.json: gofumpt + golangci-lint permissions
Gate: make go-build ✓ make go-lint ✓ make go-test ✓ curl :8080 ✓
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
85 lines
1.8 KiB
Go
85 lines
1.8 KiB
Go
package main
|
|
|
|
import (
|
|
"flag"
|
|
"fmt"
|
|
"fuj-management/go/internal/config"
|
|
"fuj-management/go/internal/logging"
|
|
"fuj-management/go/internal/web"
|
|
"os"
|
|
)
|
|
|
|
// Injected at build time via -ldflags "-X main.version=... -X main.commit=... -X main.buildDate=..."
|
|
var (
|
|
version = "dev"
|
|
commit = "unknown"
|
|
buildDate = "unknown"
|
|
)
|
|
|
|
func main() {
|
|
if len(os.Args) < 2 {
|
|
usage()
|
|
os.Exit(2)
|
|
}
|
|
|
|
cmd, args := os.Args[1], os.Args[2:]
|
|
|
|
switch cmd {
|
|
case "server":
|
|
serverCmd(args)
|
|
case "version":
|
|
versionCmd()
|
|
case "fees", "reconcile", "sync", "infer":
|
|
fmt.Fprintf(os.Stderr, "fuj %s: not implemented yet (lands in M2/M4)\n", cmd)
|
|
os.Exit(2)
|
|
case "-h", "--help", "help":
|
|
usage()
|
|
default:
|
|
fmt.Fprintf(os.Stderr, "fuj: unknown command %q\n\n", cmd)
|
|
usage()
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
|
|
func serverCmd(args []string) {
|
|
fs := flag.NewFlagSet("server", flag.ExitOnError)
|
|
addr := fs.String("addr", "", "listen address (default from SERVER_ADDR env or :8080)")
|
|
fs.Usage = func() {
|
|
fmt.Fprintln(os.Stderr, "usage: fuj server [--addr :8080]")
|
|
fs.PrintDefaults()
|
|
}
|
|
if err := fs.Parse(args); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(2)
|
|
}
|
|
|
|
cfg := config.Load()
|
|
if *addr != "" {
|
|
cfg.ServerAddr = *addr
|
|
}
|
|
|
|
logger := logging.New(cfg.LogLevel)
|
|
build := web.BuildInfo{Version: version, Commit: commit, BuildDate: buildDate}
|
|
|
|
if err := web.Run(logger, cfg.ServerAddr, build); err != nil {
|
|
fmt.Fprintln(os.Stderr, err)
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
|
|
func versionCmd() {
|
|
fmt.Printf("fuj %s (%s) built %s\n", version, commit, buildDate)
|
|
}
|
|
|
|
func usage() {
|
|
fmt.Fprintln(os.Stderr, `usage: fuj <command> [flags]
|
|
|
|
Commands:
|
|
server Start HTTP server (default :8080)
|
|
version Print version information
|
|
fees Calculate monthly fees [M2]
|
|
reconcile Show balance report [M2]
|
|
sync Sync Fio transactions [M4]
|
|
infer Infer payment details [M4]`)
|
|
}
|