created setup and backend structure

This commit is contained in:
z1glr
2025-01-07 00:51:03 +00:00
parent 063f22569d
commit 03b2b0e206
11 changed files with 306 additions and 1 deletions

View File

@@ -0,0 +1,22 @@
// For format details, see https://aka.ms/devcontainer.json. For config options, see the
// README at: https://github.com/devcontainers/templates/tree/main/src/go
{
"name": "Go",
// Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile
"image": "mcr.microsoft.com/devcontainers/go:1-1.23-bookworm"
// Features to add to the dev container. More info: https://containers.dev/features.
// "features": {},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// "forwardPorts": [],
// Use 'postCreateCommand' to run commands after the container is created.
// "postCreateCommand": "go version",
// Configure tool-specific properties.
// "customizations": {},
// Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root.
// "remoteUser": "root"
}

1
backend/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
config.yaml

72
backend/config.go Normal file
View File

@@ -0,0 +1,72 @@
package main
import (
"bytes"
"fmt"
"os"
"time"
"gopkg.in/yaml.v3"
)
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"`
ClientSession struct {
JwtSignature string `yaml:"jwt_signature"`
Expire string `yaml:"expire"`
} `yaml:"client_session"`
}
type CacheConfig struct {
Expiration time.Duration
Purge time.Duration
}
var config ConfigYaml
func loadConfig() ConfigYaml {
config := ConfigYaml{}
yamlFile, err := os.ReadFile(CONFIG_PATH)
if err != nil {
panic(fmt.Sprintf("Error opening config-file: %v", err))
}
reader := bytes.NewReader(yamlFile)
dec := yaml.NewDecoder(reader)
dec.KnownFields(true)
err = dec.Decode(&config)
if err != nil {
fmt.Fprintf(os.Stderr, "Error parsing config-file: %v", err)
os.Exit(1)
}
return config
}
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); err != nil {
panic(err)
} else {
if err := os.WriteFile(CONFIG_PATH, buf.Bytes(), 0644); err != nil {
panic(err)
}
}
}
func init() {
config = loadConfig()
}

3
backend/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module github.com/johannesbuehl/golunteer/backend
go 1.23.4

118
backend/setup.go Normal file
View File

@@ -0,0 +1,118 @@
package main
import (
"database/sql"
"fmt"
"math/rand/v2"
"os"
"regexp"
"strings"
"github.com/go-sql-driver/mysql"
"golang.org/x/crypto/bcrypt"
)
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 exit(e error) {
fmt.Printf("%v\n", e)
os.Exit(1)
}
func main() {
fmt.Println("connecting to database")
// connect to the database
sqlConfig := mysql.Config{
AllowNativePasswords: true,
Net: "tcp",
User: config.Database.User,
Passwd: config.Database.Password,
Addr: config.Database.Host,
DBName: config.Database.Database,
}
db, err := sql.Open("mysql", sqlConfig.FormatDSN())
if err != nil {
exit(err)
}
// load the sql-script
fmt.Println(`reading "setup.sql"`)
var sqlScriptCommands []byte
if c, err := os.ReadFile("setup.sql"); err != nil {
exit(err)
} else {
sqlScriptCommands = c
}
// read the currently availabe tables
fmt.Println("reading available tables in database")
if rows, err := db.Query("SHOW TABLES"); err != nil {
exit(err)
} else {
defer rows.Close()
fmt.Println("checking for already existing tables in database")
for rows.Next() {
var name string
if err := rows.Scan(&name); err != nil {
exit(err)
} else {
// check wether for the table there exists a create command
if match, err := regexp.Match(fmt.Sprintf(`(?i)^create table %s`, name), sqlScriptCommands); err != nil {
exit(err)
} else {
if match {
exit(fmt.Errorf("can't setup databases: table %q already exists", name))
}
}
}
}
}
// everything is good (so far), create the tables
fmt.Println("Creating the individual tables:")
for _, cmd := range strings.Split(string(sqlScriptCommands), ";") {
db.Exec(cmd)
}
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 {
exit(err)
} else {
fmt.Println("\thashed password")
// create an admin-user
if _, err := db.Exec("INSERT INTO USERS (name, password) VALUES ('admin', ?)", passwordHash); err != nil {
exit(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()
}