Bump root version to v4
Remove all files from v3 Move all files from v4/ to project root
This commit is contained in:
parent
3c0d45fa04
commit
9a0bf87e7b
40 changed files with 423 additions and 2130 deletions
116
cmd/api.go
Normal file
116
cmd/api.go
Normal file
|
@ -0,0 +1,116 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"fmt"
|
||||
"log"
|
||||
|
||||
"git.agecem.com/agecem/bottin/v4/data"
|
||||
"git.agecem.com/agecem/bottin/v4/handlers"
|
||||
"github.com/labstack/echo/v4"
|
||||
"github.com/labstack/echo/v4/middleware"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
var (
|
||||
apiPort int
|
||||
apiKey string
|
||||
)
|
||||
|
||||
// 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()
|
||||
|
||||
// Middlewares
|
||||
|
||||
e.Pre(middleware.AddTrailingSlash())
|
||||
|
||||
if apiKey != "" {
|
||||
e.Use(middleware.KeyAuth(func(key string, c echo.Context) (bool, error) {
|
||||
return subtle.ConstantTimeCompare([]byte(key), []byte(apiKey)) == 1, nil
|
||||
}))
|
||||
}
|
||||
|
||||
// Routes
|
||||
|
||||
e.GET("/v4/", handlers.GetV4)
|
||||
|
||||
e.POST("/v4/membres/", handlers.PostMembres)
|
||||
|
||||
e.GET("/v4/membres/:membre_id/", handlers.ReadMembre)
|
||||
|
||||
e.PUT("/v4/membres/:membre_id/prefered_name/", handlers.PutMembrePreferedName)
|
||||
|
||||
e.POST("/v4/programmes/", handlers.PostProgrammes)
|
||||
|
||||
e.POST("/v4/seed/", handlers.PostSeed)
|
||||
|
||||
// Execution
|
||||
|
||||
client, err := data.NewDataClient(connection)
|
||||
if err != nil {
|
||||
log.Fatalf("Could not establish database connection.\n Error: %s\n", err)
|
||||
}
|
||||
|
||||
err = client.DB.Ping()
|
||||
if err != nil {
|
||||
log.Fatalf("Database was supposed to be ready but Ping() failed.\n Error: %s\n", err)
|
||||
}
|
||||
|
||||
client.DB.Close()
|
||||
|
||||
e.Logger.Fatal(e.Start(fmt.Sprintf(":%d", apiPort)))
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(apiCmd)
|
||||
|
||||
// api.key
|
||||
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
|
||||
apiCmd.Flags().Int(
|
||||
"api-port", 1312,
|
||||
"API server port (config:'api.port')")
|
||||
viper.BindPFlag("api.port", apiCmd.Flags().Lookup("api-port"))
|
||||
|
||||
// db.database
|
||||
apiCmd.Flags().String("db-database", "bottin", "Postgres database (config:'db.database')")
|
||||
viper.BindPFlag("db.database", apiCmd.Flags().Lookup("db-database"))
|
||||
|
||||
// db.host
|
||||
apiCmd.Flags().String("db-host", "db", "Postgres host (config:'db.host')")
|
||||
viper.BindPFlag("db.host", apiCmd.Flags().Lookup("db-host"))
|
||||
|
||||
// db.password
|
||||
apiCmd.Flags().String("db-password", "bottin", "Postgres password (config:'db.password')")
|
||||
viper.BindPFlag("db.password", apiCmd.Flags().Lookup("db-password"))
|
||||
|
||||
// db.port
|
||||
apiCmd.Flags().Int("db-port", 5432, "Postgres port (config:'db.port')")
|
||||
viper.BindPFlag("db.port", apiCmd.Flags().Lookup("db-port"))
|
||||
|
||||
// db.user
|
||||
apiCmd.Flags().String("db-user", "bottin", "Postgres user (config:'db.user')")
|
||||
viper.BindPFlag("db.user", apiCmd.Flags().Lookup("db-user"))
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.agecem.com/agecem/bottin/bottin"
|
||||
"git.agecem.com/agecem/bottin/data"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// importCmd represents the import command
|
||||
var importCmd = &cobra.Command{
|
||||
Use: "import",
|
||||
Short: "Import content into the database",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
|
||||
if len(args) == 0 {
|
||||
log.Fatal("Not enough arguments, needs at least 1 path")
|
||||
} else {
|
||||
data.OpenDatabase()
|
||||
data.MigrateDatabase()
|
||||
bottin.UpdateFlags()
|
||||
|
||||
var valid_args []string
|
||||
|
||||
for _, arg := range args {
|
||||
_, err := os.Stat(arg)
|
||||
|
||||
if !os.IsNotExist(err) {
|
||||
log.Printf("Adding %s to valid_args", arg)
|
||||
valid_args = append(valid_args, arg)
|
||||
}
|
||||
}
|
||||
|
||||
// Import from valid args
|
||||
if len(valid_args) == 1 {
|
||||
// Do once
|
||||
bottin.InsertJson(valid_args[0])
|
||||
|
||||
} else {
|
||||
// Do multiple times
|
||||
for _, path := range valid_args {
|
||||
bottin.InsertJson(path)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
rootCmd.AddCommand(importCmd)
|
||||
}
|
32
cmd/root.go
32
cmd/root.go
|
@ -1,24 +1,3 @@
|
|||
/*
|
||||
Copyright © 2022 AGECEM & Victor Lacasse-Beaudoin
|
||||
|
||||
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.
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
|
@ -34,12 +13,7 @@ var cfgFile string
|
|||
// rootCmd represents the base command when called without any subcommands
|
||||
var rootCmd = &cobra.Command{
|
||||
Use: "bottin",
|
||||
Short: "Bottin de la masse étudiante.",
|
||||
// Uncomment the following line if your bare application
|
||||
// has an action associated with it:
|
||||
//
|
||||
// Run: func(cmd *cobra.Command, args []string) {
|
||||
// },
|
||||
Short: "Application de gestion de distribution d'agendas",
|
||||
}
|
||||
|
||||
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||
|
@ -54,10 +28,6 @@ func Execute() {
|
|||
func init() {
|
||||
cobra.OnInitialize(initConfig)
|
||||
|
||||
// Here you will define your flags and configuration settings.
|
||||
// Cobra supports persistent flags, which, if defined here,
|
||||
// will be global for your application.
|
||||
|
||||
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.bottin.yaml)")
|
||||
}
|
||||
|
||||
|
|
145
cmd/server.go
145
cmd/server.go
|
@ -1,145 +0,0 @@
|
|||
/*
|
||||
Copyright © 2022 Victor Lacasse-Beaudoin <victor.lacassebeaudoin@gmail.com>
|
||||
*/
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"git.agecem.com/agecem/bottin/bottin"
|
||||
"git.agecem.com/agecem/bottin/data"
|
||||
"github.com/spf13/cobra"
|
||||
"github.com/spf13/viper"
|
||||
"log"
|
||||
"time"
|
||||
)
|
||||
|
||||
var (
|
||||
json_insert_path string
|
||||
)
|
||||
|
||||
// serverCmd represents the server command
|
||||
var serverCmd = &cobra.Command{
|
||||
Use: "server",
|
||||
Short: "Run the bottin server",
|
||||
Run: func(cmd *cobra.Command, args []string) {
|
||||
// Timer
|
||||
db_retry_timer := time.Duration(viper.GetInt("db.retry-timer"))
|
||||
|
||||
// Open and migrate db
|
||||
for {
|
||||
err := data.OpenDatabase()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
time.Sleep(db_retry_timer * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
err = data.MigrateDatabase()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
time.Sleep(db_retry_timer * time.Second)
|
||||
continue
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
|
||||
bottin.UpdateFlags()
|
||||
|
||||
// Import from flag
|
||||
if json_insert_path != "" {
|
||||
bottin.InsertJson(json_insert_path)
|
||||
}
|
||||
|
||||
// Run web app
|
||||
bottin.RunServer()
|
||||
},
|
||||
}
|
||||
|
||||
func init() {
|
||||
declareFlags()
|
||||
rootCmd.AddCommand(serverCmd)
|
||||
}
|
||||
|
||||
func declareFlags() {
|
||||
// db.type
|
||||
serverCmd.PersistentFlags().String(
|
||||
"db-type", "",
|
||||
"Database type (config: 'db.type')")
|
||||
viper.BindPFlag(
|
||||
"db.type",
|
||||
serverCmd.PersistentFlags().Lookup("db-type"))
|
||||
serverCmd.MarkPersistentFlagRequired("db.type")
|
||||
|
||||
// db.sqlite.path
|
||||
serverCmd.PersistentFlags().String(
|
||||
"db-sqlite-path", "",
|
||||
"Path to sqlite database (config: 'db.sqlite.path')")
|
||||
viper.BindPFlag(
|
||||
"db.sqlite.path",
|
||||
serverCmd.PersistentFlags().Lookup("db-sqlite-path"))
|
||||
|
||||
// server.port
|
||||
serverCmd.PersistentFlags().Int(
|
||||
"server-port", 1312,
|
||||
"The port on which the web application will server content (config: 'server.port')")
|
||||
viper.BindPFlag(
|
||||
"server.port",
|
||||
serverCmd.PersistentFlags().Lookup("server-port"))
|
||||
|
||||
// server.static_dir
|
||||
serverCmd.PersistentFlags().String(
|
||||
"static-dir", "/var/lib/bottin/static",
|
||||
"The directory containing static assets (config: 'server.static_dir')")
|
||||
viper.BindPFlag(
|
||||
"server.static_dir",
|
||||
serverCmd.PersistentFlags().Lookup("static-dir"))
|
||||
|
||||
// login.username
|
||||
serverCmd.PersistentFlags().String(
|
||||
"login-username", "bottin",
|
||||
"The username to login to the web ui. (config: 'login.username')")
|
||||
viper.BindPFlag(
|
||||
"login.username",
|
||||
serverCmd.PersistentFlags().Lookup("login-username"))
|
||||
|
||||
// login.password
|
||||
serverCmd.PersistentFlags().String(
|
||||
"login-password", "bottin",
|
||||
"The password to login to the web ui. (config: 'login.password')")
|
||||
viper.BindPFlag(
|
||||
"login.password",
|
||||
serverCmd.PersistentFlags().Lookup("login-password"))
|
||||
|
||||
// import.insert-batch-size
|
||||
serverCmd.PersistentFlags().Int(
|
||||
"insert-batch-size", 500,
|
||||
"The amount of inserts to do per batch (config: 'import.insert_batch_size')")
|
||||
viper.BindPFlag(
|
||||
"import.insert_batch_size",
|
||||
serverCmd.PersistentFlags().Lookup("insert-batch-size"))
|
||||
|
||||
// json-insert-path
|
||||
serverCmd.PersistentFlags().StringVar(
|
||||
&json_insert_path, "json-insert-path", "",
|
||||
"The location of a json file containing Membres to insert.")
|
||||
/*
|
||||
// Not using viper for json-insert-path since it would make it too easy
|
||||
// to forget to remove from the config every time, which heavily risks
|
||||
// doubling values.
|
||||
//
|
||||
// It would at least need to check for doubles before importing the file,
|
||||
// for it to be a kind of automatic differential update of the database.
|
||||
|
||||
viper.BindPFlag(
|
||||
"import.json-insert-path",
|
||||
serverCmd.PersistentFlags().Lookup("json-insert-path"))
|
||||
*/
|
||||
|
||||
// db.retry-timer
|
||||
serverCmd.PersistentFlags().Int(
|
||||
"db-retry-timer", 2,
|
||||
"Time between failed database connection retries, in seconds.")
|
||||
viper.BindPFlag(
|
||||
"db.retry-timer",
|
||||
serverCmd.PersistentFlags().Lookup("db-retry-timer"))
|
||||
}
|
142
cmd/web.go
Normal file
142
cmd/web.go
Normal file
|
@ -0,0 +1,142 @@
|
|||
package cmd
|
||||
|
||||
import (
|
||||
"crypto/subtle"
|
||||
"embed"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"io"
|
||||
"log"
|
||||
|
||||
"git.agecem.com/agecem/bottin/v4/data"
|
||||
"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
|
||||
webApiProtocol string
|
||||
)
|
||||
|
||||
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) {
|
||||
webApiHost = viper.GetString("web.api.host")
|
||||
webApiKey = viper.GetString("web.api.key")
|
||||
webApiPort = viper.GetInt("web.api.port")
|
||||
webApiProtocol = viper.GetString("web.api.protocol")
|
||||
webPassword = viper.GetString("web.password")
|
||||
webPort = viper.GetInt("web.port")
|
||||
webUser = viper.GetString("web.user")
|
||||
|
||||
// Ping API server
|
||||
|
||||
apiClient := data.NewApiClient(webApiKey, webApiHost, webApiProtocol, webApiPort)
|
||||
|
||||
pingResult, err := apiClient.GetV4()
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Println(pingResult)
|
||||
|
||||
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)
|
||||
e.GET("/membre/", webhandlers.GetMembre)
|
||||
|
||||
// Execution
|
||||
|
||||
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.protocol
|
||||
webCmd.Flags().String(
|
||||
"web-api-protocol", "http",
|
||||
"Remote API server protocol (config:'web.api.protocol')")
|
||||
viper.BindPFlag("web.api.protocol", webCmd.Flags().Lookup("web-api-protocol"))
|
||||
|
||||
// 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"))
|
||||
}
|
Reference in a new issue