From 3f9a19079507863d2ff728105290dbe7999671d5 Mon Sep 17 00:00:00 2001 From: z1glr Date: Tue, 4 Feb 2025 11:46:56 +0100 Subject: [PATCH] first docker container version --- .dockerignore | 12 +++ Dockerfile | 47 ++++++++++ backend/pkg/db/db.go | 4 +- backend/pkg/db/setup.go | 7 ++ backend/setup.sql | 56 +----------- backend/tables.sql | 55 ++++++++++++ setup/.devcontainer/devcontainer.json | 22 ----- setup/.gitignore | 2 - setup/go.mod | 15 ---- setup/go.sum | 15 ---- setup/pkg/config/config.go | 1 - setup/setup.go | 124 -------------------------- setup/setup.sql | 46 ---------- 13 files changed, 125 insertions(+), 281 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile create mode 100644 backend/tables.sql delete mode 100644 setup/.devcontainer/devcontainer.json delete mode 100644 setup/.gitignore delete mode 100644 setup/go.mod delete mode 100644 setup/go.sum delete mode 120000 setup/pkg/config/config.go delete mode 100644 setup/setup.go delete mode 100644 setup/setup.sql diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..fb58393 --- /dev/null +++ b/.dockerignore @@ -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 \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..a6cae21 --- /dev/null +++ b/Dockerfile @@ -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"] \ No newline at end of file diff --git a/backend/pkg/db/db.go b/backend/pkg/db/db.go index 52b07b8..35875db 100644 --- a/backend/pkg/db/db.go +++ b/backend/pkg/db/db.go @@ -22,8 +22,8 @@ func init() { DB.MustExec("PRAGMA foreign_keys = ON") // create the tables if they don't exist - if dbSetupInstructions, err := os.ReadFile("setup.sql"); err != nil { - panic("can't read database-setup") + if dbSetupInstructions, err := os.ReadFile("tables.sql"); err != nil { + panic("can't read database-tables-setup") } else { DB.MustExec(string(dbSetupInstructions)) diff --git a/backend/pkg/db/setup.go b/backend/pkg/db/setup.go index c6bc7e5..afbe621 100644 --- a/backend/pkg/db/setup.go +++ b/backend/pkg/db/setup.go @@ -60,6 +60,13 @@ func setup() { // create a jwt-signature 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 WriteConfig() diff --git a/backend/setup.sql b/backend/setup.sql index 6fbcb0b..ea144b9 100644 --- a/backend/setup.sql +++ b/backend/setup.sql @@ -1,55 +1,3 @@ -CREATE TABLE IF NOT EXISTS TASKS ( - taskID INTEGER PRIMARY KEY, - taskName varchar(64) NOT NULL, - enabled BOOL DEFAULT 1 -); +INSERT INTO TASKS (taskName) VALUES ("Audio"), ("Video"); -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 -); +INSERT INTO AVAILABILITIES (availabilityName, color) VALUES ("Yes", "Green"), ("No", "Red"); \ No newline at end of file diff --git a/backend/tables.sql b/backend/tables.sql new file mode 100644 index 0000000..6fbcb0b --- /dev/null +++ b/backend/tables.sql @@ -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 +); diff --git a/setup/.devcontainer/devcontainer.json b/setup/.devcontainer/devcontainer.json deleted file mode 100644 index 3495528..0000000 --- a/setup/.devcontainer/devcontainer.json +++ /dev/null @@ -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" -} diff --git a/setup/.gitignore b/setup/.gitignore deleted file mode 100644 index 680c8c9..0000000 --- a/setup/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -config.yaml -passwords \ No newline at end of file diff --git a/setup/go.mod b/setup/go.mod deleted file mode 100644 index 3463356..0000000 --- a/setup/go.mod +++ /dev/null @@ -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 -) diff --git a/setup/go.sum b/setup/go.sum deleted file mode 100644 index 3822966..0000000 --- a/setup/go.sum +++ /dev/null @@ -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= diff --git a/setup/pkg/config/config.go b/setup/pkg/config/config.go deleted file mode 120000 index da509ee..0000000 --- a/setup/pkg/config/config.go +++ /dev/null @@ -1 +0,0 @@ -../../../backend/pkg/config/configYAML.go \ No newline at end of file diff --git a/setup/setup.go b/setup/setup.go deleted file mode 100644 index d36340e..0000000 --- a/setup/setup.go +++ /dev/null @@ -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() -} diff --git a/setup/setup.sql b/setup/setup.sql deleted file mode 100644 index 55474b7..0000000 --- a/setup/setup.sql +++ /dev/null @@ -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 -); \ No newline at end of file