code refactor

This commit is contained in:
2025-01-25 15:32:52 +01:00
parent 596dcc31b2
commit 57acac6dae
7 changed files with 193 additions and 4 deletions

View File

@@ -17,12 +17,12 @@ func InitDatabase() (*sql.DB, error) {
password := os.Getenv("PG_PASSWORD")
host := os.Getenv("PG_HOST")
connStr := fmt.Sprintf("user=postgres host=%s dbname=relay password=%s sslmode=disable", host, password)
db, err := sql.Open("postgres", connStr)
DB, err := sql.Open("postgres", connStr)
if err != nil {
log.Fatal(err)
}
return db, nil
return DB, nil
}
@@ -44,3 +44,32 @@ func GetUsers(db *sql.DB) ([]string, error) {
}
return users, err
}
func CheckUserExists(db *sql.DB, username string) (bool, error) {
query := `SELECT COUNT(1) FROM accounts WHERE username= $1`
var count int
err := db.QueryRow(query, username).Scan(&count)
if err != nil {
return false, fmt.Errorf("error checking username exists: %v", err)
}
return count > 0, err
}
func InsertUser(db *sql.DB, username string, passwordHash string) (string, error) {
query := `
INSERT INTO Accounts (username, password_hash)
VALUES ($1, $2)
RETURNING user_id;
`
var userId string
err := db.QueryRow(query, username, passwordHash).Scan(&userId)
if err != nil {
return "", fmt.Errorf("error inserting user: %v", err)
}
return userId, err
}

4
go.mod
View File

@@ -6,10 +6,12 @@ require (
github.com/gofiber/fiber/v2 v2.52.6
github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9
golang.org/x/crypto v0.32.0
)
require (
github.com/andybalholm/brotli v1.1.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.1 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/klauspost/compress v1.17.9 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
@@ -19,5 +21,5 @@ require (
github.com/valyala/bytebufferpool v1.0.0 // indirect
github.com/valyala/fasthttp v1.51.0 // indirect
github.com/valyala/tcplisten v1.0.0 // indirect
golang.org/x/sys v0.28.0 // indirect
golang.org/x/sys v0.29.0 // indirect
)

8
go.sum
View File

@@ -2,6 +2,8 @@ github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1
github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY=
github.com/gofiber/fiber/v2 v2.52.6 h1:Rfp+ILPiYSvvVuIPvxrBns+HJp8qGLDnLJawAu27XVI=
github.com/gofiber/fiber/v2 v2.52.6/go.mod h1:YEcBbO/FB+5M1IZNBP9FO3J9281zgPAreiI1oqg8nDw=
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
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/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
@@ -25,7 +27,13 @@ github.com/valyala/fasthttp v1.51.0 h1:8b30A5JlZ6C7AS81RsWjYMQmrZG6feChmgAolCl1S
github.com/valyala/fasthttp v1.51.0/go.mod h1:oI2XroL+lI7vdXyYoQk03bXBThfFl2cVdIA3Xl7cH8g=
github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8=
github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.28.0 h1:Fksou7UEQUWlKvIdsqzJmUmCX3cZuD2+P3XyyzwMhlA=
golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=

74
handlers/signup.go Normal file
View File

@@ -0,0 +1,74 @@
package handlers
import (
"github.com/gofiber/fiber/v2"
"github.com/golang-jwt/jwt/v5"
"golang.org/x/crypto/bcrypt"
"log"
"os"
"relay-server/database"
"relay-server/helpers"
"relay-server/models"
"time"
)
func Signup(c *fiber.Ctx) error {
db, _ := database.InitDatabase()
u := new(models.SignupStruct)
if err := c.BodyParser(u); err != nil {
return err
}
// Checks if username or passwords are empty
if u.Username == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"})
} else if u.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"})
}
// Checks if passwords have valid length and characters
if !helpers.IsValidPassword(u.Password) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"})
}
// Checks if username have valid length and characters
if !helpers.IsValidUsername(u.Username) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"})
}
// Checks if username already exist in database
exist, _ := database.CheckUserExists(db, u.Username)
if exist {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "user already exists"})
}
// Create password hash
passwordHash, err := bcrypt.GenerateFromPassword([]byte(u.Password), bcrypt.DefaultCost)
if err != nil {
log.Printf("error hashing password: %v", err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "internal server error"})
}
// Insert username and password hash to database
userId, err := database.InsertUser(db, u.Username, string(passwordHash))
if err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Internal server error"})
}
// Generate token with user id and username
token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
"user_id": userId,
"username": u.Username,
})
// Sign token
signedToken, err := token.SignedString([]byte(os.Getenv("JWT_SECRET")))
// Set token to cookies
cookie := new(fiber.Cookie)
cookie.Name = "token"
cookie.Value = signedToken
cookie.Expires = time.Now().Add(30 * 24 * time.Hour)
cookie.HTTPOnly = true
// If everything went well sent username and user_id assigned by database
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Successfully signed up", "username": u.Username, "user_id": userId})
}

61
helpers/filter.go Normal file
View File

@@ -0,0 +1,61 @@
package helpers
import (
"regexp"
)
const MIN_USERNAME_LENGTH = 4
const MAX_USERNAME_LENGTH = 20
const MIN_PASSWORD_LENGTH = 8
const MAX_PASSWORD_LENGTH = 128
const PASSWORD_REGEX = `^[A-Za-z0-9!@#$%^&*(),.?":{}|<>]+$`
const USERNAME_REGEX = `^[a-zA-Z0-9_]+$`
func IsValidUsername(username interface{}) bool {
// Checks if username is type of string
strUsername, ok := username.(string)
if !ok {
return false
}
match, _ := regexp.MatchString(USERNAME_REGEX, strUsername)
if !match {
return false
}
// Checks if username length is valid
if len(strUsername) < MIN_USERNAME_LENGTH {
return false
}
if len(strUsername) > MAX_USERNAME_LENGTH {
return false
}
return true
}
func IsValidPassword(password interface{}) bool {
strPassword, ok := password.(string)
if !ok {
return false
}
if len(strPassword) < MIN_PASSWORD_LENGTH {
return false
}
if len(strPassword) > MAX_PASSWORD_LENGTH {
return false
}
match, _ := regexp.MatchString(PASSWORD_REGEX, strPassword)
if !match {
return false
}
return true
}

11
main.go
View File

@@ -5,6 +5,7 @@ import (
"github.com/gofiber/fiber/v2"
"log"
"relay-server/database"
"relay-server/handlers"
)
func main() {
@@ -19,11 +20,19 @@ func main() {
log.Fatal(err)
}
}(db)
app.Get("/", func(c *fiber.Ctx) error {
return c.SendString("Hello, World!")
})
app.Get("/users", func(c *fiber.Ctx) error {
users, _ := database.GetUsers(db)
return c.JSON(fiber.Map{"users": users})
})
app.Listen(":3000")
app.Post("/api/auth/signup", handlers.Signup)
err = app.Listen(":3000")
if err != nil {
return
}
}

6
models/signup.go Normal file
View File

@@ -0,0 +1,6 @@
package models
type SignupStruct struct {
Username string `json:"username" xml:"username" form:"username"`
Password string `json:"password" xml:"password" form:"password"`
}