tsk-5: Add contact (#7)

Closes #5

Reviewed-on: phoenix/textsender-api#7
Co-authored-by: phoenix <kundeng00@pm.me>
Co-committed-by: phoenix <kundeng00@pm.me>
This commit is contained in:
phoenix
2025-11-03 22:27:34 +00:00
committed by phoenix
parent 3db003fc47
commit 96fc87ea5e
16 changed files with 911 additions and 78 deletions
+83 -9
View File
@@ -1,17 +1,91 @@
package main
import "fmt"
import "log"
import "net/http"
import (
"context"
"fmt"
"log"
"net/http"
"os"
"os/signal"
"syscall"
"time"
import "git.kundeng.us/phoenix/textsender-api/internal/config"
import "git.kundeng.us/phoenix/textsender-api/internal/handler"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"git.kundeng.us/phoenix/textsender-api/internal/config"
"git.kundeng.us/phoenix/textsender-api/internal/db"
"git.kundeng.us/phoenix/textsender-api/internal/handler"
"git.kundeng.us/phoenix/textsender-api/internal/handler/endpoint"
mdlware "git.kundeng.us/phoenix/textsender-api/internal/middleware"
"git.kundeng.us/phoenix/textsender-api/internal/store"
)
func main() {
fmt.Println(config.APP_NAME)
cfg := config.Load()
if cfg == nil {
fmt.Errorf("Error initializing config")
os.Exit(-1)
}
http.HandleFunc(handler.MESSAGE_DRAFT_ENDPOINT, handler.DraftMessageHandler)
database, err := db.NewDatabase(cfg.GetDBConnString())
if err != nil {
log.Fatalf("Failed to connect to database: %v", err)
}
defer database.Close()
log.Println("Starting server", config.PORT)
log.Fatal(http.ListenAndServe(config.PortString(), nil))
ctx := context.Background()
if cfg.ResetDB {
log.Println("Resetting database")
if err := database.ResetDatabase(ctx); err != nil {
log.Fatalf("Failed to reset database: %v", err)
}
log.Println("Database reset completed. Exiting.")
return
}
contactStore := store.NewContactStore(database.Pool)
contactHandler := handler.NewContactHandler(contactStore)
router := chi.NewRouter()
router.Use(middleware.Logger)
router.Use(middleware.Recoverer)
router.Use(middleware.Timeout(60 * time.Second))
router.Use(mdlware.JSONContentType)
router.Post(endpoint.ADD_CONTACT_ENDPOINT, contactHandler.AddContact)
// Start server
server := &http.Server{
Addr: ":" + cfg.ServerPort,
Handler: router,
ReadTimeout: 15 * time.Second,
WriteTimeout: 15 * time.Second,
IdleTimeout: 60 * time.Second,
}
// Graceful shutdown
go func() {
log.Printf("Server starting on port %s", cfg.ServerPort)
if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
log.Fatalf("Server failed to start: %v", err)
}
}()
// Wait for interrupt signal
quit := make(chan os.Signal, 1)
signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
<-quit
log.Println("Shutting down server...")
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()
if err := server.Shutdown(ctx); err != nil {
log.Fatalf("Server forced to shutdown: %v", err)
}
log.Println("Server exited")
}
+82
View File
@@ -0,0 +1,82 @@
package main
import (
"context"
"flag"
"fmt"
"os"
"path"
"testing"
"github.com/gorilla/mux"
"github.com/joho/godotenv"
"git.kundeng.us/phoenix/textsender-api/internal/config"
"git.kundeng.us/phoenix/textsender-api/internal/db"
"git.kundeng.us/phoenix/textsender-api/internal/handler"
"git.kundeng.us/phoenix/textsender-api/internal/handler/endpoint"
"git.kundeng.us/phoenix/textsender-api/internal/store"
)
var testRouter *mux.Router
func TestMain(m *testing.M) {
cfg := load()
db, err := db.NewDatabase(cfg.GetDBConnString())
if err != nil {
fmt.Println(err.Error())
panic("Failed to initialize database")
}
defer db.Close()
ctx := context.Background()
err = db.ResetDatabase(ctx)
if err != nil {
fmt.Println(err.Error())
panic("Failed to initialize database")
}
contactStore := store.NewContactStore(db.Pool)
contactHandler := handler.NewContactHandler(contactStore)
testRouter = mux.NewRouter()
testRouter.HandleFunc(endpoint.ADD_CONTACT_ENDPOINT, contactHandler.AddContact).Methods("POST")
code := m.Run()
os.Exit(code)
}
func load() *config.Config {
resetDb := flag.Bool("reset-db", false, "Reset the database schema and exit")
port := flag.String("port", config.PORT, "Server port")
flag.Parse()
cwd, _ := os.Getwd()
envPath := path.Join(cwd, ".env")
err := godotenv.Load(envPath)
if err != nil {
envPath = path.Join(cwd, "../..", ".env")
if err := godotenv.Load(envPath); err != nil {
panic("Error loading .env file: " + err.Error())
}
}
unpackedConnString := config.UnpackDBConnString()
dbConnString := unpackedConnString.Parse()
return &config.Config{
DBConnString: dbConnString,
ServerPort: *port,
ResetDB: *resetDb,
}
}
func resetTestDB(t *testing.T) {
t.Helper()
_, err := db.Pool.Exec(context.Background(), "DELETE FROM contacts")
if err != nil {
t.Fatalf("Failed to reset test database: %v", err)
}
}