This repository has been archived on 2026-01-02. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
cover-letter-templater/backend/controllers/user/user.go
Leons Aleksandrovs 938c9a66e5 feat(api): create template
Add template table to the database
Create controller function to check if user has template, and create it
in the database
Made universal jwt.Claims of user data retrieval function
2025-07-09 23:19:31 +03:00

147 lines
3.6 KiB
Go

package user
import (
"backend/config"
"backend/models/user"
"backend/utils/hash"
"backend/utils/jwt"
"errors"
"fmt"
"log"
"net/http"
res "backend/utils/responses"
"github.com/gin-gonic/gin"
"github.com/go-playground/validator/v10"
"github.com/jackc/pgx/v5/pgconn"
)
type RegisterForm struct {
Email string `json:"email" validate:"required,email"`
Name string `json:"name" validate:"required,min=2,max=50"`
Password string `json:"password" validate:"required,min=8"`
RepeatPassword string `json:"repeatPassword" validate:"required,min=8,eqfield=Password"`
}
func Register(c *gin.Context) {
// Receive data from frontend, check if data is okay, hash password, call model
var data RegisterForm
if err := c.ShouldBindJSON(&data); err != nil {
res.Error(c, err.Error(), http.StatusInternalServerError)
return
}
// Validate data
validate := validator.New()
if err := validate.Struct(data); err != nil {
// Handle error
res.Error(c, err.Error(), http.StatusBadRequest)
return
}
// Hash password
hash, err := hash.HashPassword(data.Password)
if err != nil {
res.Error(c, err.Error(), http.StatusInternalServerError)
return
}
// Insert into database
if err := user.Create(data.Email, data.Name, hash); err != nil {
// Find out postgres error
var pgErr *pgconn.PgError
if !errors.As(err, &pgErr) {
// Unknown error
res.Error(c, fmt.Sprintf("[UNEXPECTED DB ERROR] %v", err.Error()), http.StatusInternalServerError)
return
}
// Postgres error
log.Printf("[ERROR] Postgres code: %s", pgErr.Code)
if pgErr.Code == "23505" {
// UNIQUE constraint violation (EMAIL TAKEN)
res.Error(c, "Email already exists", http.StatusBadRequest)
return
}
// Unknown error
res.Error(c, fmt.Sprintf("[UNKNOWN ERROR] %v", err.Error()), http.StatusInternalServerError)
return
}
// Return success
res.Success(c, gin.H{
"message": "Successfully registered",
})
}
type LoginForm struct {
Email string `json:"email" validate:"required,email"`
Password string `json:"password" validate:"required"`
}
func Login(c *gin.Context) {
// Bind data
var data LoginForm
if err := c.ShouldBindJSON(&data); err != nil {
res.Error(c, err.Error(), http.StatusBadRequest)
return
}
// Validate data
validate := validator.New()
if err := validate.Struct(data); err != nil {
res.Error(c, err.Error(), http.StatusBadRequest)
return
}
// Find user in database
user, err := user.FindByEmail(data.Email)
if err != nil {
var pgErr *pgconn.PgError
// Check if pg err
if errors.As(err, &pgErr) {
res.Error(c, pgErr.Message, http.StatusInternalServerError)
return
}
// Email not found
res.Error(c, "Email or password are incorrect", http.StatusUnauthorized)
return
}
// Check hash
match := hash.CheckPasswordHash(data.Password, user.Password)
if !match {
res.Error(c, "Email or password are incorrect", http.StatusUnauthorized)
return
}
// Generate JWT token, and send to client
signedToken, err := jwt.GenerateJWT(user)
if err != nil {
res.Error(c, fmt.Sprintf("[JWT Generation] %s", err.Error()), http.StatusInternalServerError)
return
}
// Return token as cookie
secureCookie := config.Env["Environment"] != "dev" // In dev environment cookie wont be secure
// 3600S -> 1H * 24H -> 1D * 7 -> 1W
c.SetCookie("jwt-token", signedToken, 3600*24*7, "/", "localhost", secureCookie, true)
// Return successful login
res.Success(c, gin.H{"message": "Successfully logged in"})
}
// Returns info from token middleware
func TokenInfo(c *gin.Context) {
user, err := jwt.GetUser(c)
if err != nil {
res.Error(c, err.Error(), http.StatusInternalServerError)
return
}
res.Success(c, user)
}