-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
21 changed files
with
1,625 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") | ||
} |
Oops, something went wrong.