From 3aa7faa2f607fd59b7de7690b6b42c2ce32d239e Mon Sep 17 00:00:00 2001 From: Victor Lacasse-Beaudoin Date: Thu, 25 May 2023 19:22:46 -0400 Subject: [PATCH] [WIP] Add web client Ignore .swp files Rename serverCmd to apiCmd (the web client is technically a server too) Add webCmd for html routes hosting Add embedding and templating for web client Add webhandlers Fix some variables not being filled automatically by viper Change flags and reorganize config structure --- .gitignore | 3 + v4/cmd/{server.go => api.go} | 60 ++++++++++-------- v4/cmd/web.go | 111 ++++++++++++++++++++++++++++++++- v4/docker-compose.yaml | 10 ++- v4/web/embed.go | 6 +- v4/web/templates/index.html | 32 +++++++++- v4/web/webhandlers/handlers.go | 11 ++++ 7 files changed, 201 insertions(+), 32 deletions(-) rename v4/cmd/{server.go => api.go} (59%) create mode 100644 v4/web/webhandlers/handlers.go diff --git a/.gitignore b/.gitignore index ab3b38e..e2f7237 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,6 @@ # env .env + +# .swp +*.swp diff --git a/v4/cmd/server.go b/v4/cmd/api.go similarity index 59% rename from v4/cmd/server.go rename to v4/cmd/api.go index 411eb13..a800734 100644 --- a/v4/cmd/server.go +++ b/v4/cmd/api.go @@ -18,11 +18,22 @@ var ( apiKey string ) -// serverCmd represents the server command -var serverCmd = &cobra.Command{ - Use: "server", +// apiCmd represents the api command +var apiCmd = &cobra.Command{ + Use: "api", Short: "Démarrer le serveur API", + Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { + apiKey = viper.GetString("api.key") + apiPort = viper.GetInt("api.port") + + connection := data.PostgresConnection{ + User: viper.GetString("db.user"), + Password: viper.GetString("db.password"), + Host: viper.GetString("db.host"), + Database: viper.GetString("db.database"), + Port: viper.GetInt("db.port"), + } e := echo.New() @@ -52,14 +63,6 @@ var serverCmd = &cobra.Command{ // Execution - connection := data.PostgresConnection{ - User: viper.GetString("db.user"), - Password: viper.GetString("db.password"), - Host: viper.GetString("db.host"), - Database: viper.GetString("db.database"), - Port: viper.GetInt("db.port"), - } - client, err := data.NewDataClient(connection) if err != nil { log.Fatalf("Could not establish database connection.\n Error: %s\n", err) @@ -72,40 +75,45 @@ var serverCmd = &cobra.Command{ client.DB.Close() + log.Println("apiPort: ", apiPort) + log.Println("apiKey: ", apiKey) + e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", apiPort))) }, } func init() { - rootCmd.AddCommand(serverCmd) + rootCmd.AddCommand(apiCmd) // api.key - serverCmd.Flags().StringVar( - &apiKey, "api-key", "bottin", + apiCmd.Flags().String( + "api-key", "bottin", "API server key. Leave empty for no key auth. (config: 'api.key')") + viper.BindPFlag("api.key", apiCmd.Flags().Lookup("api-key")) // api.port - serverCmd.Flags().IntVar( - &apiPort, "api-port", 1312, + apiCmd.Flags().Int( + "api-port", 1312, "API server port (config:'api.port')") + viper.BindPFlag("api.port", apiCmd.Flags().Lookup("api-port")) // db.database - serverCmd.Flags().String("db-database", "bottin", "Postgres database (config:'db.database')") - viper.BindPFlag("db.database", serverCmd.Flags().Lookup("db-database")) + apiCmd.Flags().String("db-database", "bottin", "Postgres database (config:'db.database')") + viper.BindPFlag("db.database", apiCmd.Flags().Lookup("db-database")) // db.host - serverCmd.Flags().String("db-host", "postgres", "Postgres host (config:'db.host')") - viper.BindPFlag("db.host", serverCmd.Flags().Lookup("db-host")) + apiCmd.Flags().String("db-host", "db", "Postgres host (config:'db.host')") + viper.BindPFlag("db.host", apiCmd.Flags().Lookup("db-host")) // db.password - serverCmd.Flags().String("db-password", "bottin", "Postgres password (config:'db.password')") - viper.BindPFlag("db.password", serverCmd.Flags().Lookup("db-password")) + apiCmd.Flags().String("db-password", "bottin", "Postgres password (config:'db.password')") + viper.BindPFlag("db.password", apiCmd.Flags().Lookup("db-password")) // db.port - serverCmd.Flags().Int("db-port", 5432, "Postgres port (config:'db.port')") - viper.BindPFlag("db.port", serverCmd.Flags().Lookup("db-port")) + apiCmd.Flags().Int("db-port", 5432, "Postgres port (config:'db.port')") + viper.BindPFlag("db.port", apiCmd.Flags().Lookup("db-port")) // db.user - serverCmd.Flags().String("db-user", "bottin", "Postgres user (config:'db.user')") - viper.BindPFlag("db.user", serverCmd.Flags().Lookup("db-user")) + apiCmd.Flags().String("db-user", "bottin", "Postgres user (config:'db.user')") + viper.BindPFlag("db.user", apiCmd.Flags().Lookup("db-user")) } diff --git a/v4/cmd/web.go b/v4/cmd/web.go index ab10ba0..6e812f7 100644 --- a/v4/cmd/web.go +++ b/v4/cmd/web.go @@ -1,20 +1,129 @@ package cmd import ( + "crypto/subtle" + "embed" "fmt" + "html/template" + "io" + "log" + "git.agecem.com/agecem/bottin/v4/web" + "git.agecem.com/agecem/bottin/v4/web/webhandlers" + "github.com/labstack/echo/v4" + "github.com/labstack/echo/v4/middleware" "github.com/spf13/cobra" + "github.com/spf13/viper" ) +var ( + webUser string + webPassword string + webPort int + webApiHost string + webApiKey string + webApiPort int +) + +var templatesFS embed.FS + +type Template struct { + templates *template.Template +} + +func (t *Template) Render(w io.Writer, name string, data interface{}, c echo.Context) error { + return t.templates.ExecuteTemplate(w, name, data) +} + // webCmd represents the web command var webCmd = &cobra.Command{ Use: "web", Short: "Démarrer le client web", + Args: cobra.ExactArgs(0), Run: func(cmd *cobra.Command, args []string) { - fmt.Println("web called") + webApiHost = viper.GetString("web.api.host") + webApiKey = viper.GetString("web.api.key") + webApiPort = viper.GetInt("web.api.port") + webPassword = viper.GetString("web.password") + webPort = viper.GetInt("web.port") + webUser = viper.GetString("web.user") + + e := echo.New() + + // Middlewares + + e.Pre(middleware.AddTrailingSlash()) + + e.Use(middleware.BasicAuth(func(user, password string, c echo.Context) (bool, error) { + usersMatch := subtle.ConstantTimeCompare([]byte(user), []byte(webUser)) == 1 + passwordsMatch := subtle.ConstantTimeCompare([]byte(password), []byte(webPassword)) == 1 + return usersMatch && passwordsMatch, nil + })) + + // Template + + t := &Template{ + templates: template.Must(template.ParseFS(templatesFS, "templates/*.html")), + } + + e.Renderer = t + + // Routes + + e.GET("/", webhandlers.GetIndex) + + // Execution + + fmt.Println("webPort: ", webPort) + fmt.Println("web.port: ", viper.GetInt("web.port")) + webPortFlag, err := cmd.Flags().GetInt("web-port") + if err != nil { + log.Fatal(err) + } + fmt.Println("web-port: ", webPortFlag) + + e.Logger.Fatal(e.Start( + fmt.Sprintf(":%d", webPort))) }, } func init() { rootCmd.AddCommand(webCmd) + templatesFS = web.GetTemplates() + + // web.api.host + webCmd.Flags().String( + "web-api-host", "api", + "Remote API server host (config:'web.api.host')") + viper.BindPFlag("web.api.host", webCmd.Flags().Lookup("web-api-host")) + + // web.api.key + webCmd.Flags().String( + "web-api-key", "bottin", + "Remote API server key (config:'web.api.key')") + viper.BindPFlag("web.api.key", webCmd.Flags().Lookup("web-api-key")) + + // web.api.port + webCmd.Flags().Int( + "web-api-port", 1312, + "Remote API server port (config:'web.api.port')") + viper.BindPFlag("web.api.port", webCmd.Flags().Lookup("web-api-port")) + + // web.password + webCmd.Flags().String( + "web-password", "bottin", + "Web client password (config:'web.password')") + viper.BindPFlag("web.password", webCmd.Flags().Lookup("web-password")) + + // web.port + webCmd.Flags().Int( + "web-port", 2312, + "Web client port (config:'web.port')") + viper.BindPFlag("web.port", webCmd.Flags().Lookup("web-port")) + + // web.user + webCmd.Flags().String( + "web-user", "bottin", + "Web client user (config:'web.user')") + viper.BindPFlag("web.user", webCmd.Flags().Lookup("web-user")) } diff --git a/v4/docker-compose.yaml b/v4/docker-compose.yaml index bfb46f1..53b726c 100644 --- a/v4/docker-compose.yaml +++ b/v4/docker-compose.yaml @@ -1,6 +1,6 @@ services: - postgres: + db: image: postgres:latest environment: POSTGRES_DATABASE: "${BOTTIN_POSTGRES_DATABASE}" @@ -11,11 +11,19 @@ services: volumes: - 'pgdata:/var/lib/postgresql/data' + #api: + # depends_on: db + + #web: + # depends_on: api + adminer: image: adminer restart: always ports: - 8088:8080 + depends_on: + - db volumes: pgdata: diff --git a/v4/web/embed.go b/v4/web/embed.go index cae74d2..465e5ec 100644 --- a/v4/web/embed.go +++ b/v4/web/embed.go @@ -2,9 +2,9 @@ package web import "embed" -//go:embed templates -var templates embed.FS +//go:embed templates/* +var templatesFS embed.FS func GetTemplates() embed.FS { - return templates + return templatesFS } diff --git a/v4/web/templates/index.html b/v4/web/templates/index.html index 7df8015..e318199 100644 --- a/v4/web/templates/index.html +++ b/v4/web/templates/index.html @@ -1 +1,31 @@ -Hello world from bottin +{{ define "index-html" }} + + + + + + AGECEM | Bottin + + + + +

+ Bottin des membres de l'AGECEM +

+ +

+ Scannez la carte étudiante d'unE membre
+ -ou-
+ Entrez manuellement le code à 7 chiffres +

+ +
+ + +
+ + + +{{ end }} diff --git a/v4/web/webhandlers/handlers.go b/v4/web/webhandlers/handlers.go new file mode 100644 index 0000000..8376dd8 --- /dev/null +++ b/v4/web/webhandlers/handlers.go @@ -0,0 +1,11 @@ +package webhandlers + +import ( + "net/http" + + "github.com/labstack/echo/v4" +) + +func GetIndex(c echo.Context) error { + return c.Render(http.StatusOK, "index-html", nil) +}