diff --git a/trading_assistant_api/Makefile b/trading_assistant_api/Makefile new file mode 100644 index 0000000..774e1b1 --- /dev/null +++ b/trading_assistant_api/Makefile @@ -0,0 +1,27 @@ +SHELL := /bin/sh + +.PHONY: bootstrap tidy-all run-user run-country test-all + +bootstrap: + @echo "Initializing go.work..." + @go work init || true + @go work use ./common || true + @go work use ./services/user || true + @go work use ./services/country || true + +tidy-all: + @echo "Running go mod tidy for all modules..." + @cd common && go mod tidy + @cd services/user && go mod tidy + @cd services/country && go mod tidy + +run-user: + @echo "Starting user service..." + @cd services/user && PORT=8080 go run . + +run-country: + @echo "Starting country service..." + @cd services/country && PORT=8081 go run . + +test-all: + @echo "No tests yet" diff --git a/trading_assistant_api/common/go.mod b/trading_assistant_api/common/go.mod new file mode 100644 index 0000000..b93cdd1 --- /dev/null +++ b/trading_assistant_api/common/go.mod @@ -0,0 +1,3 @@ +module common + +go 1.25.7 diff --git a/trading_assistant_api/common/logger/logger.go b/trading_assistant_api/common/logger/logger.go new file mode 100644 index 0000000..371da7e --- /dev/null +++ b/trading_assistant_api/common/logger/logger.go @@ -0,0 +1,32 @@ +package logger + +import ( + "log" + "os" +) + +type Logger interface { + Printf(format string, v ...any) + Fatalf(format string, v ...any) +} + +type stdLogger struct { + l *log.Logger +} + +func (s *stdLogger) Printf(format string, v ...any) { s.l.Printf(format, v...) } +func (s *stdLogger) Fatalf(format string, v ...any) { s.l.Fatalf(format, v...) } + +var defaultLogger Logger = &stdLogger{ + l: log.New(os.Stdout, "[app] ", log.LstdFlags|log.Lshortfile), +} + +func L() Logger { + return defaultLogger +} + +func SetLogger(l Logger) { + if l != nil { + defaultLogger = l + } +} diff --git a/trading_assistant_api/common/utils/env.go b/trading_assistant_api/common/utils/env.go new file mode 100644 index 0000000..ab054e3 --- /dev/null +++ b/trading_assistant_api/common/utils/env.go @@ -0,0 +1,11 @@ +package utils + +import "os" + +func GetEnv(key, def string) string { + val := os.Getenv(key) + if val == "" { + return def + } + return val +} diff --git a/trading_assistant_api/services/country/go.mod b/trading_assistant_api/services/country/go.mod new file mode 100644 index 0000000..033f372 --- /dev/null +++ b/trading_assistant_api/services/country/go.mod @@ -0,0 +1,7 @@ +module country + +go 1.25.7 + +require common v0.0.0 + +replace common => ../../common diff --git a/trading_assistant_api/services/country/main.go b/trading_assistant_api/services/country/main.go new file mode 100644 index 0000000..5999a87 --- /dev/null +++ b/trading_assistant_api/services/country/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "common/logger" + "common/utils" +) + +func main() { + port := utils.GetEnv("PORT", "8081") + srv := &http.Server{ + Addr: ":" + port, + Handler: routes(), + } + + logger.L().Printf("country service starting on :%s", port) + + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logger.L().Fatalf("listen: %v", err) + } + }() + + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + logger.L().Printf("country service shutting down...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + logger.L().Printf("server shutdown: %v", err) + } + logger.L().Printf("country service exited") +} + +func routes() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("country-service v0.1.0")) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "hello from country-service") + }) + return mux +} diff --git a/trading_assistant_api/services/user/go.mod b/trading_assistant_api/services/user/go.mod new file mode 100644 index 0000000..99030ed --- /dev/null +++ b/trading_assistant_api/services/user/go.mod @@ -0,0 +1,7 @@ +module user + +go 1.25.7 + +require common v0.0.0 + +replace common => ../../common diff --git a/trading_assistant_api/services/user/main.go b/trading_assistant_api/services/user/main.go new file mode 100644 index 0000000..64a13f1 --- /dev/null +++ b/trading_assistant_api/services/user/main.go @@ -0,0 +1,58 @@ +package main + +import ( + "context" + "fmt" + "net/http" + "os" + "os/signal" + "syscall" + "time" + + "common/logger" + "common/utils" +) + +func main() { + port := utils.GetEnv("PORT", "8080") + srv := &http.Server{ + Addr: ":" + port, + Handler: routes(), + } + + logger.L().Printf("user service starting on :%s", port) + + go func() { + if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { + logger.L().Fatalf("listen: %v", err) + } + }() + + quit := make(chan os.Signal, 1) + signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM) + <-quit + logger.L().Printf("user service shutting down...") + + ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) + defer cancel() + if err := srv.Shutdown(ctx); err != nil { + logger.L().Printf("server shutdown: %v", err) + } + logger.L().Printf("user service exited") +} + +func routes() http.Handler { + mux := http.NewServeMux() + mux.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("ok")) + }) + mux.HandleFunc("/version", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + w.Write([]byte("user-service v0.1.0")) + }) + mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintf(w, "hello from user-service") + }) + return mux +}