first docker container version

This commit is contained in:
z1glr
2025-02-04 11:46:56 +01:00
parent da06038263
commit 3f9a190795
13 changed files with 125 additions and 281 deletions

12
.dockerignore Normal file
View File

@@ -0,0 +1,12 @@
.git
backend/.devcontainer
backend/.vscode
backend/.gitignore
backend/logs
backend/config.yaml
client/node_modules
client/.gitignore
client/out
client/.next

47
Dockerfile Normal file
View File

@@ -0,0 +1,47 @@
# build the backend
FROM golang:1.23-alpine AS backend-builder
# set the workdir
WORKDIR /usr/src/backend
# install the dependencies
COPY backend/go.mod backend/go.sum ./
RUN go mod download && go mod verify
# build the source-code
COPY backend .
RUN go build -ldflags "-s -w" -o dist/backend
# build the frontend
FROM node:current AS client-builder
WORKDIR /usr/src/client
COPY client/package*.json ./
RUN npm ci
COPY client .
RUN npm run build
FROM alpine:latest
WORKDIR /usr/bin/app
# copy the backend
COPY --from=backend-builder /usr/src/backend/dist/backend backend
# copy the client-html
COPY --from=client-builder /usr/src/client/out html
# Create a group and user
RUN addgroup -S golunteer && adduser -S golunteer -G golunteer
EXPOSE 61016
EXPOSE 80
# Tell docker that all future commands should run as the appuser user
USER golunteer
# run the app
CMD ["app"]

View File

