Skip to content

Commit

Permalink
Adds Basic Infrastructure
Browse files Browse the repository at this point in the history
  • Loading branch information
jannikf02 committed Aug 12, 2024
1 parent e07ed40 commit 7fae173
Show file tree
Hide file tree
Showing 21 changed files with 1,625 additions and 1 deletion.
157 changes: 157 additions & 0 deletions backend/aashub/api/handler/users.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package api

import (
b64 "encoding/base64"
"encoding/json"
"net/http"
"time"

repositories "github.com/aas-hub-org/aashub/internal/database/repositories"
interfaces "github.com/aas-hub-org/aashub/internal/interfaces"
)

type APIUser struct {
Username string `json:"username"`
Password string `json:"password"`
Email string `json:"email"`
}

type UserHandler struct {
Repo interfaces.UserRepositoryInterface
}

type VerificationHandler struct {
VerificationRepository interfaces.VerificationRepositoryInterface
}

// RegisterUser registers a new user in the system.
// @Summary Register a new user
// @Description Registers a new user with the provided username, email, and password.
// @Tags users
// @Accept json
// @Produce json
// @Param user body APIUser true "User to register"
// @Success 201 {string} string "Successfully registered the user"
// @Failure 400 {string} string "Invalid request parameters"
// @Failure 500 {string} string "Internal server error"
// @Router /users/register [post]
func (h *UserHandler) RegisterUser(w http.ResponseWriter, r *http.Request) {
var user APIUser
err := json.NewDecoder(r.Body).Decode(&user)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

// Check if any of the required fields are empty
if user.Username == "" || user.Email == "" || user.Password == "" {
http.Error(w, "Missing required field(s)", http.StatusBadRequest)
return
}

err = h.Repo.RegisterUser(user.Username, user.Email, user.Password)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

w.WriteHeader(http.StatusCreated)
}

// VerifyUser godoc
// @Summary Verify user
// @Description Verifies a user using base64 URL encoded email and verification code.
// @Tags verification
// @Accept json
// @Produce json
// @Param email query string true "Base64 URL Encoded Email"
// @Param code query string true "Base64 URL Encoded Verification Code"
// @Success 200 {string} string "User verified successfully"
// @Failure 400 {string} string "Invalid email or code"
// @Failure 500 {string} string "Verification failed"
// @Router /verify [get]
func (h *VerificationHandler) VerifyUser(w http.ResponseWriter, r *http.Request) {
// Extract query parameters
email_byte, mail_decode_err := b64.RawURLEncoding.DecodeString(r.URL.Query().Get("email"))
code_byte, code_decode_err := b64.RawURLEncoding.DecodeString(r.URL.Query().Get("code"))

if mail_decode_err != nil || code_decode_err != nil {
http.Error(w, "Invalid email or code", http.StatusBadRequest)
return
}

email := string(email_byte)
code := string(code_byte)

error_type, err := h.VerificationRepository.Verify(email, code)
if err != nil {
if error_type == "system" {
http.Error(w, "Verification failed", http.StatusInternalServerError)
return
} else {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}
}

// Write success response
w.WriteHeader(http.StatusOK)
w.Write([]byte("User verified successfully"))
}

