added adding and editing of availabilities

This commit is contained in:
z1glr
2025-01-18 13:25:12 +00:00
parent e37310b774
commit e17c9db90c
24 changed files with 737 additions and 163 deletions

View File

@@ -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()
}

View File

@@ -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
View 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)
}
}
}

View File

@@ -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,
}
}