Skip to content

Commit

Permalink
Merge pull request #22 from Sudarsh1010/basic-sign-up-flow
Browse files Browse the repository at this point in the history
Basic sign up flow
  • Loading branch information
DMZTdhruv authored Nov 7, 2024
2 parents 1a8b7bb + 400846f commit f71019b
Show file tree
Hide file tree
Showing 33 changed files with 752 additions and 219 deletions.
8 changes: 7 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,15 @@
# Build the application
all: build test

build:
lint:
@golangci-lint run


build-frontend:
@echo "Building frontend..."
@cd web && pnpm install && pnpm build && cd ..

build:
@echo "Building..."
@go build -o main cmd/api/main.go

Expand Down
3 changes: 2 additions & 1 deletion cmd/api/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,15 @@ package main
import (
"context"
"fmt"
"keizer-auth-api/internal/server"
"log"
"os"
"os/signal"
"strconv"
"syscall"
"time"

"keizer-auth-api/internal/server"

_ "github.com/joho/godotenv/autoload"
)

Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ require (
github.com/gookit/validate v1.5.2 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/jackc/pgx/v5 v5.7.1 // indirect
github.com/jackc/puddle/v2 v2.2.2 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
Expand Down
3 changes: 2 additions & 1 deletion internal/app/container.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
package app

import (
"sync"

"keizer-auth-api/internal/database"
"keizer-auth-api/internal/repositories"
"keizer-auth-api/internal/services"
"sync"
)

type Container struct {
Expand Down
23 changes: 18 additions & 5 deletions internal/controllers/auth_controller.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
package controllers

import (
"errors"

"keizer-auth-api/internal/services"
"keizer-auth-api/internal/validators"

"github.com/gofiber/fiber/v2"
"gorm.io/gorm"
)

type AuthController struct {
Expand All @@ -15,12 +18,12 @@ func NewAuthController(as *services.AuthService) *AuthController {
return &AuthController{authService: as}
}

func (ac *AuthController) Login(c *fiber.Ctx) error {
func (ac *AuthController) SignIn(c *fiber.Ctx) error {
return c.SendString("Login successful!")
}

func (ac *AuthController) Register(c *fiber.Ctx) error {
user := new(validators.UserRegister)
func (ac *AuthController) SignUp(c *fiber.Ctx) error {
user := new(validators.SignUpUser)

if err := c.BodyParser(user); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid request body"})
Expand All @@ -31,8 +34,18 @@ func (ac *AuthController) Register(c *fiber.Ctx) error {
}

if err := ac.authService.RegisterUser(user); err != nil {
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Something went wrong"})
if errors.Is(err, gorm.ErrCheckConstraintViolated) {
return c.
Status(fiber.StatusBadRequest).
JSON(fiber.Map{
"error": "Input validation error, please check your details",
})
}

return c.
Status(fiber.StatusInternalServerError).
JSON(fiber.Map{"error": "Failed to sign up user"})
}

return c.SendString("User registered!")
return c.JSON(fiber.Map{"message": "User Signed Up!"})
}
27 changes: 20 additions & 7 deletions internal/database/database.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@ package database
import (
"context"
"fmt"
"keizer-auth-api/internal/models"
"log"
"os"
"strconv"
"time"

"keizer-auth-api/internal/models"

_ "github.com/joho/godotenv/autoload"
"gorm.io/driver/postgres"
"gorm.io/gorm"
Expand Down Expand Up @@ -47,7 +48,8 @@ func New() Service {
}

dsn := fmt.Sprintf(
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable TimeZone=Asia/Shanghai",
"host=%s user=%s password=%s dbname=%s port=%s sslmode=disable "+
"TimeZone=Asia/Shanghai",
host,
username,
password,
Expand All @@ -64,7 +66,10 @@ func New() Service {
log.Fatal(err)
}

autoMigrate(gormDB)
err = autoMigrate(gormDB)
if err != nil {
log.Fatal(err)
}

dbInstance = &service{
db: gormDB,
Expand All @@ -78,7 +83,11 @@ func GetDB() *gorm.DB {
}

func autoMigrate(db *gorm.DB) error {
return db.AutoMigrate(&models.User{})
return db.AutoMigrate(
&models.User{},
&models.Domain{},
&models.Session{},
)
}

// Health checks the health of the database connection by pinging the database.
Expand Down Expand Up @@ -126,15 +135,19 @@ func (s *service) Health() map[string]string {
}

if dbStats.WaitCount > 1000 {
stats["message"] = "The database has a high number of wait events, indicating potential bottlenecks."
stats["message"] = "The database has a high number of wait events, " +
"indicating potential bottlenecks."
}

if dbStats.MaxIdleClosed > int64(dbStats.OpenConnections)/2 {
stats["message"] = "Many idle connections are being closed, consider revising the connection pool settings."
stats["message"] = "Many idle connections are being closed, " +
"consider revising the connection pool settings."
}

if dbStats.MaxLifetimeClosed > int64(dbStats.OpenConnections)/2 {
stats["message"] = "Many connections are being closed due to max lifetime, consider increasing max lifetime or revising the connection usage pattern."
stats["message"] = "Many connections are being closed due to max " +
"lifetime, consider increasing max lifetime or revising the " +
"connection usage pattern."
}

return stats
Expand Down
40 changes: 0 additions & 40 deletions internal/middlewares/origin_validator.go

This file was deleted.

2 changes: 1 addition & 1 deletion internal/models/domain.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package models

type Domain struct {
Origin string `gorm:"not null;unique,index;type:varchar(100)"`
Origin string `gorm:"not null;unique,index;type:varchar(100);default:null"`
Base
IsActive bool `gorm:"not null;default:false"`
}
4 changes: 2 additions & 2 deletions internal/models/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (

type Session struct {
ExpiresAt time.Time
Token string `gorm:"not null;unique"`
SessionId string `gorm:"primaryKey;not null"`
Token string `gorm:"not null;unique;default:null"`
SessionId string `gorm:"primaryKey;not null;default:null"`
UserID uuid.UUID
}
7 changes: 3 additions & 4 deletions internal/models/user.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,12 @@ import "time"

type User struct {
LastLogin time.Time
Email string `gorm:"not null;unique,index;type:varchar(100)"`
Email string `gorm:"not null;default:null;unique,index;type:varchar(100)"`
PasswordHash string
FirstName string `gorm:"not null;type:varchar(100)"`
LastName string `gorm:"type:varchar(100)"`
FirstName string `gorm:"not null;type:varchar(100);default:null"`
LastName string `gorm:"type:varchar(100);default:null"`
Base
Sessions []Session
IsVerified bool `gorm:"not null;default:false"`
IsActive bool `gorm:"not null;default:false"`
Otp string
}
3 changes: 2 additions & 1 deletion internal/repositories/session_repository.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
package repositories

import (
"keizer-auth-api/internal/models"
"time"

"keizer-auth-api/internal/models"

"gorm.io/gorm"
)

Expand Down
13 changes: 8 additions & 5 deletions internal/server/routes.go
Original file line number Diff line number Diff line change
@@ -1,20 +1,23 @@
package server

import (
"keizer-auth-api/internal/middlewares"

"github.com/gofiber/fiber/v2"
"github.com/gofiber/fiber/v2/middleware/cors"
)

func (s *FiberServer) RegisterFiberRoutes() {
s.Use(cors.New(cors.Config{
AllowOrigins: "*",
}))

s.Get("/health", s.healthHandler)

api := s.Group("/api", middlewares.OriginValidationMiddleware)
api := s.Group("/api")

// auth handlers
auth := api.Group("/auth")
auth.Post("/register", s.controllers.Auth.Register)
auth.Post("/login", s.controllers.Auth.Login)
auth.Post("/sign-up", s.controllers.Auth.SignUp)
auth.Post("/sign-in", s.controllers.Auth.SignIn)

s.Static("/", "./web/dist")
s.Static("*", "./web/dist/index.html")
Expand Down
17 changes: 8 additions & 9 deletions internal/services/auth_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package services

import (
"fmt"

"keizer-auth-api/internal/models"
"keizer-auth-api/internal/repositories"
"keizer-auth-api/internal/utils"
Expand All @@ -16,31 +17,29 @@ func NewAuthService(userRepo *repositories.UserRepository) *AuthService {
return &AuthService{userRepo: userRepo}
}

func (as *AuthService) RegisterUser(userRegister *validators.UserRegister) error {
func (as *AuthService) RegisterUser(userRegister *validators.SignUpUser) error {
passwordHash, err := utils.HashPassword(userRegister.Password)
if err != nil {
return fmt.Errorf("failed to hash password: %w", err)
}

otp, err := utils.GenerateOTP()
if err != nil {
return fmt.Errorf("failed to generate OTP: %w", err)
}

err = SendOTPEmail(userRegister.Email, otp)
if err != nil {
// TODO: email should be sent using async func
if err = SendOTPEmail(userRegister.Email, otp); err != nil {
return fmt.Errorf("failed to send OTP email: %w", err)
}

err = as.userRepo.CreateUser(&models.User{
if err = as.userRepo.CreateUser(&models.User{
Email: userRegister.Email,
FirstName: userRegister.FirstName,
LastName: userRegister.LastName,
PasswordHash: passwordHash,
Otp: otp,
})

if err != nil {
return fmt.Errorf("failed to create user: %w", err)
}); err != nil {
return err
}

return nil
Expand Down
21 changes: 17 additions & 4 deletions internal/services/email_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,20 @@ type EmailService struct {
}

func NewEmailService() *EmailService {
return &EmailService{host: smtpHost, port: smtpPort, user: smtpUser, pass: smtpPassword, from: from}
return &EmailService{
host: smtpHost,
port: smtpPort,
user: smtpUser,
pass: smtpPassword,
from: from,
}
}

func (es *EmailService) SendEmail(to string, subject string, body string) error {
func (es *EmailService) SendEmail(
to string,
subject string,
body string,
) error {
message := []byte("To: " + to + "\r\n" +
"Subject: " + subject + "\r\n" +
"\r\n" +
Expand All @@ -36,9 +46,12 @@ func (es *EmailService) SendEmail(to string, subject string, body string) error
if es.pass != "" {
auth = smtp.PlainAuth("", es.user, es.pass, es.host)
}
err := smtp.SendMail(es.host+":"+es.port, auth, es.from, []string{to}, message)

if err != nil {
if err := smtp.SendMail(
es.host+":"+es.port, auth, es.from,
[]string{to},
message,
); err != nil {
return fmt.Errorf("failed to send email: %w", err)
}
return nil
Expand Down
14 changes: 14 additions & 0 deletions internal/utils/general.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package utils

import "unicode"

func ToSnakeCase(str string) string {
var result []rune
for i, char := range str {
if unicode.IsUpper(char) && i > 0 {
result = append(result, '_')
}
result = append(result, unicode.ToLower(char))
}
return string(result)
}
Loading

0 comments on commit f71019b

Please sign in to comment.