Version 7 #53
29 changed files with 421 additions and 661 deletions
15
Dockerfile
15
Dockerfile
|
@ -1,23 +1,20 @@
|
||||||
FROM golang:1.22.0 as build
|
FROM golang:1.22.3 as build
|
||||||
|
|
||||||
LABEL author="vlbeaudoin"
|
LABEL author="vlbeaudoin"
|
||||||
|
|
||||||
WORKDIR /go/src/app
|
WORKDIR /go/src/app
|
||||||
|
|
||||||
COPY go.mod go.sum main.go ./
|
COPY go.mod go.sum db.go entity.go main.go responses.go ./
|
||||||
|
|
||||||
ADD cmd/ cmd/
|
ADD cmd/ cmd/
|
||||||
ADD data/ data/
|
ADD templates/ templates/
|
||||||
ADD handlers/ handlers/
|
ADD sql/ sql/
|
||||||
ADD models/ models/
|
|
||||||
ADD responses/ responses/
|
|
||||||
ADD web/ web/
|
|
||||||
|
|
||||||
RUN CGO_ENABLED=0 go build -a -installsuffix cgo -o bottin .
|
RUN CGO_ENABLED=0 go build -a -o bottin .
|
||||||
|
|
||||||
# Alpine
|
# Alpine
|
||||||
|
|
||||||
FROM alpine:3.19
|
FROM alpine:3.20
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
|
|
141
cmd/api.go
141
cmd/api.go
|
@ -5,9 +5,6 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
|
||||||
"codeberg.org/vlbeaudoin/serpents"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/data"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/handlers"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -19,14 +16,51 @@ var (
|
||||||
apiKey string
|
apiKey string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
ViperAPIPort string = "api.port"
|
||||||
|
FlagAPIPort string = "api-port"
|
||||||
|
DefaultAPIPort int = 1312
|
||||||
|
DescriptionAPIPort string = "API server port"
|
||||||
|
|
||||||
|
ViperAPIKey string = "api.key"
|
||||||
|
FlagAPIKey string = "api-key"
|
||||||
|
DefaultAPIKey string = "bottin"
|
||||||
|
DescriptionAPIKey string = "API server key. Leave empty for no key auth (not recommended)"
|
||||||
|
|
||||||
|
ViperDBDatabase string = "db.database"
|
||||||
|
FlagDBDatabase string = "db-database"
|
||||||
|
DefaultDBDatabase string = "bottin"
|
||||||
|
DescriptionDBDatabase string = "Postgres database"
|
||||||
|
|
||||||
|
ViperDBHost string = "db.host"
|
||||||
|
FlagDBHost string = "db-host"
|
||||||
|
DefaultDBHost string = "db"
|
||||||
|
DescriptionDBHost string = "Postgres host"
|
||||||
|
|
||||||
|
ViperDBPassword string = "db.password"
|
||||||
|
FlagDBPassword string = "db-password"
|
||||||
|
DefaultDBPassword string = "bottin"
|
||||||
|
DescriptionDBPassword string = "Postgres password"
|
||||||
|
|
||||||
|
ViperDBPort string = "db.port"
|
||||||
|
FlagDBPort string = "db-port"
|
||||||
|
DefaultDBPort int = 5432
|
||||||
|
DescriptionDBPort string = "Postgres port"
|
||||||
|
|
||||||
|
ViperDBUser string = "db.user"
|
||||||
|
FlagDBUser string = "db-user"
|
||||||
|
DefaultDBUser string = "bottin"
|
||||||
|
DescriptionDBUser string = "Postgres user"
|
||||||
|
)
|
||||||
|
|
||||||
// apiCmd represents the api command
|
// apiCmd represents the api command
|
||||||
var apiCmd = &cobra.Command{
|
var apiCmd = &cobra.Command{
|
||||||
Use: "api",
|
Use: "api",
|
||||||
Short: "Démarrer le serveur API",
|
Short: "Démarrer le serveur API",
|
||||||
Args: cobra.ExactArgs(0),
|
Args: cobra.ExactArgs(0),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
apiKey = viper.GetString("api.key")
|
apiKey = viper.GetString(ViperAPIKey)
|
||||||
apiPort = viper.GetInt("api.port")
|
apiPort = viper.GetInt(ViperAPIPort)
|
||||||
|
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
|
@ -41,40 +75,42 @@ var apiCmd = &cobra.Command{
|
||||||
}
|
}
|
||||||
|
|
||||||
// DataClient
|
// DataClient
|
||||||
|
/*
|
||||||
|
client, err := data.NewDataClientFromViper()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("Could not establish database connection.\n Error: %s\n", err)
|
||||||
|
}
|
||||||
|
defer client.DB.Close()
|
||||||
|
|
||||||
client, err := data.NewDataClientFromViper()
|
err = client.DB.Ping()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Could not establish database connection.\n Error: %s\n", err)
|
log.Fatalf("Database was supposed to be ready but Ping() failed.\n Error: %s\n", err)
|
||||||
}
|
}
|
||||||
defer client.DB.Close()
|
|
||||||
|
|
||||||
err = client.DB.Ping()
|
_, err = client.Seed()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Fatalf("Database was supposed to be ready but Ping() failed.\n Error: %s\n", err)
|
log.Fatalf("Error during client.Seed(): %s", err)
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
_, err = client.Seed()
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("Error during client.Seed(): %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
h := handlers.New(client)
|
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
|
/*
|
||||||
|
h := handlers.New(client)
|
||||||
|
|
||||||
e.GET("/v6/health/", h.GetHealth)
|
e.GET("/v7/health/", h.GetHealth)
|
||||||
|
|
||||||
e.POST("/v6/membres/", h.PostMembres)
|
e.POST("/v7/membres/", h.PostMembres)
|
||||||
|
|
||||||
e.GET("/v6/membres/", h.ListMembres)
|
e.GET("/v7/membres/", h.ListMembres)
|
||||||
|
|
||||||
e.GET("/v6/membres/:membre_id/", h.ReadMembre)
|
e.GET("/v7/membres/:membre_id/", h.ReadMembre)
|
||||||
|
|
||||||
e.PUT("/v6/membres/:membre_id/prefered_name/", h.PutMembrePreferedName)
|
e.PUT("/v7/membres/:membre_id/prefered_name/", h.PutMembrePreferedName)
|
||||||
|
|
||||||
e.POST("/v6/programmes/", h.PostProgrammes)
|
e.POST("/v7/programmes/", h.PostProgrammes)
|
||||||
|
|
||||||
e.POST("/v6/seed/", h.PostSeed)
|
e.POST("/v7/seed/", h.PostSeed)
|
||||||
|
*/
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
|
|
||||||
|
@ -86,37 +122,44 @@ func init() {
|
||||||
rootCmd.AddCommand(apiCmd)
|
rootCmd.AddCommand(apiCmd)
|
||||||
|
|
||||||
// api.key
|
// api.key
|
||||||
serpents.String(apiCmd.Flags(),
|
apiCmd.Flags().String(FlagAPIKey, DefaultAPIKey, DescriptionAPIKey)
|
||||||
"api.key", "api-key", "bottin",
|
if err := viper.BindPFlag(ViperAPIKey, apiCmd.Flags().Lookup(FlagAPIKey)); err != nil {
|
||||||
"API server key. Leave empty for no key auth")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// api.port
|
// api.port
|
||||||
serpents.Int(apiCmd.Flags(),
|
apiCmd.Flags().Int(FlagAPIPort, DefaultAPIPort, DescriptionAPIPort)
|
||||||
"api.port", "api-port", 1312,
|
if err := viper.BindPFlag(ViperAPIPort, apiCmd.Flags().Lookup(FlagAPIPort)); err != nil {
|
||||||
"API server port")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// db.database
|
// db.database
|
||||||
serpents.String(apiCmd.Flags(),
|
apiCmd.Flags().String(FlagDBDatabase, DefaultDBDatabase, DescriptionDBDatabase)
|
||||||
"db.database", "db-database", "bottin",
|
if err := viper.BindPFlag(ViperDBDatabase, apiCmd.Flags().Lookup(FlagDBDatabase)); err != nil {
|
||||||
"Postgres database")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// db.host
|
// db.host
|
||||||
serpents.String(apiCmd.Flags(),
|
apiCmd.Flags().String(FlagDBHost, DefaultDBHost, DescriptionDBHost)
|
||||||
"db.host", "db-host", "db",
|
if err := viper.BindPFlag(ViperDBHost, apiCmd.Flags().Lookup(FlagDBHost)); err != nil {
|
||||||
"Postgres host")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// db.password
|
// db.password
|
||||||
serpents.String(apiCmd.Flags(),
|
apiCmd.Flags().String(FlagDBPassword, DefaultDBPassword, DescriptionDBPassword)
|
||||||
"db.password", "db-password", "bottin",
|
if err := viper.BindPFlag(ViperDBPassword, apiCmd.Flags().Lookup(FlagDBPassword)); err != nil {
|
||||||
"Postgres password")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// db.port
|
// db.port
|
||||||
serpents.Int(apiCmd.Flags(),
|
apiCmd.Flags().Int(FlagDBPort, DefaultDBPort, DescriptionDBPort)
|
||||||
"db.port", "db-port", 5432,
|
if err := viper.BindPFlag(ViperDBPort, apiCmd.Flags().Lookup(FlagDBPort)); err != nil {
|
||||||
"Postgres port")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// db.user
|
// db.user
|
||||||
serpents.String(apiCmd.Flags(),
|
apiCmd.Flags().String(FlagDBUser, DefaultDBUser, DescriptionDBUser)
|
||||||
"db.user", "db-user", "bottin",
|
if err := viper.BindPFlag(ViperDBUser, apiCmd.Flags().Lookup(FlagDBUser)); err != nil {
|
||||||
"Postgres user")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,7 +14,7 @@ var cfgFile string
|
||||||
// rootCmd represents the base command when called without any subcommands
|
// rootCmd represents the base command when called without any subcommands
|
||||||
var rootCmd = &cobra.Command{
|
var rootCmd = &cobra.Command{
|
||||||
Use: "bottin",
|
Use: "bottin",
|
||||||
Short: "Application de gestion de distribution d'agendas",
|
Short: "Bottin étudiant de l'AGECEM",
|
||||||
}
|
}
|
||||||
|
|
||||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
|
135
cmd/web.go
135
cmd/web.go
|
@ -7,12 +7,7 @@ import (
|
||||||
"html/template"
|
"html/template"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"log"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"codeberg.org/vlbeaudoin/serpents"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/data"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/web"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/web/webhandlers"
|
|
||||||
"github.com/labstack/echo/v4"
|
"github.com/labstack/echo/v4"
|
||||||
"github.com/labstack/echo/v4/middleware"
|
"github.com/labstack/echo/v4/middleware"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -29,6 +24,43 @@ var (
|
||||||
webApiProtocol string
|
webApiProtocol string
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
viperWebUser string = "web.user"
|
||||||
|
flagWebUser string = "web-user"
|
||||||
|
defaultWebUser string = "bottin"
|
||||||
|
descriptionWebUser string = "Web client basic auth user"
|
||||||
|
|
||||||
|
viperWebPassword string = "web.password"
|
||||||
|
flagWebPassword string = "web-password"
|
||||||
|
defaultWebPassword string = "bottin"
|
||||||
|
descriptionWebPassword string = "Web client basic auth password"
|
||||||
|
|
||||||
|
viperWebPort string = "web.port"
|
||||||
|
flagWebPort string = "web-port"
|
||||||
|
defaultWebPort int = 2312
|
||||||
|
descriptionWebPort string = "Web client port"
|
||||||
|
|
||||||
|
viperWebAPIHost string = "api.host"
|
||||||
|
flagWebAPIHost string = "api-host"
|
||||||
|
defaultWebAPIHost string = "api"
|
||||||
|
descriptionWebAPIHost string = "Target API server host"
|
||||||
|
|
||||||
|
viperWebAPIKey string = "api.key"
|
||||||
|
flagWebAPIKey string = "api-key"
|
||||||
|
defaultWebAPIKey string = "bottin"
|
||||||
|
descriptionWebAPIKey string = "Target API server key"
|
||||||
|
|
||||||
|
viperWebAPIPort string = "api.port"
|
||||||
|
flagWebAPIPort string = "api-port"
|
||||||
|
defaultWebAPIPort int = 1312
|
||||||
|
descriptionWebAPIPort string = "Target API server port"
|
||||||
|
|
||||||
|
viperWebAPIProtocol string = "api.protocol"
|
||||||
|
flagWebAPIProtocol string = "api-protocol"
|
||||||
|
defaultWebAPIProtocol string = "http"
|
||||||
|
descriptionWebAPIProtocol string = "Target API server protocol (http/https)"
|
||||||
|
)
|
||||||
|
|
||||||
var templatesFS embed.FS
|
var templatesFS embed.FS
|
||||||
|
|
||||||
type Template struct {
|
type Template struct {
|
||||||
|
@ -45,27 +77,28 @@ var webCmd = &cobra.Command{
|
||||||
Short: "Démarrer le client web",
|
Short: "Démarrer le client web",
|
||||||
Args: cobra.ExactArgs(0),
|
Args: cobra.ExactArgs(0),
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
webApiHost = viper.GetString("web.api.host")
|
webApiHost = viper.GetString(viperWebAPIHost)
|
||||||
webApiKey = viper.GetString("web.api.key")
|
webApiKey = viper.GetString(viperWebAPIKey)
|
||||||
webApiPort = viper.GetInt("web.api.port")
|
webApiPort = viper.GetInt(viperWebAPIPort)
|
||||||
webApiProtocol = viper.GetString("web.api.protocol")
|
webApiProtocol = viper.GetString(viperWebAPIProtocol)
|
||||||
webPassword = viper.GetString("web.password")
|
webPassword = viper.GetString(viperWebPassword)
|
||||||
webPort = viper.GetInt("web.port")
|
webPort = viper.GetInt(viperWebPort)
|
||||||
webUser = viper.GetString("web.user")
|
webUser = viper.GetString(viperWebUser)
|
||||||
|
|
||||||
// Ping API server
|
// Ping API server
|
||||||
|
/*
|
||||||
|
client := http.DefaultClient
|
||||||
|
defer client.CloseIdleConnections()
|
||||||
|
|
||||||
client := http.DefaultClient
|
apiClient := data.NewApiClient(client, webApiKey, webApiHost, webApiProtocol, webApiPort)
|
||||||
defer client.CloseIdleConnections()
|
|
||||||
|
|
||||||
apiClient := data.NewApiClient(client, webApiKey, webApiHost, webApiProtocol, webApiPort)
|
pingResult, err := apiClient.GetHealth()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
pingResult, err := apiClient.GetHealth()
|
log.Println(pingResult)
|
||||||
if err != nil {
|
*/
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Println(pingResult)
|
|
||||||
|
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
|
|
||||||
|
@ -88,11 +121,12 @@ var webCmd = &cobra.Command{
|
||||||
e.Renderer = t
|
e.Renderer = t
|
||||||
|
|
||||||
// Routes
|
// Routes
|
||||||
|
/*
|
||||||
|
handler := webhandlers.Handler{APIClient: apiClient}
|
||||||
|
|
||||||
handler := webhandlers.Handler{APIClient: apiClient}
|
e.GET("/", handler.GetIndex)
|
||||||
|
e.GET("/membre/", handler.GetMembre)
|
||||||
e.GET("/", handler.GetIndex)
|
*/
|
||||||
e.GET("/membre/", handler.GetMembre)
|
|
||||||
|
|
||||||
// Execution
|
// Execution
|
||||||
|
|
||||||
|
@ -103,40 +137,47 @@ var webCmd = &cobra.Command{
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
rootCmd.AddCommand(webCmd)
|
rootCmd.AddCommand(webCmd)
|
||||||
templatesFS = web.GetTemplates()
|
//templatesFS = web.GetTemplates()
|
||||||
|
|
||||||
// web.api.host
|
// web.api.host
|
||||||
serpents.String(webCmd.Flags(),
|
webCmd.Flags().String(flagWebAPIHost, defaultWebAPIHost, descriptionWebAPIHost)
|
||||||
"web.api.host", "web-api-host", "api",
|
if err := viper.BindPFlag(viperWebAPIHost, webCmd.Flags().Lookup(flagWebAPIHost)); err != nil {
|
||||||
"Remote API server host")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.api.key
|
// web.api.key
|
||||||
serpents.String(webCmd.Flags(),
|
webCmd.Flags().String(flagWebAPIKey, defaultWebAPIKey, descriptionWebAPIKey)
|
||||||
"web.api.key", "web-api-key", "bottin",
|
if err := viper.BindPFlag(viperWebAPIKey, webCmd.Flags().Lookup(flagWebAPIKey)); err != nil {
|
||||||
"Remote API server key")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.api.protocol
|
// web.api.protocol
|
||||||
serpents.String(webCmd.Flags(),
|
webCmd.Flags().String(flagWebAPIProtocol, defaultWebAPIProtocol, descriptionWebAPIProtocol)
|
||||||
"web.api.protocol", "web-api-protocol", "http",
|
if err := viper.BindPFlag(viperWebAPIProtocol, webCmd.Flags().Lookup(flagWebAPIProtocol)); err != nil {
|
||||||
"Remote API server protocol")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.api.port
|
// web.api.port
|
||||||
serpents.Int(webCmd.Flags(),
|
webCmd.Flags().Int(flagWebAPIPort, defaultWebAPIPort, descriptionWebAPIPort)
|
||||||
"web.api.port", "web-api-port", 1312,
|
if err := viper.BindPFlag(viperWebAPIPort, webCmd.Flags().Lookup(flagWebAPIPort)); err != nil {
|
||||||
"Remote API server port")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.password
|
// web.password
|
||||||
serpents.String(webCmd.Flags(),
|
webCmd.Flags().String(flagWebPassword, defaultWebPassword, descriptionWebPassword)
|
||||||
"web.password", "web-password", "bottin",
|
if err := viper.BindPFlag(viperWebPassword, webCmd.Flags().Lookup(flagWebPassword)); err != nil {
|
||||||
"Web client password")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.port
|
// web.port
|
||||||
serpents.Int(webCmd.Flags(),
|
webCmd.Flags().Int(flagWebPort, defaultWebPort, descriptionWebPort)
|
||||||
"web.port", "web-port", 2312,
|
if err := viper.BindPFlag(viperWebPort, webCmd.Flags().Lookup(flagWebPort)); err != nil {
|
||||||
"Web client port")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
// web.user
|
// web.user
|
||||||
serpents.String(webCmd.Flags(),
|
webCmd.Flags().String(flagWebUser, defaultWebUser, descriptionWebUser)
|
||||||
"web.user", "web-user", "bottin",
|
if err := viper.BindPFlag(viperWebUser, webCmd.Flags().Lookup(flagWebUser)); err != nil {
|
||||||
"Web client user")
|
log.Fatal(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
99
config.go
Normal file
99
config.go
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
const (
|
||||||
|
ViperAPIPort string = "api.port"
|
||||||
|
FlagAPIPort string = "api-port"
|
||||||
|
DefaultAPIPort int = 1312
|
||||||
|
DescriptionAPIPort string = "API server port"
|
||||||
|
|
||||||
|
ViperAPIKey string = "api.key"
|
||||||
|
FlagAPIKey string = "api-key"
|
||||||
|
DefaultAPIKey string = "bottin"
|
||||||
|
DescriptionAPIKey string = "API server key. Leave empty for no key auth (not recommended)"
|
||||||
|
|
||||||
|
ViperDBDatabase string = "db.database"
|
||||||
|
FlagDBDatabase string = "db-database"
|
||||||
|
DefaultDBDatabase string = "bottin"
|
||||||
|
DescriptionDBDatabase string = "Postgres database"
|
||||||
|
|
||||||
|
ViperDBHost string = "db.host"
|
||||||
|
FlagDBHost string = "db-host"
|
||||||
|
DefaultDBHost string = "db"
|
||||||
|
DescriptionDBHost string = "Postgres host"
|
||||||
|
|
||||||
|
ViperDBPassword string = "db.password"
|
||||||
|
FlagDBPassword string = "db-password"
|
||||||
|
DefaultDBPassword string = "bottin"
|
||||||
|
DescriptionDBPassword string = "Postgres password"
|
||||||
|
|
||||||
|
ViperDBPort string = "db.port"
|
||||||
|
FlagDBPort string = "db-port"
|
||||||
|
DefaultDBPort int = 5432
|
||||||
|
DescriptionDBPort string = "Postgres port"
|
||||||
|
|
||||||
|
ViperDBUser string = "db.user"
|
||||||
|
FlagDBUser string = "db-user"
|
||||||
|
DefaultDBUser string = "bottin"
|
||||||
|
DescriptionDBUser string = "Postgres user"
|
||||||
|
|
||||||
|
viperWebUser string = "web.user"
|
||||||
|
flagWebUser string = "web-user"
|
||||||
|
defaultWebUser string = "bottin"
|
||||||
|
descriptionWebUser string = "Web client basic auth user"
|
||||||
|
|
||||||
|
viperWebPassword string = "web.password"
|
||||||
|
flagWebPassword string = "web-password"
|
||||||
|
defaultWebPassword string = "bottin"
|
||||||
|
descriptionWebPassword string = "Web client basic auth password"
|
||||||
|
|
||||||
|
viperWebPort string = "web.port"
|
||||||
|
flagWebPort string = "web-port"
|
||||||
|
defaultWebPort int = 2312
|
||||||
|
descriptionWebPort string = "Web client port"
|
||||||
|
|
||||||
|
viperWebAPIHost string = "api.host"
|
||||||
|
flagWebAPIHost string = "api-host"
|
||||||
|
defaultWebAPIHost string = "api"
|
||||||
|
descriptionWebAPIHost string = "Target API server host"
|
||||||
|
|
||||||
|
viperWebAPIKey string = "api.key"
|
||||||
|
flagWebAPIKey string = "api-key"
|
||||||
|
defaultWebAPIKey string = "bottin"
|
||||||
|
descriptionWebAPIKey string = "Target API server key"
|
||||||
|
|
||||||
|
viperWebAPIPort string = "api.port"
|
||||||
|
flagWebAPIPort string = "api-port"
|
||||||
|
defaultWebAPIPort int = 1312
|
||||||
|
descriptionWebAPIPort string = "Target API server port"
|
||||||
|
|
||||||
|
viperWebAPIProtocol string = "api.protocol"
|
||||||
|
flagWebAPIProtocol string = "api-protocol"
|
||||||
|
defaultWebAPIProtocol string = "http"
|
||||||
|
descriptionWebAPIProtocol string = "Target API server protocol (http/https)"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Config struct {
|
||||||
|
API struct {
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Key string `yaml:"key"`
|
||||||
|
} `yaml:"api"`
|
||||||
|
DB struct {
|
||||||
|
Database string `yaml:"database"`
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
SSLMode string `yaml:"sslmode"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
User string `yaml:"user"`
|
||||||
|
} `yaml:"db"`
|
||||||
|
Web struct {
|
||||||
|
User string `yaml:"user"`
|
||||||
|
Password string `yaml:"password"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
API struct {
|
||||||
|
Host string `yaml:"host"`
|
||||||
|
Key string `yaml:"key"`
|
||||||
|
Port int `yaml:"port"`
|
||||||
|
Protocol string `yaml:"protocol"`
|
||||||
|
} `yaml:"api"`
|
||||||
|
} `yaml:"web"`
|
||||||
|
}
|
|
@ -1,78 +0,0 @@
|
||||||
package data
|
|
||||||
|
|
||||||
import (
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"codeberg.org/vlbeaudoin/voki/v2"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/models"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/responses"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ApiClient struct {
|
|
||||||
Voki *voki.Voki
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewApiClientFromViper(client *http.Client) *ApiClient {
|
|
||||||
apiClientKey := viper.GetString("web.api.key")
|
|
||||||
apiClientHost := viper.GetString("web.api.host")
|
|
||||||
apiClientProtocol := viper.GetString("web.api.protocol")
|
|
||||||
apiClientPort := viper.GetInt("web.api.port")
|
|
||||||
|
|
||||||
return NewApiClient(client, apiClientKey, apiClientHost, apiClientProtocol, apiClientPort)
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewApiClient(client *http.Client, key, host, protocol string, port int) *ApiClient {
|
|
||||||
return &ApiClient{
|
|
||||||
Voki: voki.New(client, host, key, port, protocol),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// GetHealth allows checking for API server health
|
|
||||||
func (a *ApiClient) GetHealth() (string, error) {
|
|
||||||
var getHealthResponse responses.GetHealthResponse
|
|
||||||
err := a.Voki.Unmarshal(http.MethodGet, "/v6/health", nil, true, &getHealthResponse)
|
|
||||||
if err != nil {
|
|
||||||
return getHealthResponse.Message, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if getHealthResponse.Message == "" {
|
|
||||||
return getHealthResponse.Message, errors.New("Could not confirm that API server is up, no response message")
|
|
||||||
}
|
|
||||||
|
|
||||||
return getHealthResponse.Message, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApiClient) GetMembre(membreID string) (models.Membre, error) {
|
|
||||||
var getMembreResponse struct {
|
|
||||||
Message string `json:"message"`
|
|
||||||
Data struct {
|
|
||||||
Membre models.Membre `json:"membre"`
|
|
||||||
} `json:"data"`
|
|
||||||
}
|
|
||||||
|
|
||||||
if membreID == "" {
|
|
||||||
return getMembreResponse.Data.Membre, errors.New("Veuillez fournir un numéro étudiant à rechercher")
|
|
||||||
}
|
|
||||||
|
|
||||||
err := a.Voki.Unmarshal(http.MethodGet, fmt.Sprintf("/v6/membres/%s", membreID), nil, true, &getMembreResponse)
|
|
||||||
if err != nil {
|
|
||||||
return getMembreResponse.Data.Membre, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if getMembreResponse.Data.Membre == *new(models.Membre) {
|
|
||||||
if getMembreResponse.Message != "" {
|
|
||||||
return getMembreResponse.Data.Membre, fmt.Errorf(getMembreResponse.Message)
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMembreResponse.Data.Membre, fmt.Errorf("Ce numéro étudiant ne correspond à aucunE membre")
|
|
||||||
}
|
|
||||||
|
|
||||||
return getMembreResponse.Data.Membre, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *ApiClient) ListMembres() (r responses.ListMembresResponse, err error) {
|
|
||||||
return r, a.Voki.Unmarshal(http.MethodGet, "/v6/membres", nil, true, &r)
|
|
||||||
}
|
|
|
@ -1,15 +1,26 @@
|
||||||
package data
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"context"
|
||||||
"fmt"
|
_ "embed"
|
||||||
|
|
||||||
"git.agecem.com/agecem/bottin/v6/models"
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
_ "github.com/jackc/pgx/stdlib"
|
|
||||||
"github.com/jmoiron/sqlx"
|
|
||||||
"github.com/spf13/viper"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
//go:embed sql/schema.sql
|
||||||
|
var sqlSchema string
|
||||||
|
|
||||||
|
type PostgresClient struct {
|
||||||
|
Ctx context.Context
|
||||||
|
Pool *pgxpool.Pool
|
||||||
|
}
|
||||||
|
|
||||||
|
func (db *PostgresClient) CreateOrReplaceSchema() error {
|
||||||
|
_, err := db.Pool.Exec(db.Ctx, sqlSchema)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
// DataClient is a postgres client based on sqlx
|
// DataClient is a postgres client based on sqlx
|
||||||
type DataClient struct {
|
type DataClient struct {
|
||||||
PostgresConnection PostgresConnection
|
PostgresConnection PostgresConnection
|
||||||
|
@ -28,11 +39,11 @@ type PostgresConnection struct {
|
||||||
func NewDataClientFromViper() (*DataClient, error) {
|
func NewDataClientFromViper() (*DataClient, error) {
|
||||||
client, err := NewDataClient(
|
client, err := NewDataClient(
|
||||||
PostgresConnection{
|
PostgresConnection{
|
||||||
User: viper.GetString("db.user"),
|
User: viper.GetString(cmd.ViperDBHost),
|
||||||
Password: viper.GetString("db.password"),
|
Password: viper.GetString(cmd.ViperDBPassword),
|
||||||
Host: viper.GetString("db.host"),
|
Host: viper.GetString(cmd.ViperDBHost),
|
||||||
Database: viper.GetString("db.database"),
|
Database: viper.GetString(cmd.ViperDBDatabase),
|
||||||
Port: viper.GetInt("db.port"),
|
Port: viper.GetInt(cmd.ViperDBPort),
|
||||||
})
|
})
|
||||||
|
|
||||||
return client, err
|
return client, err
|
||||||
|
@ -60,7 +71,7 @@ func NewDataClient(connection PostgresConnection) (*DataClient, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataClient) Seed() (int64, error) {
|
func (d *DataClient) Seed() (int64, error) {
|
||||||
result, err := d.DB.Exec(models.Schema)
|
result, err := d.DB.Exec(sqlSchema)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, err
|
return 0, err
|
||||||
}
|
}
|
||||||
|
@ -74,7 +85,7 @@ func (d *DataClient) Seed() (int64, error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// InsertMembres inserts a slice of Membre into a database, returning the amount inserted and any error encountered
|
// InsertMembres inserts a slice of Membre into a database, returning the amount inserted and any error encountered
|
||||||
func (d *DataClient) InsertMembres(membres []models.Membre) (int64, error) {
|
func (d *DataClient) InsertMembres(membres []Membre) (int64, error) {
|
||||||
var rowsInserted int64
|
var rowsInserted int64
|
||||||
tx, err := d.DB.Beginx()
|
tx, err := d.DB.Beginx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -107,7 +118,7 @@ func (d *DataClient) InsertMembres(membres []models.Membre) (int64, error) {
|
||||||
return rowsInserted, nil
|
return rowsInserted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataClient) InsertProgrammes(programmes []models.Programme) (int64, error) {
|
func (d *DataClient) InsertProgrammes(programmes []Programme) (int64, error) {
|
||||||
var rowsInserted int64
|
var rowsInserted int64
|
||||||
tx, err := d.DB.Beginx()
|
tx, err := d.DB.Beginx()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -141,8 +152,8 @@ func (d *DataClient) InsertProgrammes(programmes []models.Programme) (int64, err
|
||||||
return rowsInserted, nil
|
return rowsInserted, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataClient) GetMembre(membreID string) (models.Membre, error) {
|
func (d *DataClient) GetMembre(membreID string) (Membre, error) {
|
||||||
var membre models.Membre
|
var membre Membre
|
||||||
|
|
||||||
rows, err := d.DB.Queryx("SELECT * FROM membres WHERE id = $1 LIMIT 1;", membreID)
|
rows, err := d.DB.Queryx("SELECT * FROM membres WHERE id = $1 LIMIT 1;", membreID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -177,6 +188,7 @@ func (d *DataClient) UpdateMembreName(membreID, newName string) (int64, error) {
|
||||||
return rows, nil
|
return rows, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *DataClient) GetMembres() (membres []models.Membre, err error) {
|
func (d *DataClient) GetMembres() (membres []Membre, err error) {
|
||||||
return membres, d.DB.Select(&membres, "SELECT * FROM membres;")
|
return membres, d.DB.Select(&membres, "SELECT * FROM membres;")
|
||||||
}
|
}
|
||||||
|
*/
|
45
db_test.go
Normal file
45
db_test.go
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/jackc/pgx/v5/pgxpool"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDB(t *testing.T) {
|
||||||
|
ctx := context.Background()
|
||||||
|
|
||||||
|
//prep
|
||||||
|
pool, err := pgxpool.New(
|
||||||
|
ctx,
|
||||||
|
fmt.Sprintf(
|
||||||
|
"user=%s password=%s database=%s host=%s port=%d sslmode=%s ",
|
||||||
|
"bottin",
|
||||||
|
"bottin",
|
||||||
|
"bottin",
|
||||||
|
"localhost",
|
||||||
|
5432,
|
||||||
|
"prefer", //TODO change to "require"
|
||||||
|
))
|
||||||
|
if err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer pool.Close()
|
||||||
|
|
||||||
|
db := &PostgresClient{
|
||||||
|
Ctx: ctx,
|
||||||
|
Pool: pool,
|
||||||
|
}
|
||||||
|
|
||||||
|
//exec
|
||||||
|
|
||||||
|
t.Run("create or replace schema",
|
||||||
|
func(t *testing.T) {
|
||||||
|
if err := db.CreateOrReplaceSchema(); err != nil {
|
||||||
|
t.Error(err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
|
@ -1,7 +1,7 @@
|
||||||
services:
|
services:
|
||||||
|
|
||||||
db:
|
db:
|
||||||
image: 'docker.io/library/postgres:16.1'
|
image: 'docker.io/library/postgres:16'
|
||||||
environment:
|
environment:
|
||||||
POSTGRES_DATABASE: "${BOTTIN_POSTGRES_DATABASE}"
|
POSTGRES_DATABASE: "${BOTTIN_POSTGRES_DATABASE}"
|
||||||
POSTGRES_PASSWORD: "${BOTTIN_POSTGRES_PASSWORD}"
|
POSTGRES_PASSWORD: "${BOTTIN_POSTGRES_PASSWORD}"
|
||||||
|
|
|
@ -1,19 +1,4 @@
|
||||||
package models
|
package main
|
||||||
|
|
||||||
const Schema = `
|
|
||||||
CREATE TABLE IF NOT EXISTS programmes (
|
|
||||||
id TEXT PRIMARY KEY,
|
|
||||||
titre TEXT
|
|
||||||
);
|
|
||||||
|
|
||||||
CREATE TABLE IF NOT EXISTS membres (
|
|
||||||
id VARCHAR(7) PRIMARY KEY,
|
|
||||||
last_name TEXT,
|
|
||||||
first_name TEXT,
|
|
||||||
prefered_name TEXT,
|
|
||||||
programme_id TEXT REFERENCES programmes(id)
|
|
||||||
);
|
|
||||||
`
|
|
||||||
|
|
||||||
type Programme struct {
|
type Programme struct {
|
||||||
ID string `db:"id" json:"programme_id" csv:"programme_id"`
|
ID string `db:"id" json:"programme_id" csv:"programme_id"`
|
||||||
|
@ -27,7 +12,3 @@ type Membre struct {
|
||||||
PreferedName string `db:"prefered_name" json:"prefered_name" csv:"prefered_name"`
|
PreferedName string `db:"prefered_name" json:"prefered_name" csv:"prefered_name"`
|
||||||
ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"`
|
ProgrammeID string `db:"programme_id" json:"programme_id" csv:"programme_id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type Entry interface {
|
|
||||||
Programme | Membre
|
|
||||||
}
|
|
16
go.mod
16
go.mod
|
@ -1,36 +1,31 @@
|
||||||
module git.agecem.com/agecem/bottin/v6
|
module git.agecem.com/agecem/bottin/v7
|
||||||
|
|
||||||
go 1.22.0
|
go 1.22.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
codeberg.org/vlbeaudoin/serpents v1.1.0
|
|
||||||
codeberg.org/vlbeaudoin/voki/v2 v2.0.3
|
codeberg.org/vlbeaudoin/voki/v2 v2.0.3
|
||||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a
|
github.com/jackc/pgx/v5 v5.6.0
|
||||||
github.com/jackc/pgx v3.6.2+incompatible
|
|
||||||
github.com/jmoiron/sqlx v1.3.5
|
|
||||||
github.com/labstack/echo/v4 v4.11.4
|
github.com/labstack/echo/v4 v4.11.4
|
||||||
github.com/spf13/cobra v1.8.0
|
github.com/spf13/cobra v1.8.0
|
||||||
github.com/spf13/viper v1.18.2
|
github.com/spf13/viper v1.18.2
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/cockroachdb/apd v1.1.0 // indirect
|
|
||||||
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
github.com/fsnotify/fsnotify v1.7.0 // indirect
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible // indirect
|
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
github.com/golang-jwt/jwt v3.2.2+incompatible // indirect
|
||||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 // indirect
|
github.com/jackc/pgpassfile v1.0.0 // indirect
|
||||||
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 // indirect
|
||||||
github.com/labstack/gommon v0.4.2 // indirect
|
github.com/labstack/gommon v0.4.2 // indirect
|
||||||
github.com/magiconair/properties v1.8.7 // indirect
|
github.com/magiconair/properties v1.8.7 // indirect
|
||||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
github.com/pelletier/go-toml/v2 v2.1.1 // indirect
|
||||||
github.com/pkg/errors v0.9.1 // indirect
|
|
||||||
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
github.com/sagikazarmark/locafero v0.4.0 // indirect
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
github.com/sagikazarmark/slog-shim v0.1.0 // indirect
|
||||||
github.com/shopspring/decimal v1.3.1 // indirect
|
|
||||||
github.com/sourcegraph/conc v0.3.0 // indirect
|
github.com/sourcegraph/conc v0.3.0 // indirect
|
||||||
github.com/spf13/afero v1.11.0 // indirect
|
github.com/spf13/afero v1.11.0 // indirect
|
||||||
github.com/spf13/cast v1.6.0 // indirect
|
github.com/spf13/cast v1.6.0 // indirect
|
||||||
|
@ -42,6 +37,7 @@ require (
|
||||||
golang.org/x/crypto v0.19.0 // indirect
|
golang.org/x/crypto v0.19.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
|
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a // indirect
|
||||||
golang.org/x/net v0.21.0 // indirect
|
golang.org/x/net v0.21.0 // indirect
|
||||||
|
golang.org/x/sync v0.5.0 // indirect
|
||||||
golang.org/x/sys v0.17.0 // indirect
|
golang.org/x/sys v0.17.0 // indirect
|
||||||
golang.org/x/text v0.14.0 // indirect
|
golang.org/x/text v0.14.0 // indirect
|
||||||
golang.org/x/time v0.5.0 // indirect
|
golang.org/x/time v0.5.0 // indirect
|
||||||
|
|
40
go.sum
40
go.sum
|
@ -1,9 +1,5 @@
|
||||||
codeberg.org/vlbeaudoin/serpents v1.1.0 h1:U9f2+2D1yUVHx90yePi2ZOLRLG/Wkoob4JXDIVyoBwA=
|
|
||||||
codeberg.org/vlbeaudoin/serpents v1.1.0/go.mod h1:3bE/R0ToABwcUJtS1VcGEBa86K5FYhrZGAbFl2qL8kQ=
|
|
||||||
codeberg.org/vlbeaudoin/voki/v2 v2.0.3 h1:H3j7yk8uBiDK19OUWAKbYKmw0tsSw4t0LA5lyAfyT3E=
|
codeberg.org/vlbeaudoin/voki/v2 v2.0.3 h1:H3j7yk8uBiDK19OUWAKbYKmw0tsSw4t0LA5lyAfyT3E=
|
||||||
codeberg.org/vlbeaudoin/voki/v2 v2.0.3/go.mod h1:TVdOLAxB94EJkylt5dleJlTkBzuxau8Xwd4TANQIR7U=
|
codeberg.org/vlbeaudoin/voki/v2 v2.0.3/go.mod h1:TVdOLAxB94EJkylt5dleJlTkBzuxau8Xwd4TANQIR7U=
|
||||||
github.com/cockroachdb/apd v1.1.0 h1:3LFP3629v+1aKXU5Q37mxmRxX/pIu1nijXydLShEq5I=
|
|
||||||
github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ=
|
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
@ -13,12 +9,6 @@ github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHk
|
||||||
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
|
||||||
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=
|
||||||
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=
|
||||||
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
|
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
|
||||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a h1:RYfmiM0zluBJOiPDJseKLEN4BapJ42uSi9SZBQ2YyiA=
|
|
||||||
github.com/gocarina/gocsv v0.0.0-20231116093920-b87c2d0e983a/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI=
|
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible h1:3qXRTX8/NbyulANqlc0lchS1gqAVxRgsuW1YrTJupqA=
|
|
||||||
github.com/gofrs/uuid v4.4.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY=
|
||||||
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I=
|
||||||
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
|
||||||
|
@ -27,12 +17,14 @@ github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733 h1:vr3AYkKovP8uR8AvSGGUK1IDqRa5lAAvEkZG1LKaCRc=
|
github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
|
||||||
github.com/jackc/fake v0.0.0-20150926172116-812a484cc733/go.mod h1:WrMFNQdiFJ80sQsxDoMokWK1W5TQtxBFNpzWTD84ibQ=
|
github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
|
||||||
github.com/jackc/pgx v3.6.2+incompatible h1:2zP5OD7kiyR3xzRYMhOcXVvkDZsImVXfj+yIyTQf3/o=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
|
||||||
github.com/jackc/pgx v3.6.2+incompatible/go.mod h1:0ZGrqGqkRlliWnWB4zKnWtjbSWbGkVEFm4TeybAXq+I=
|
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
|
||||||
github.com/jmoiron/sqlx v1.3.5 h1:vFFPA71p1o5gAeqtEAwLU4dnX2napprKtHr7PYIcN3g=
|
github.com/jackc/pgx/v5 v5.6.0 h1:SWJzexBzPL5jb0GEsrPMLIsi/3jOo7RHlzTjcAeDrPY=
|
||||||
github.com/jmoiron/sqlx v1.3.5/go.mod h1:nRVWtLre0KfCLJvgxzCsLVMogSvQ1zNJtpYr2Ccp0mQ=
|
github.com/jackc/pgx/v5 v5.6.0/go.mod h1:DNZ/vlrUnhWCoFGxHAG8U2ljioxukquj7utPDgtQdTw=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1 h1:RhxXJtFG022u4ibrCSMSiu5aOq1i77R3OHKNJj77OAk=
|
||||||
|
github.com/jackc/puddle/v2 v2.2.1/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=
|
||||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||||
|
@ -41,8 +33,6 @@ github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zG
|
||||||
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8=
|
||||||
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0=
|
||||||
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU=
|
||||||
github.com/lib/pq v1.2.0 h1:LXpIM/LZ5xGFhOpXAQUIMM1HdyqzVYM13zNdjCEEcA0=
|
|
||||||
github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
|
||||||
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY=
|
||||||
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
|
||||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||||
|
@ -50,14 +40,10 @@ github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovk
|
||||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
github.com/mattn/go-sqlite3 v1.14.6 h1:dNPt6NO46WmLVt2DLNpwczCmdV5boIZ6g/tlDrlRUbg=
|
|
||||||
github.com/mattn/go-sqlite3 v1.14.6/go.mod h1:NyWgC/yNuGj7Q9rpYnZvas74GogHl5/Z4A/KQRfk6bU=
|
|
||||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
github.com/pelletier/go-toml/v2 v2.1.1 h1:LWAJwfNvjQZCFIDKWYQaM62NcYeYViCmWIwmOStowAI=
|
||||||
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
github.com/pelletier/go-toml/v2 v2.1.1/go.mod h1:tJU2Z3ZkXwnxa4DPO899bsyIoywizdUvyaeZurnPPDc=
|
||||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
|
||||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
|
||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
@ -68,8 +54,6 @@ github.com/sagikazarmark/locafero v0.4.0 h1:HApY1R9zGo4DBgr7dqsTH/JJxLTTsOt7u6ke
|
||||||
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
github.com/sagikazarmark/locafero v0.4.0/go.mod h1:Pe1W6UlPYUk/+wc/6KFhbORCfqzgYEpgQ3O5fPuL3H4=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
|
||||||
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
|
||||||
github.com/shopspring/decimal v1.3.1 h1:2Usl1nmF/WZucqkFZhnfFYxxxu8LG21F6nPQBE5gKV8=
|
|
||||||
github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o=
|
|
||||||
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
github.com/sourcegraph/conc v0.3.0 h1:OQTbbt6P72L20UqAkXXuLOj79LfEanQ+YQFNpLA9ySo=
|
||||||
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
github.com/sourcegraph/conc v0.3.0/go.mod h1:Sdozi7LEKbFPqYX2/J+iBAM6HpqSLTASQIKqDmF7Mt0=
|
||||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||||
|
@ -85,6 +69,8 @@ github.com/spf13/viper v1.18.2/go.mod h1:EKmWIqdnk5lOcmR72yw6hS+8OPYcwD0jteitLMV
|
||||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
|
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||||
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
|
||||||
|
@ -103,6 +89,8 @@ golang.org/x/exp v0.0.0-20240213143201-ec583247a57a h1:HinSgX1tJRX3KsL//Gxynpw5C
|
||||||
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
golang.org/x/exp v0.0.0-20240213143201-ec583247a57a/go.mod h1:CxmFvTBINI24O/j8iY7H1xHzx2i4OsyguNBmN/uPtqc=
|
||||||
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
|
||||||
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
|
||||||
|
golang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=
|
||||||
|
golang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
|
||||||
|
@ -112,8 +100,8 @@ golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
|
||||||
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk=
|
||||||
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
golang.org/x/time v0.5.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
|
|
@ -1,11 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import "git.agecem.com/agecem/bottin/v6/data"
|
|
||||||
|
|
||||||
type Handler struct {
|
|
||||||
DataClient *data.DataClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func New(dataClient *data.DataClient) *Handler {
|
|
||||||
return &Handler{DataClient: dataClient}
|
|
||||||
}
|
|
|
@ -1,36 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.agecem.com/agecem/bottin/v6/data"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/responses"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h *Handler) GetHealth(c echo.Context) error {
|
|
||||||
var response responses.GetHealthResponse
|
|
||||||
|
|
||||||
dataClient, err := data.NewDataClientFromViper()
|
|
||||||
if err != nil {
|
|
||||||
response.StatusCode = http.StatusInternalServerError
|
|
||||||
response.Message = "Error during data.NewDataClientFromViper()"
|
|
||||||
response.Error = err.Error()
|
|
||||||
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
defer dataClient.DB.Close()
|
|
||||||
|
|
||||||
if err = dataClient.DB.Ping(); err != nil {
|
|
||||||
response.StatusCode = http.StatusInternalServerError
|
|
||||||
response.Message = "Error during dataClient.DB.Ping()"
|
|
||||||
response.Error = err.Error()
|
|
||||||
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
response.StatusCode = http.StatusOK
|
|
||||||
response.Message = "Bottin API v6 is ready"
|
|
||||||
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
|
@ -1,129 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"encoding/csv"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.agecem.com/agecem/bottin/v6/models"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/responses"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
|
|
||||||
"github.com/gocarina/gocsv"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h *Handler) PostMembres(c echo.Context) error {
|
|
||||||
var response responses.PostMembresResponse
|
|
||||||
|
|
||||||
var membres []models.Membre
|
|
||||||
|
|
||||||
switch c.Request().Header.Get("Content-Type") {
|
|
||||||
case "application/json":
|
|
||||||
if err := c.Bind(&membres); err != nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Could not bind membres"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
case "text/csv":
|
|
||||||
body := c.Request().Body
|
|
||||||
if body == nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Request body is empty"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
defer body.Close()
|
|
||||||
|
|
||||||
// Parse the CSV data from the request body using gocsv.
|
|
||||||
if err := gocsv.Unmarshal(body, &membres); err != nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Could not unmarshal into membres"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Invalid Content-Type: Please use application/json or text/csv"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(membres) == 0 {
|
|
||||||
response.StatusCode = http.StatusOK
|
|
||||||
response.Message = "Nothing to do"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
newMembres, err := h.DataClient.InsertMembres(membres)
|
|
||||||
if err != nil {
|
|
||||||
response.StatusCode = http.StatusInternalServerError
|
|
||||||
response.Message = "Could not insert membres"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
response.StatusCode = http.StatusCreated
|
|
||||||
response.Message = "Insert successful"
|
|
||||||
response.Data.MembresInserted = newMembres
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) PostProgrammes(c echo.Context) error {
|
|
||||||
var response responses.PostProgrammesResponse
|
|
||||||
|
|
||||||
var programmes []models.Programme
|
|
||||||
|
|
||||||
switch c.Request().Header.Get("Content-Type") {
|
|
||||||
case "application/json":
|
|
||||||
if err := c.Bind(&programmes); err != nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Could not bind programmes"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
case "text/csv":
|
|
||||||
body := c.Request().Body
|
|
||||||
if body == nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Request body is empty"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
defer body.Close()
|
|
||||||
|
|
||||||
gocsv.SetCSVReader(func(in io.Reader) gocsv.CSVReader {
|
|
||||||
r := csv.NewReader(in)
|
|
||||||
r.Comma = ';'
|
|
||||||
return r // Allows use ; as delimiter
|
|
||||||
})
|
|
||||||
|
|
||||||
// Parse the CSV data from the request body using gocsv.
|
|
||||||
if err := gocsv.Unmarshal(body, &programmes); err != nil {
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Could not unmarshal into programmes"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
response.StatusCode = http.StatusBadRequest
|
|
||||||
response.Message = "Invalid Content-Type"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
if len(programmes) == 0 {
|
|
||||||
response.StatusCode = http.StatusOK
|
|
||||||
response.Message = "Nothing to do"
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
newProgrammes, err := h.DataClient.InsertProgrammes(programmes)
|
|
||||||
if err != nil {
|
|
||||||
response.StatusCode = http.StatusInternalServerError
|
|
||||||
response.Message = "Could not insert programmes"
|
|
||||||
response.Error = err.Error()
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
||||||
|
|
||||||
response.StatusCode = http.StatusCreated
|
|
||||||
response.Message = "Insert successful"
|
|
||||||
response.Data.ProgrammesInserted = newProgrammes
|
|
||||||
return c.JSON(response.StatusCode, response)
|
|
||||||
}
|
|
|
@ -1,61 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.agecem.com/agecem/bottin/v6/responses"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h *Handler) ReadMembre(c echo.Context) error {
|
|
||||||
membreID := c.Param("membre_id")
|
|
||||||
|
|
||||||
membre, err := h.DataClient.GetMembre(membreID)
|
|
||||||
if err != nil {
|
|
||||||
if err.Error() == "No membre by that id was found" {
|
|
||||||
return c.JSON(http.StatusNotFound, map[string]string{
|
|
||||||
"message": "Not Found",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{
|
|
||||||
"message": "Unknown error during GetMembre",
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
|
||||||
"message": "Read successful",
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"membre": &membre,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) ListMembres(c echo.Context) error {
|
|
||||||
var r responses.ListMembresResponse
|
|
||||||
|
|
||||||
membres, err := h.DataClient.GetMembres()
|
|
||||||
if err != nil {
|
|
||||||
r.StatusCode = http.StatusInternalServerError
|
|
||||||
r.Error = err.Error()
|
|
||||||
r.Message = "Error during (*handlers.Handler).DataClient.GetMembres"
|
|
||||||
|
|
||||||
return c.JSON(r.StatusCode, r)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.StatusCode = http.StatusOK
|
|
||||||
|
|
||||||
switch membres := len(membres); membres {
|
|
||||||
case 0:
|
|
||||||
r.Message = "No membres returned from database"
|
|
||||||
case 1:
|
|
||||||
r.Message = "Membre returned from database"
|
|
||||||
default:
|
|
||||||
r.Message = fmt.Sprintf("%d membres returned from database", membres)
|
|
||||||
}
|
|
||||||
|
|
||||||
r.Data.Membres = membres
|
|
||||||
|
|
||||||
return c.JSON(r.StatusCode, r)
|
|
||||||
}
|
|
|
@ -1,24 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h *Handler) PostSeed(c echo.Context) error {
|
|
||||||
rows, err := h.DataClient.Seed()
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{
|
|
||||||
"message": "Seed failed",
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
|
||||||
"message": "Seed successful",
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"rows": rows,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
|
@ -1,42 +0,0 @@
|
||||||
package handlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
func (h *Handler) PutMembrePreferedName(c echo.Context) error {
|
|
||||||
membreID := c.Param("membre_id")
|
|
||||||
|
|
||||||
var newName string
|
|
||||||
|
|
||||||
err := c.Bind(&newName)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{
|
|
||||||
"message": "Could not bind newName",
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
rows, err := h.DataClient.UpdateMembreName(membreID, newName)
|
|
||||||
if err != nil {
|
|
||||||
return c.JSON(http.StatusInternalServerError, map[string]string{
|
|
||||||
"message": "Could not update membre name",
|
|
||||||
"error": err.Error(),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
if rows == 0 {
|
|
||||||
return c.JSON(http.StatusBadRequest, map[string]string{
|
|
||||||
"message": "No update was done, probably no membre by that id",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.JSON(http.StatusOK, map[string]interface{}{
|
|
||||||
"message": "Update successful",
|
|
||||||
"data": map[string]interface{}{
|
|
||||||
"rows": rows,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
}
|
|
16
main.go
16
main.go
|
@ -1,7 +1,19 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import "git.agecem.com/agecem/bottin/v6/cmd"
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
cmd.Execute()
|
if err := Run(context.Background(), Config{}, nil, os.Stdout); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func Run(ctx context.Context, config Config, args []string, stdout io.Writer) error {
|
||||||
|
return fmt.Errorf("not implemented")
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,18 @@
|
||||||
package responses
|
package main
|
||||||
|
|
||||||
import "codeberg.org/vlbeaudoin/voki/v2"
|
import "codeberg.org/vlbeaudoin/voki/v2"
|
||||||
|
|
||||||
|
type GetHealthResponse struct {
|
||||||
|
voki.ResponseWithError
|
||||||
|
}
|
||||||
|
|
||||||
|
type ListMembresResponse struct {
|
||||||
|
voki.ResponseWithError
|
||||||
|
Data struct {
|
||||||
|
Membres []Membre
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
type PostMembresResponse struct {
|
type PostMembresResponse struct {
|
||||||
voki.ResponseWithError
|
voki.ResponseWithError
|
||||||
Data struct {
|
Data struct {
|
|
@ -1,7 +0,0 @@
|
||||||
package responses
|
|
||||||
|
|
||||||
import "codeberg.org/vlbeaudoin/voki/v2"
|
|
||||||
|
|
||||||
type GetHealthResponse struct {
|
|
||||||
voki.ResponseWithError
|
|
||||||
}
|
|
|
@ -1,13 +0,0 @@
|
||||||
package responses
|
|
||||||
|
|
||||||
import (
|
|
||||||
"codeberg.org/vlbeaudoin/voki/v2"
|
|
||||||
"git.agecem.com/agecem/bottin/v6/models"
|
|
||||||
)
|
|
||||||
|
|
||||||
type ListMembresResponse struct {
|
|
||||||
voki.ResponseWithError
|
|
||||||
Data struct {
|
|
||||||
Membres []models.Membre
|
|
||||||
}
|
|
||||||
}
|
|
12
sql/schema.sql
Normal file
12
sql/schema.sql
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
CREATE TABLE IF NOT EXISTS programmes (
|
||||||
|
id TEXT PRIMARY KEY,
|
||||||
|
titre TEXT
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS membres (
|
||||||
|
id VARCHAR(7) PRIMARY KEY,
|
||||||
|
last_name TEXT,
|
||||||
|
first_name TEXT,
|
||||||
|
prefered_name TEXT,
|
||||||
|
programme_id TEXT REFERENCES programmes(id)
|
||||||
|
);
|
|
@ -1,9 +0,0 @@
|
||||||
MIT License
|
|
||||||
|
|
||||||
Copyright (c) 2021-2023 AGECEM
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
@ -1 +0,0 @@
|
||||||
deprecated, see git.agecem.com/agecem/bottin or git.agecem.com/agecem/bottin/v5
|
|
10
v4/go.mod
10
v4/go.mod
|
@ -1,10 +0,0 @@
|
||||||
module git.agecem.com/agecem/bottin/v4
|
|
||||||
|
|
||||||
go 1.20
|
|
||||||
|
|
||||||
//retract (
|
|
||||||
// v4.1.0
|
|
||||||
// v4.0.3
|
|
||||||
// v4.0.2
|
|
||||||
// v4.0.1
|
|
||||||
//)
|
|
10
web/embed.go
10
web/embed.go
|
@ -1,10 +0,0 @@
|
||||||
package web
|
|
||||||
|
|
||||||
import "embed"
|
|
||||||
|
|
||||||
//go:embed templates/*
|
|
||||||
var templatesFS embed.FS
|
|
||||||
|
|
||||||
func GetTemplates() embed.FS {
|
|
||||||
return templatesFS
|
|
||||||
}
|
|
|
@ -1,46 +0,0 @@
|
||||||
package webhandlers
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.agecem.com/agecem/bottin/v6/data"
|
|
||||||
"github.com/labstack/echo/v4"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Handler struct {
|
|
||||||
APIClient *data.ApiClient
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) GetIndex(c echo.Context) error {
|
|
||||||
return c.Render(http.StatusOK, "index-html", nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *Handler) GetMembre(c echo.Context) error {
|
|
||||||
|
|
||||||
membreID := c.QueryParam("membre_id")
|
|
||||||
|
|
||||||
membre, err := h.APIClient.GetMembre(membreID)
|
|
||||||
if err != nil {
|
|
||||||
return c.Render(http.StatusBadRequest, "index-html", struct {
|
|
||||||
Result string
|
|
||||||
}{
|
|
||||||
Result: fmt.Sprintln("👎", err.Error()),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
membreResult := fmt.Sprintf(`👍
|
|
||||||
Membre trouvéE: [%s]`, membre.ID)
|
|
||||||
|
|
||||||
if membre.PreferedName != "" {
|
|
||||||
membreResult = fmt.Sprintf("%s -> %s", membreResult, membre.PreferedName)
|
|
||||||
} else {
|
|
||||||
membreResult = fmt.Sprintf("%s -> %s, %s", membreResult, membre.LastName, membre.FirstName)
|
|
||||||
}
|
|
||||||
|
|
||||||
return c.Render(http.StatusOK, "index-html", struct {
|
|
||||||
Result string
|
|
||||||
}{
|
|
||||||
Result: membreResult,
|
|
||||||
})
|
|
||||||
}
|
|
Loading…
Reference in a new issue