added adding and editing of availabilities
This commit is contained in:
@@ -50,10 +50,10 @@ func (config ConfigStruct) SignJWT(val any) (string, error) {
|
||||
return t.SignedString([]byte(config.ClientSession.JwtSignature))
|
||||
}
|
||||
|
||||
func loadConfig() ConfigStruct {
|
||||
func LoadConfig() ConfigStruct {
|
||||
Config := ConfigYaml{}
|
||||
|
||||
yamlFile, err := os.ReadFile("config.yaml")
|
||||
yamlFile, err := os.ReadFile(CONFIG_PATH)
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("Error opening config-file: %q", err))
|
||||
}
|
||||
@@ -91,5 +91,5 @@ func loadConfig() ConfigStruct {
|
||||
}
|
||||
|
||||
func init() {
|
||||
Config = loadConfig()
|
||||
Config = LoadConfig()
|
||||
}
|
||||
|
||||
@@ -13,13 +13,8 @@ var CONFIG_PATH = "config.yaml"
|
||||
|
||||
type ConfigYaml struct {
|
||||
LogLevel string `yaml:"log_level"`
|
||||
Database struct {
|
||||
Host string `yaml:"host"`
|
||||
User string `yaml:"user"`
|
||||
Password string `yaml:"password"`
|
||||
Database string `yaml:"database"`
|
||||
} `yaml:"database"`
|
||||
Server struct {
|
||||
Database string `yaml:"database"`
|
||||
Server struct {
|
||||
Port int `yaml:"port"`
|
||||
} `yaml:"server"`
|
||||
ClientSession struct {
|
||||
|
||||
@@ -1,59 +1,58 @@
|
||||
package availabilities
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
cache "github.com/jfarleyx/go-simple-cache"
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db"
|
||||
)
|
||||
|
||||
type availabilitiesDB struct {
|
||||
Id int `db:"id"`
|
||||
Text string `db:"text"`
|
||||
Disabled bool `db:"disabled"`
|
||||
type AvailabilityDB struct {
|
||||
Id int `db:"id" json:"id" validate:"required"`
|
||||
Availability
|
||||
}
|
||||
|
||||
type Availability struct {
|
||||
Text string
|
||||
Disabled bool
|
||||
Text string `db:"text" json:"text" validate:"required"`
|
||||
Enabled bool `db:"enabled" json:"enabled" validate:"required"`
|
||||
Color string `db:"color" json:"color" validate:"required"`
|
||||
}
|
||||
|
||||
var c *cache.Cache
|
||||
func Add(a Availability) error {
|
||||
_, err := db.DB.NamedExec("INSERT INTO AVAILABILITIES (text, color, enabled) VALUES (:text, :color, :enabled)", a)
|
||||
|
||||
func Keys() (map[int]Availability, error) {
|
||||
if availabilities, hit := c.Get("availabilities"); !hit {
|
||||
refresh()
|
||||
return err
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("availabilities not stored cached")
|
||||
func Update(a AvailabilityDB) error {
|
||||
_, err := db.DB.NamedExec("UPDATE AVAILABILITIES SET text = :text, color = :color, enabled = :enabled WHERE id = :id", a)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func Slice() ([]AvailabilityDB, error) {
|
||||
// get the availabilitiesRaw from the database
|
||||
var availabilitiesRaw []AvailabilityDB
|
||||
|
||||
if err := db.DB.Select(&availabilitiesRaw, "SELECT * FROM AVAILABILITIES"); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
return availabilities.(map[int]Availability), nil
|
||||
return availabilitiesRaw, nil
|
||||
}
|
||||
}
|
||||
|
||||
func refresh() {
|
||||
// get the availabilitiesRaw from the database
|
||||
var availabilitiesRaw []availabilitiesDB
|
||||
|
||||
if err := db.DB.Select(&availabilitiesRaw, "SELECT * FROM AVAILABILITIES"); err == nil {
|
||||
func Keys() (map[int]Availability, error) {
|
||||
if availabilitiesRaw, err := Slice(); err != nil {
|
||||
return nil, err
|
||||
} else {
|
||||
// convert the result in a map
|
||||
availabilities := map[int]Availability{}
|
||||
|
||||
for _, a := range availabilitiesRaw {
|
||||
availabilities[a.Id] = Availability{
|
||||
Text: a.Text,
|
||||
Disabled: a.Disabled,
|
||||
Text: a.Text,
|
||||
Enabled: a.Enabled,
|
||||
Color: a.Color,
|
||||
}
|
||||
}
|
||||
|
||||
c.Set("availabilities", availabilities)
|
||||
return availabilities, nil
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
c = cache.New(24 * time.Hour)
|
||||
|
||||
c.OnExpired(refresh)
|
||||
|
||||
refresh()
|
||||
}
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"time"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/go-sql-driver/mysql"
|
||||
"github.com/jmoiron/sqlx"
|
||||
_config "github.com/johannesbuehl/golunteer/backend/pkg/config"
|
||||
_ "modernc.org/sqlite" // SQLite driver
|
||||
)
|
||||
|
||||
var config = _config.Config
|
||||
@@ -14,20 +16,31 @@ var config = _config.Config
|
||||
var DB *sqlx.DB
|
||||
|
||||
func init() {
|
||||
// setup the database-connection
|
||||
sqlConfig := mysql.Config{
|
||||
AllowNativePasswords: true,
|
||||
Net: "tcp",
|
||||
User: config.Database.User,
|
||||
Passwd: config.Database.Password,
|
||||
Addr: config.Database.Host,
|
||||
DBName: config.Database.Database,
|
||||
}
|
||||
|
||||
// connect to the database
|
||||
DB = sqlx.MustOpen("mysql", sqlConfig.FormatDSN())
|
||||
DB.SetMaxIdleConns(10)
|
||||
DB.SetMaxIdleConns(100)
|
||||
DB.SetConnMaxLifetime(time.Minute)
|
||||
DB = sqlx.MustOpen("sqlite", config.Database)
|
||||
|
||||
// create the tables if they don't exist
|
||||
if dbSetupInstructions, err := os.ReadFile("setup.sql"); err != nil {
|
||||
panic("can't read database-setup")
|
||||
} else {
|
||||
DB.MustExec(string(dbSetupInstructions))
|
||||
|
||||
// take wether the admin-user is present as an indicator to a new instance
|
||||
var admin struct {
|
||||
Admin bool `db:"admin"`
|
||||
}
|
||||
if err := DB.QueryRowx("SELECT admin FROM USERS WHERE name = 'admin'").StructScan(&admin); err != nil {
|
||||
// if the error isn't because there was no result, it's a real one
|
||||
if err != sql.ErrNoRows {
|
||||
panic(fmt.Errorf("can't query for the admin-user: %v", err))
|
||||
} else {
|
||||
// setup everything
|
||||
setup()
|
||||
fmt.Println("setup completed")
|
||||
|
||||
// reload the config
|
||||
_config.LoadConfig()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
80
backend/pkg/db/setup.go
Normal file
80
backend/pkg/db/setup.go
Normal file
@@ -0,0 +1,80 @@
|
||||
package db
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"math/rand/v2"
|
||||
"os"
|
||||
|
||||
"github.com/google/uuid"
|
||||
_config "github.com/johannesbuehl/golunteer/backend/pkg/config"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func createPassword(l int) string {
|
||||
passwordChars := [...]string{`A`, `B`, `C`, `D`, `E`, `F`, `G`, `H`, `I`, `J`, `K`, `L`, `M`, `N`, `O`, `P`, `Q`, `R`, `S`, `T`, `U`, `V`, `W`, `X`, `Y`, `Z`, `Ä`, `Ö`, `Ü`, `a`, `b`, `c`, `d`, `e`, `f`, `g`, `h`, `i`, `j`, `k`, `l`, `m`, `n`, `o`, `p`, `q`, `r`, `s`, `t`, `u`, `v`, `w`, `x`, `y`, `z`, `ä`, `ö`, `ü`, `ß`, `0`, `1`, `2`, `3`, `4`, `5`, `6`, `7`, `8`, `9`, `!`, `"`, `§`, `$`, `%`, `&`, `/`, `(`, `)`, `=`, `?`, `@`, `{`, `}`, `[`, `]`, `#`, `+`, `'`, `*`, `,`, `.`, `-`, `;`, `:`, `_`, `<`, `>`, `|`, `°`}
|
||||
var password string
|
||||
|
||||
for ii := 0; ii < l; ii++ {
|
||||
password += passwordChars[rand.IntN(len(passwordChars))]
|
||||
}
|
||||
|
||||
return password
|
||||
}
|
||||
|
||||
func setup() {
|
||||
fmt.Println("Creating admin-password:")
|
||||
|
||||
// create an admin-password
|
||||
const passwordLength = 20
|
||||
password := createPassword(passwordLength)
|
||||
|
||||
// hash the admin-password
|
||||
if passwordHash, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost); err != nil {
|
||||
panic(fmt.Errorf("can't generate password: %v", err))
|
||||
} else {
|
||||
fmt.Println("\thashed password")
|
||||
|
||||
// create an admin-user
|
||||
user := struct {
|
||||
Name string `db:"name"`
|
||||
Password []byte `db:"password"`
|
||||
Admin bool `db:"admin"`
|
||||
TokenID string `db:"tokenID"`
|
||||
}{
|
||||
Name: "admin",
|
||||
Password: passwordHash,
|
||||
Admin: true,
|
||||
TokenID: uuid.NewString(),
|
||||
}
|
||||
if _, err := DB.NamedExec("INSERT INTO USERS (name, password, tokenID, admin) VALUES (:name, :password, :tokenID, :admin)", &user); err != nil {
|
||||
panic(fmt.Errorf("can't insert admin-user into the database: %v", err))
|
||||
}
|
||||
|
||||
fmt.Println("\twrote hashed password to database")
|
||||
}
|
||||
|
||||
fmt.Printf("created user \"admin\" with password %s\n", password)
|
||||
|
||||
// create a jwt-signature
|
||||
config.ClientSession.JwtSignature = createPassword(100)
|
||||
|
||||
// write the modified config-file
|
||||
WriteConfig()
|
||||
|
||||
}
|
||||
|
||||
func WriteConfig() {
|
||||
buf := bytes.Buffer{}
|
||||
enc := yaml.NewEncoder(&buf)
|
||||
enc.SetIndent(2)
|
||||
// Can set default indent here on the encoder
|
||||
if err := enc.Encode(&config.ConfigYaml); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
if err := os.WriteFile(_config.CONFIG_PATH, buf.Bytes(), 0644); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,14 +9,14 @@ import (
|
||||
)
|
||||
|
||||
type tasksDB struct {
|
||||
ID int `db:"id"`
|
||||
Text string `db:"text"`
|
||||
Disabled bool `db:"disabled"`
|
||||
ID int `db:"id"`
|
||||
Text string `db:"text"`
|
||||
Enabled bool `db:"enabled"`
|
||||
}
|
||||
|
||||
type Task struct {
|
||||
Text string `json:"text"`
|
||||
Disabled bool `json:"disabled"`
|
||||
Text string `json:"text"`
|
||||
Enabled bool `json:"enabled"`
|
||||
}
|
||||
|
||||
var c *cache.Cache
|
||||
@@ -41,8 +41,8 @@ func refresh() {
|
||||
|
||||
for _, a := range tasksRaw {
|
||||
tasks[a.ID] = Task{
|
||||
Text: a.Text,
|
||||
Disabled: a.Disabled,
|
||||
Text: a.Text,
|
||||
Enabled: a.Enabled,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,7 +37,11 @@ func init() {
|
||||
Out: os.Stdout,
|
||||
TimeFormat: time.DateTime,
|
||||
FormatLevel: func(i interface{}) string {
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
if i == nil {
|
||||
return "| LOG |"
|
||||
} else {
|
||||
return strings.ToUpper(fmt.Sprintf("| %-6s|", i))
|
||||
}
|
||||
},
|
||||
FormatFieldName: func(i interface{}) string {
|
||||
return fmt.Sprintf("%s", i)
|
||||
|
||||
107
backend/pkg/router/availabilities.go
Normal file
107
backend/pkg/router/availabilities.go
Normal file
@@ -0,0 +1,107 @@
|
||||
package router
|
||||
|
||||
import (
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/johannesbuehl/golunteer/backend/pkg/db/availabilities"
|
||||
)
|
||||
|
||||
func getAvailabilities(args HandlerArgs) responseMessage {
|
||||
response := responseMessage{}
|
||||
|
||||
// get all the availabilites from the database
|
||||
if avails, err := availabilities.Slice(); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Error().Msgf("can't get availabilites: %v", err)
|
||||
|
||||
return response
|
||||
} else {
|
||||
response.Data = struct {
|
||||
Availabilities []availabilities.AvailabilityDB `json:"availabilities"`
|
||||
}{Availabilities: avails}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
|
||||
func postAvailabilitie(args HandlerArgs) responseMessage {
|
||||
response := responseMessage{}
|
||||
|
||||
// check admin
|
||||
if !args.User.Admin {
|
||||
response.Status = fiber.StatusUnauthorized
|
||||
|
||||
logger.Warn().Msg("user is no admin")
|
||||
|
||||
return response
|
||||
|
||||
// parse the body
|
||||
} else {
|
||||
var body availabilities.Availability
|
||||
|
||||
if err := args.C.BodyParser(&body); err != nil {
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
|
||||
return response
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&response); err != nil {
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
|
||||
return response
|
||||
} else if err := availabilities.Add(body); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Error().Msgf("can't add availability: %v", err)
|
||||
|
||||
return response
|
||||
} else {
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func patchAvailabilities(args HandlerArgs) responseMessage {
|
||||
response := responseMessage{}
|
||||
|
||||
// check admin
|
||||
if !args.User.Admin {
|
||||
response.Status = fiber.StatusUnauthorized
|
||||
|
||||
logger.Warn().Msg("user is no admin")
|
||||
|
||||
return response
|
||||
|
||||
// parse the body
|
||||
} else {
|
||||
var body availabilities.AvailabilityDB
|
||||
|
||||
if err := args.C.BodyParser(&body); err != nil {
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("can't parse body: %v", err)
|
||||
|
||||
return response
|
||||
|
||||
// validate the body
|
||||
} else if err := validate.Struct(&response); err != nil {
|
||||
response.Status = fiber.StatusBadRequest
|
||||
|
||||
logger.Log().Msgf("invalid body: %v", err)
|
||||
|
||||
return response
|
||||
} else if err := availabilities.Update(body); err != nil {
|
||||
response.Status = fiber.StatusInternalServerError
|
||||
|
||||
logger.Error().Msgf("can't update availability: %v", err)
|
||||
|
||||
return response
|
||||
} else {
|
||||
return response
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -81,14 +81,17 @@ func init() {
|
||||
"events/user/pending": getEventsUserPending,
|
||||
"tasks": getTasks,
|
||||
"users": getUsers,
|
||||
"availabilities": getAvailabilities,
|
||||
},
|
||||
"POST": {
|
||||
"events": postEvent,
|
||||
"users": postUser,
|
||||
"events": postEvent,
|
||||
"users": postUser,
|
||||
"availabilities": postAvailabilitie,
|
||||
},
|
||||
"PATCH": {
|
||||
"users": patchUser,
|
||||
"events": patchEvent,
|
||||
"users": patchUser,
|
||||
"events": patchEvent,
|
||||
"availabilities": patchAvailabilities,
|
||||
},
|
||||
"PUT": {
|
||||
"users/password": putPassword,
|
||||
|
||||
Reference in New Issue
Block a user