@@ -22,8 +22,8 @@ func init() {
DB.MustExec("PRAGMA foreign_keys = ON") DB.MustExec("PRAGMA foreign_keys = ON")
// create the tables if they don't exist // create the tables if they don't exist
if dbSetupInstructions, err := os.ReadFile("setup.sql"); err != nil { if dbSetupInstructions, err := os.ReadFile("tables.sql"); err != nil {
panic("can't read database-setup") panic("can't read database-tables-setup")
} else { } else {
DB.MustExec(string(dbSetupInstructions)) DB.MustExec(string(dbSetupInstructions))

View File

@@ -60,6 +60,13 @@ func setup() {
// create a jwt-signature // create a jwt-signature
config.ClientSession.JwtSignature = createPassword(100) config.ClientSession.JwtSignature = createPassword(100)
// setp the database
if dbSetupInstructions, err := os.ReadFile("setup.sql"); err != nil {
panic("can't read database-setup")
} else {
DB.MustExec(string(dbSetupInstructions))
}
// write the modified config-file // write the modified config-file
WriteConfig() WriteConfig()

View File

@@ -1,55 +1,3 @@
CREATE TABLE IF NOT EXISTS TASKS ( INSERT INTO TASKS (taskName) VALUES ("Audio"), ("Video");
taskID INTEGER PRIMARY KEY,
taskName varchar(64) NOT NULL,
enabled BOOL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS AVAILABILITIES ( INSERT INTO AVAILABILITIES (availabilityName, color) VALUES ("Yes", "Green"), ("No", "Red");
availabilityID INTEGER PRIMARY KEY,
availabilityName varchar(32) NOT NULL,
color varchar(7) NOT NULL,
enabled BOOL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS USERS (
userName varchar(64) PRIMARY KEY,
password BLOB NOT NULL,
admin BOOL NOT NULL DEFAULT(false),
tokenID varchar(36) NOT NULL,
CHECK (length(password) = 60),
CHECK (length(tokenID) = 36)
);
CREATE TABLE IF NOT EXISTS USER_TASKS (
userName varchar(64),
taskID INTEGER,
PRIMARY KEY (userName, taskID),
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (taskID) REFERENCES TASKS(taskID) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS EVENTS (
eventID INTEGER PRIMARY KEY,
date DATETIME NOT NULL,
description TEXT DEFAULT ""
);
CREATE TABLE IF NOT EXISTS USER_AVAILABILITIES (
userName varchar(64) NOT NULL,
eventID INTEGER NOT NULL,
availabilityID INTEGER NOT NULL,
PRIMARY KEY (userName, eventID),
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (eventID) REFERENCES EVENTS(eventID) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (availabilityID) REFERENCES AVAILABILITIES(availabilityID) ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS USER_ASSIGNMENTS (
eventID INTEGER NOT NULL,
taskID INTEGER NOT NULL,
userName varchar(64),
PRIMARY KEY (eventID, taskID),
FOREIGN KEY (eventID) REFERENCES EVENTS(eventID) ON DELETE CASCADE,
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (taskID) REFERENCES TASKS(taskID) ON UPDATE CASCADE
);

55
backend/tables.sql Normal file
View File

@@ -0,0 +1,55 @@
CREATE TABLE IF NOT EXISTS TASKS (
taskID INTEGER PRIMARY KEY,
taskName varchar(64) NOT NULL,
enabled BOOL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS AVAILABILITIES (
availabilityID INTEGER PRIMARY KEY,
availabilityName varchar(32) NOT NULL,
color varchar(7) NOT NULL,
enabled BOOL DEFAULT 1
);
CREATE TABLE IF NOT EXISTS USERS (
userName varchar(64) PRIMARY KEY,
password BLOB NOT NULL,
admin BOOL NOT NULL DEFAULT(false),
tokenID varchar(36) NOT NULL,
CHECK (length(password) = 60),
CHECK (length(tokenID) = 36)
);
CREATE TABLE IF NOT EXISTS USER_TASKS (
userName varchar(64),
taskID INTEGER,
PRIMARY KEY (userName, taskID),
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (taskID) REFERENCES TASKS(taskID) ON DELETE CASCADE ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS EVENTS (
eventID INTEGER PRIMARY KEY,
date DATETIME NOT NULL,
description TEXT DEFAULT ""
);
CREATE TABLE IF NOT EXISTS USER_AVAILABILITIES (
userName varchar(64) NOT NULL,
eventID INTEGER NOT NULL,
availabilityID INTEGER NOT NULL,
PRIMARY KEY (userName, eventID),
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (eventID) REFERENCES EVENTS(eventID) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (availabilityID) REFERENCES AVAILABILITIES(availabilityID) ON UPDATE CASCADE
);
CREATE TABLE IF NOT EXISTS USER_ASSIGNMENTS (
eventID INTEGER NOT NULL,
taskID INTEGER NOT NULL,
userName varchar(64),
PRIMARY KEY (eventID, taskID),
FOREIGN KEY (eventID) REFERENCES EVENTS(eventID) ON DELETE CASCADE,
FOREIGN KEY (userName) REFERENCES USERS(userName) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (taskID) REFERENCES TASKS(taskID) ON UPDATE CASCADE
);

View File

@@ -1,22 +0,0 @@
// 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"
}

2
setup/.gitignore vendored
View File

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

View File

@@ -1,15 +0,0 @@
module github.com/johannesbuehl/golunteer/setup
go 1.23.4
require (
github.com/go-sql-driver/mysql v1.8.1
gopkg.in/yaml.v3 v3.0.1
)
require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/google/uuid v1.6.0
github.com/jmoiron/sqlx v1.4.0
golang.org/x/crypto v0.32.0
)

View File

@@ -1,15 +0,0 @@
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o=
github.com/jmoiron/sqlx v1.4.0/go.mod h1:ZrZ7UsYB/weZdl2Bxg6jCRO9c3YHl8r3ahlKmRT4JLY=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1 +0,0 @@
../../../backend/pkg/config/configYAML.go

View File

@@ -1,124 +0,0 @@
package main
import (
"fmt"
"math/rand/v2"
"os"
"regexp"
"strings"
"github.com/google/uuid"
"github.com/go-sql-driver/mysql"
"github.com/jmoiron/sqlx"
_config "github.com/johannesbuehl/golunteer/setup/pkg/config"
"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() {
config := &_config.YamlConfig
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 := sqlx.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
tokenId := uuid.NewString()
if _, err := db.Exec("INSERT INTO USERS (name, password, tokenID) VALUES ('admin', ?, ?)", passwordHash, tokenId); 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
_config.WriteConfig()
}

View File

@@ -1,46 +0,0 @@
CREATE TABLE TASKS (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
text varchar(64) NOT NULL,
disabled BOOL DEFAULT(false)
);
CREATE TABLE AVAILABILITIES (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
text varchar(32) NOT NULL,
disabled BOOL DEFAULT(false)
);
CREATE TABLE USERS (
name varchar(64) PRIMARY KEY,
password binary(60) NOT NULL,
admin BOOL NOT NULL DEFAULT(false),
tokenID varchar(64) NOT NULL,
CHECK (CHAR_LENGTH(password) = 60),
CHECK (CHAR_LENGTH(tokenID) = 36)
);
CREATE TABLE EVENTS (
id INTEGER PRIMARY KEY AUTO_INCREMENT,
date DATETIME NOT NULL,
description TEXT DEFAULT("")
);
CREATE TABLE USER_AVAILABILITIES (
userName varchar(64) NOT NULL,
eventID INTEGER NOT NULL,
availabilityID INTEGER NOT NULL,
PRIMARY KEY (userName, eventID),
FOREIGN KEY (userName) REFERENCES USERS(name) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (eventID) REFERENCES EVENTS(id) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (availabilityID) REFERENCES AVAILABILITIES(id) ON UPDATE CASCADE
);
CREATE TABLE USER_ASSIGNMENTS (
eventID INTEGER NOT NULL,
taskID INTEGER NOT NULL,
userName varchar(64),
PRIMARY KEY (eventID, taskID),
FOREIGN KEY (eventID) REFERENCES EVENTS(id) ON DELETE CASCADE,
FOREIGN KEY (userName) REFERENCES USERS(name) ON DELETE CASCADE ON UPDATE CASCADE,
FOREIGN KEY (taskID) REFERENCES TASKS(id) ON UPDATE CASCADE
);