From 02b494a22134febb0342114389908c992385302a Mon Sep 17 00:00:00 2001 From: "Maurice L." Date: Fri, 20 Feb 2026 23:28:42 +0100 Subject: [PATCH] Initial commit --- .gitignore | 0 .idea/.gitignore | 8 +++++ .idea/dataSources.xml | 12 ++++++++ .idea/discord.xml | 7 +++++ .idea/modules.xml | 8 +++++ .idea/shap-planner-backend.iml | 9 ++++++ .idea/sqldialects.xml | 7 +++++ .idea/vcs.xml | 6 ++++ database.db | 0 go.mod | 9 ++++++ go.sum | 6 ++++ handlers/account.go | 50 +++++++++++++++++++++++++++++++ main.go | 21 +++++++++++++ models/models.go | 16 ++++++++++ storage/storage.go | 55 ++++++++++++++++++++++++++++++++++ utils/util.go | 20 +++++++++++++ 16 files changed, 234 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/dataSources.xml create mode 100644 .idea/discord.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/shap-planner-backend.iml create mode 100644 .idea/sqldialects.xml create mode 100644 .idea/vcs.xml create mode 100644 database.db create mode 100644 go.mod create mode 100644 go.sum create mode 100644 handlers/account.go create mode 100644 main.go create mode 100644 models/models.go create mode 100644 storage/storage.go create mode 100644 utils/util.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/dataSources.xml b/.idea/dataSources.xml new file mode 100644 index 0000000..e946ea9 --- /dev/null +++ b/.idea/dataSources.xml @@ -0,0 +1,12 @@ + + + + + sqlite.xerial + true + org.sqlite.JDBC + jdbc:sqlite:$PROJECT_DIR$/database.db + $ProjectFileDir$ + + + \ No newline at end of file diff --git a/.idea/discord.xml b/.idea/discord.xml new file mode 100644 index 0000000..d8e9561 --- /dev/null +++ b/.idea/discord.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..b2cfdce --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/shap-planner-backend.iml b/.idea/shap-planner-backend.iml new file mode 100644 index 0000000..5e764c4 --- /dev/null +++ b/.idea/shap-planner-backend.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/sqldialects.xml b/.idea/sqldialects.xml new file mode 100644 index 0000000..efa7019 --- /dev/null +++ b/.idea/sqldialects.xml @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/database.db b/database.db new file mode 100644 index 0000000..e69de29 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..1917f1d --- /dev/null +++ b/go.mod @@ -0,0 +1,9 @@ +module shap-planner-backend + +go 1.26.0 + +require ( + github.com/google/uuid v1.6.0 + github.com/mattn/go-sqlite3 v1.14.34 + golang.org/x/crypto v0.48.0 +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..7da030b --- /dev/null +++ b/go.sum @@ -0,0 +1,6 @@ +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/mattn/go-sqlite3 v1.14.34 h1:3NtcvcUnFBPsuRcno8pUtupspG/GM+9nZ88zgJcp6Zk= +github.com/mattn/go-sqlite3 v1.14.34/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +golang.org/x/crypto v0.48.0 h1:/VRzVqiRSggnhY7gNRxPauEQ5Drw9haKdM0jqfcCFts= +golang.org/x/crypto v0.48.0/go.mod h1:r0kV5h3qnFPlQnBSrULhlsRfryS2pmewsg+XfMgkVos= diff --git a/handlers/account.go b/handlers/account.go new file mode 100644 index 0000000..0b71af9 --- /dev/null +++ b/handlers/account.go @@ -0,0 +1,50 @@ +package handlers + +import ( + "encoding/json" + "net/http" + "shap-planner-backend/models" + "shap-planner-backend/storage" + "shap-planner-backend/utils" +) + +func Register(w http.ResponseWriter, r *http.Request) { + var user models.User + _ = json.NewDecoder(r.Body).Decode(&user) + hashed, _ := utils.HashPassword(user.Password) + user.Password = hashed + user.ID = utils.GenerateUUID() + + err := storage.AddUser(user) + if err != nil { + http.Error(w, "User exists", http.StatusBadRequest) + return + } + w.WriteHeader(http.StatusCreated) +} + +func Login(w http.ResponseWriter, r *http.Request) { + var creds struct { + Username string `json:"username"` + Password string `json:"password"` + } + _ = json.NewDecoder(r.Body).Decode(&creds) + + user, err := storage.GetUserByUsername(creds.Username) + if err != nil { + http.Error(w, "User not found", http.StatusUnauthorized) + return + } + + if !utils.CheckPasswordHash(creds.Password, user.Password) { + http.Error(w, "Wrong password", http.StatusUnauthorized) + return + } + + // TODO: JWT oder Session-Token erzeugen + w.WriteHeader(http.StatusOK) + err = json.NewEncoder(w).Encode(user) + if err != nil { + return + } +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..0d41a63 --- /dev/null +++ b/main.go @@ -0,0 +1,21 @@ +package shap_planner_backend + +import ( + "log" + "net/http" + "shap-planner-backend/handlers" + "shap-planner-backend/storage" +) + +func main() { + err := storage.InitDB("database.db") + if err != nil { + log.Fatal(err) + } + + http.HandleFunc("/register", handlers.Register) + http.HandleFunc("/login", handlers.Login) + + log.Println("Server läuft auf :8080") + log.Fatal(http.ListenAndServe(":8080", nil)) +} diff --git a/models/models.go b/models/models.go new file mode 100644 index 0000000..d334943 --- /dev/null +++ b/models/models.go @@ -0,0 +1,16 @@ +package models + +type User struct { + ID string `json:"id"` + Username string `json:"username"` + Password string `json:"password"` +} + +type Expense struct { + ID string `json:"id"` + Amount int `json:"amt"` + Description string `json:"desc"` + + Payer User `json:"payer"` + Debtors []User `json:"debtors"` +} diff --git a/storage/storage.go b/storage/storage.go new file mode 100644 index 0000000..2f2a509 --- /dev/null +++ b/storage/storage.go @@ -0,0 +1,55 @@ +package storage + +import ( + "database/sql" + _ "database/sql" + _ "github.com/mattn/go-sqlite3" + "shap-planner-backend/models" + _ "shap-planner-backend/models" +) + +var DB *sql.DB + +func InitDB(filepath string) error { + var err error + DB, err = sql.Open("sqlite3", filepath) + if err != nil { + return err + } + + //Create Users-Table + _, err = DB.Exec(`CREATE TABLE IF NOT EXISTS users( + id TEXT PRIMARY KEY, + username TEXT UNIQUE, + password TEXT + );`) + if err != nil { + return err + } + + //Create Expenses-Table + _, err = DB.Exec(`CREATE TABLE IF NOT EXISTS expenses( + id TEXT PRIMARY KEY + + )`) + return err +} + +func AddUser(user models.User) error { + _, err := DB.Exec("INSERT INTO users(id, username, password) VALUES (?, ?, ?)", user.ID, user.Username, user.Password) + return err +} + +func GetUserByUsername(username string) (models.User, error) { + row := DB.QueryRow("SELECT * FROM users WHERE username = ?", username) + var user models.User + err := row.Scan(&user.ID, &user.Username, &user.Password) + return user, err +} + +func GetUserById(id string) (models.User, error) { + row := DB.QueryRow("SELECT * FROM users WHERE id = ?", id) + var user models.User + err := row.Scan(&user.ID, &user.Username, &user.Password) + return user, err +} diff --git a/utils/util.go b/utils/util.go new file mode 100644 index 0000000..2b8083f --- /dev/null +++ b/utils/util.go @@ -0,0 +1,20 @@ +package utils + +import ( + "github.com/google/uuid" + "golang.org/x/crypto/bcrypt" +) + +func HashPassword(password string) (string, error) { + bytes, err := bcrypt.GenerateFromPassword([]byte(password), 14) + return string(bytes), err +} + +func CheckPasswordHash(password, hash string) bool { + err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password)) + return err == nil +} + +func GenerateUUID() string { + return uuid.New().String() +}