// LoginUser logs in a user, sets a cookie with a JWT token, and returns the token in the response
// @Summary User login and set cookie
// @Description Logs in a user by identifier (username or email) and password, sets a cookie with a JWT token if successful, and returns the JWT token in the response.
// @Tags users
// @Accept multipart/form-data
// @Produce json
// @Param identifier formData string true "Username or Email"
// @Param password formData string true "Password"
// @Success 204 "Successfully logged in"
// @Failure 400 {object} map[string]string "Missing required field(s) or bad request"
// @Failure 404 {object} map[string]string "User not found"
// @Failure 500 {object} map[string]string "Internal Server Error"
// @Router /login [post]
func (h *UserHandler) LoginUser(w http.ResponseWriter, r *http.Request) {
// Parse form data
err := r.ParseMultipartForm(32 << 20)
if err != nil {
http.Error(w, err.Error(), http.StatusBadRequest)
return
}

identifier := r.FormValue("identifier")
password := r.FormValue("password")

// Check if any of the required fields are empty
if identifier == "" || password == "" {
http.Error(w, "Missing required field(s)", http.StatusBadRequest)
return
}

token, err := h.Repo.LoginUser(identifier, password)
if err != nil {
if err == repositories.ErrUserRepoNotFound {
http.Error(w, "User not found", http.StatusNotFound)
return
}
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}

// Create a cookie
expiration := time.Now().Add(24 * time.Hour) // Set expiration to 24 hours from now
cookie := http.Cookie{
Name: "token", // Name of the cookie
Value: token, // Token value
Expires: expiration, // Expiration time
HttpOnly: true, // Make the cookie HTTP-only (not accessible via JavaScript)
Path: "/", // Cookie path
// Secure: true, // Uncomment this if you are serving your site over HTTPS
}

// Set the cookie in the response header
http.SetCookie(w, &cookie)

w.WriteHeader(http.StatusNoContent)
}
82 changes: 82 additions & 0 deletions backend/aashub/cmd/aashub/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package main

import (
"log"
"net/http"
"time"

api "github.com/aas-hub-org/aashub/api/handler"
"github.com/aas-hub-org/aashub/internal/database"
repositories "github.com/aas-hub-org/aashub/internal/database/repositories"

docs "github.com/aas-hub-org/aashub/docs"
"github.com/gin-contrib/cors"
"github.com/gin-gonic/gin"
swaggerfiles "github.com/swaggo/files"
ginSwagger "github.com/swaggo/gin-swagger"
)

// @BasePath /api/v1

// PingExample godoc
// @Summary ping example
// @Schemes
// @Description do ping
// @Tags example
// @Accept json
// @Produce json
// @Success 200 {string} Helloworld
// @Router /example/helloworld [get]
func Helloworld(g *gin.Context) {
g.JSON(http.StatusOK, "helloworld")
}

func main() {
r := gin.Default()

// Configure CORS
corsConfig := cors.Config{
AllowAllOrigins: true,
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD", "OPTIONS"},
AllowHeaders: []string{"Origin", "Content-Length", "Content-Type", "Authorization"},
ExposeHeaders: []string{"Content-Length"},
AllowCredentials: true,
MaxAge: 12 * time.Hour,
}
r.Use(cors.New(corsConfig))

// Initialize database
database, err := database.NewDB()
if err != nil {
log.Fatalf("Could not connect to the database: %v", err)
}

// Initialize repositories
verificationRepo := &repositories.VerificationRepository{DB: database}
mailVerificationRepo := &repositories.EmailVerificationRepository{VerificationRepository: verificationRepo}
userRepo := &repositories.UserRepository{DB: database, VerificationRepository: mailVerificationRepo}

// Initialize handlers
userHandler := &api.UserHandler{Repo: userRepo}
verificationHandler := &api.VerificationHandler{VerificationRepository: verificationRepo}

docs.SwaggerInfo.BasePath = "/api/v1"
v1 := r.Group("/api/v1")
{
eg := v1.Group("/example")
{
eg.GET("/helloworld", Helloworld)
}
ug := v1.Group("/users")
{
ug.POST("/register", gin.WrapF(userHandler.RegisterUser))
ug.POST("/login", gin.WrapF(userHandler.LoginUser))
}
vg := v1.Group("/verify")
{
vg.GET("/", gin.WrapF(verificationHandler.VerifyUser))
}
}
r.GET("/swagger/*any", ginSwagger.WrapHandler(swaggerfiles.Handler))
r.Run(":9000")
}
Loading

0 comments on commit 7fae173

Please sign in to comment.