136 lines
3.2 KiB
Go
136 lines
3.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"embed"
|
|
"errors"
|
|
"github.com/gin-gonic/gin"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"strconv"
|
|
)
|
|
|
|
//go:embed migrations/*.sql
|
|
var migrations embed.FS
|
|
var db *Db
|
|
|
|
// ServerConfig holds configuration for the server.
|
|
type ServerConfig struct {
|
|
// The datasource to the SQLite database.
|
|
// Use ":memory:" for an in-memory database.
|
|
Datasource string
|
|
|
|
// The port to listen on.
|
|
// Use an empty string to listen on a random port.
|
|
Port string
|
|
|
|
// The channel that gets signaled when the server has started.
|
|
Started chan bool
|
|
}
|
|
|
|
func getUsers(c *gin.Context) {
|
|
users, err := db.GetUsers()
|
|
if err != nil {
|
|
log.Printf("Error getting users: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
|
return
|
|
}
|
|
c.IndentedJSON(http.StatusOK, users)
|
|
}
|
|
|
|
func getUser(c *gin.Context) {
|
|
userIdStr := c.Param("userId")
|
|
userId, err := strconv.Atoi(userIdStr)
|
|
if err != nil {
|
|
log.Printf("Invalid user ID: %v", err)
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
user, err := db.GetUser(userId)
|
|
if err != nil {
|
|
log.Printf("Error getting user: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
|
return
|
|
}
|
|
if user == nil {
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
|
return
|
|
}
|
|
|
|
c.IndentedJSON(http.StatusOK, user)
|
|
}
|
|
|
|
func getUserGoals(c *gin.Context) {
|
|
userIdStr := c.Param("userId")
|
|
userId, err := strconv.Atoi(userIdStr)
|
|
if err != nil {
|
|
log.Printf("Invalid user ID: %v", err)
|
|
c.JSON(http.StatusBadRequest, gin.H{"error": "Invalid user ID"})
|
|
return
|
|
}
|
|
|
|
exists, err := db.UserExists(userId)
|
|
if err != nil {
|
|
log.Printf("Error checking user existence: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
|
return
|
|
}
|
|
if !exists {
|
|
log.Printf("Error checking user existence: %v", err)
|
|
c.JSON(http.StatusNotFound, gin.H{"error": "User not found"})
|
|
return
|
|
}
|
|
|
|
goals, err := db.GetUserGoals(userId)
|
|
if err != nil {
|
|
log.Printf("Error getting user goals: %v", err)
|
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "Internal Server Error"})
|
|
return
|
|
}
|
|
c.IndentedJSON(http.StatusOK, goals)
|
|
}
|
|
|
|
/*
|
|
*
|
|
Initialises the database, and then starts the server.
|
|
If the context gets cancelled, the server is shutdown and the database is closed.
|
|
*/
|
|
func start(ctx context.Context, config *ServerConfig) {
|
|
db = NewDb(config.Datasource)
|
|
defer db.db.MustClose()
|
|
|
|
router := gin.Default()
|
|
router.GET("/api/users", getUsers)
|
|
router.GET("/api/user/:userId", getUser)
|
|
router.GET("/api/user/:userId/goals", getUserGoals)
|
|
|
|
srv := &http.Server{
|
|
Addr: ":" + config.Port,
|
|
Handler: router.Handler(),
|
|
}
|
|
go func() {
|
|
if err := srv.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
|
|
log.Fatalf("listen: %s\n", err)
|
|
}
|
|
}()
|
|
|
|
log.Printf("Running server on port %s\n", config.Port)
|
|
if config.Started != nil {
|
|
config.Started <- true
|
|
}
|
|
<-ctx.Done()
|
|
log.Println("Shutting down")
|
|
if err := srv.Shutdown(context.Background()); err != nil {
|
|
log.Fatalf("Server forced to shutdown: %v", err)
|
|
}
|
|
}
|
|
|
|
func main() {
|
|
config := ServerConfig{
|
|
Datasource: os.Getenv("DB_PATH"),
|
|
}
|
|
start(context.Background(), &config)
|
|
}
|