I am batman
This commit is contained in:
396
internal/handlers/authHandler.go
Normal file
396
internal/handlers/authHandler.go
Normal file
@@ -0,0 +1,396 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"OpsMastery.v5/internal/utils"
|
||||
"github.com/gofiber/adaptor/v2"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/markbates/goth/gothic"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
func SignUp(c *fiber.Ctx) error {
|
||||
var user models.User
|
||||
var input struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
Name string `json:"name"`
|
||||
Role string `json:"role"`
|
||||
Username string `json:"username"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
user.Email = strings.ToLower(input.Email)
|
||||
user.Password = input.Password
|
||||
user.Name = strings.ToLower(input.Name)
|
||||
user.Role = input.Role
|
||||
user.Username = strings.ToLower(input.Username)
|
||||
user.Active = false
|
||||
user.VerificationToken = utils.GenerateRandomToken()
|
||||
|
||||
storedHash, err := bcrypt.GenerateFromPassword([]byte(user.Password), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to hash password"})
|
||||
}
|
||||
user.Password = string(storedHash)
|
||||
|
||||
log.Printf("Hashed password for user %s: %s\n", user.Email, user.Password)
|
||||
|
||||
var existingUser models.User
|
||||
if err := database.DB().Where("email = ? OR username = ?", user.Email, user.Username).First(&existingUser).Error; err == nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "Email or username already exists"})
|
||||
}
|
||||
|
||||
if err := database.DB().Create(&user).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
if err := utils.SendVerificationEmail(user.Email, user.VerificationToken); err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to send verification email"})
|
||||
}
|
||||
|
||||
return c.Status(http.StatusCreated).JSON(fiber.Map{
|
||||
"message": "Registration successful. Please check your email to verify your account.",
|
||||
})
|
||||
}
|
||||
|
||||
func SignIn(c *fiber.Ctx) error {
|
||||
var userInput struct {
|
||||
EmailOrUsername string `json:"emailOrUsername"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&userInput); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
// Force to lowercase for case-insensitive match
|
||||
normalizedInput := strings.ToLower(userInput.EmailOrUsername)
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().Where("LOWER(email) = ? OR LOWER(username) = ?", normalizedInput, normalizedInput).First(&user).Error; err != nil {
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid email/username or password"})
|
||||
}
|
||||
|
||||
if !user.Active {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{
|
||||
"error": "Please verify your email before signing in",
|
||||
})
|
||||
}
|
||||
|
||||
if err := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(userInput.Password)); err != nil {
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid email/username or password"})
|
||||
}
|
||||
|
||||
// Generate JWTs
|
||||
accessToken, refreshToken, err := utils.GenerateJWT(user)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "Could not generate tokens"})
|
||||
}
|
||||
|
||||
// Set Refresh Token in HttpOnly Cookie
|
||||
c.Cookie(&fiber.Cookie{
|
||||
Name: "refresh_token",
|
||||
Value: refreshToken,
|
||||
Expires: time.Now().Add(7 * 24 * time.Hour),
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
SameSite: "Strict",
|
||||
Path: "/api/v1/auth/refresh", // restrict usage
|
||||
})
|
||||
|
||||
// Return Access Token in response (frontend stores in memory)
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{
|
||||
"message": "Sign in successful",
|
||||
"access_token": accessToken,
|
||||
"user": fiber.Map{
|
||||
"id": user.ID,
|
||||
"email": user.Email,
|
||||
"name": user.Name,
|
||||
"role": user.Role,
|
||||
"username": user.Username,
|
||||
"address": user.Address,
|
||||
"phoneNumber": user.PhoneNumber,
|
||||
"active": user.Active,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
func RefreshToken(c *fiber.Ctx) error {
|
||||
refreshToken := c.Cookies("refresh_token")
|
||||
if refreshToken == "" {
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "Refresh token not found"})
|
||||
}
|
||||
|
||||
claims, err := utils.ValidateJWT(refreshToken, true)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "Invalid refresh token"})
|
||||
}
|
||||
|
||||
// Get user from claims
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, claims["sub"]).Error; err != nil {
|
||||
return c.Status(http.StatusUnauthorized).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
// Generate new tokens
|
||||
accessToken, newRefreshToken, err := utils.GenerateJWT(user)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": "Could not generate tokens"})
|
||||
}
|
||||
|
||||
// Rotate refresh token
|
||||
c.Cookie(&fiber.Cookie{
|
||||
Name: "refresh_token",
|
||||
Value: newRefreshToken,
|
||||
Expires: time.Now().Add(7 * 24 * time.Hour),
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
SameSite: "Strict",
|
||||
Path: "/api/auth/refresh",
|
||||
})
|
||||
|
||||
// Return new Access Token
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
"access_token": accessToken,
|
||||
})
|
||||
}
|
||||
|
||||
func SignOut(c *fiber.Ctx) error {
|
||||
// Invalidate the refresh token cookie
|
||||
c.Cookie(&fiber.Cookie{
|
||||
Name: "refresh_token",
|
||||
Value: "",
|
||||
Expires: time.Now().Add(-1 * time.Hour),
|
||||
HTTPOnly: true,
|
||||
Secure: false,
|
||||
SameSite: "Strict",
|
||||
Path: "/api/auth/refresh",
|
||||
})
|
||||
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Successfully signed out"})
|
||||
}
|
||||
|
||||
// func VerifyEmail(c *fiber.Ctx) error {
|
||||
// token := c.Params("token")
|
||||
|
||||
// var user models.User
|
||||
// if err := database.DB().Where("verification_token = ?", token).First(&user).Error; err != nil {
|
||||
// return c.Status(fiber.StatusNotFound).JSON(fiber.Map{
|
||||
// "error": "Invalid verification token",
|
||||
// })
|
||||
// }
|
||||
|
||||
// user.Active = true
|
||||
// user.VerificationToken = "" // Clear the token after verification
|
||||
|
||||
// if err := database.DB().Save(&user).Error; err != nil {
|
||||
// return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
// "error": "Failed to verify email",
|
||||
// })
|
||||
// }
|
||||
|
||||
// return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
// "message": "Email verified successfully",
|
||||
// })
|
||||
// }
|
||||
func VerifyEmail(c *fiber.Ctx) error {
|
||||
var body struct {
|
||||
Token string `json:"token"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&body); err != nil || body.Token == "" {
|
||||
return c.Redirect("http://localhost:3000/auth/verification-failed", fiber.StatusSeeOther)
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().Where("verification_token = ?", body.Token).First(&user).Error; err != nil {
|
||||
return c.Redirect("http://localhost:3000/auth/verification-failed", fiber.StatusSeeOther)
|
||||
}
|
||||
|
||||
user.Active = true
|
||||
user.VerificationToken = ""
|
||||
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
return c.Redirect("http://localhost:3000/auth/verification-failed", fiber.StatusSeeOther)
|
||||
}
|
||||
|
||||
return c.Redirect("http://localhost:3000/auth/verification-success", fiber.StatusSeeOther)
|
||||
}
|
||||
|
||||
func RequestPasswordReset(c *fiber.Ctx) error {
|
||||
var input struct {
|
||||
Email string `json:"email"`
|
||||
}
|
||||
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().Where("email = ?", input.Email).First(&user).Error; err != nil {
|
||||
// Don't reveal if email exists or not
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
"message": "If your email is registered, you will receive a password reset link",
|
||||
})
|
||||
}
|
||||
|
||||
resetToken := utils.GenerateRandomToken()
|
||||
user.ResetToken = resetToken
|
||||
user.ResetTokenExpiry = time.Now().Add(1 * time.Hour)
|
||||
|
||||
// Log the token being set
|
||||
log.Printf("Setting reset token for user %s: %s", user.Email, resetToken)
|
||||
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
log.Printf("Error saving user with reset token: %v", err)
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to process password reset",
|
||||
})
|
||||
}
|
||||
|
||||
// Add this log right before SendPasswordResetEmail
|
||||
log.Printf("About to send reset email with token: %s", resetToken)
|
||||
|
||||
if err := utils.SendPasswordResetEmail(user.Email, resetToken); err != nil {
|
||||
log.Printf("Error sending reset email: %v", err)
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to send reset email",
|
||||
})
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusOK).JSON(fiber.Map{
|
||||
"message": "If your email is registered, you will receive a password reset link",
|
||||
})
|
||||
}
|
||||
|
||||
func ResetPassword(c *fiber.Ctx) error {
|
||||
var body struct {
|
||||
ResetToken string `json:"reset_token"`
|
||||
NewPassword string `json:"new_password"`
|
||||
}
|
||||
|
||||
// Log the raw request body
|
||||
rawBody := string(c.Body())
|
||||
log.Printf("Received reset password request body: %s", rawBody)
|
||||
|
||||
if err := c.BodyParser(&body); err != nil {
|
||||
log.Printf("Error parsing request body: %v", err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid request body",
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("Reset token: %s", body.ResetToken)
|
||||
|
||||
// Validate the token
|
||||
var user models.User
|
||||
if err := database.DB().Where("reset_token = ?", body.ResetToken).First(&user).Error; err != nil {
|
||||
log.Printf("No user found with reset token: %s, error: %v", body.ResetToken, err)
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Invalid reset token",
|
||||
})
|
||||
}
|
||||
|
||||
// Check if token has expired
|
||||
if user.ResetTokenExpiry.Before(time.Now()) {
|
||||
log.Printf("Token expired. Expiry: %v, Current time: %v", user.ResetTokenExpiry, time.Now())
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{
|
||||
"error": "Reset token has expired",
|
||||
})
|
||||
}
|
||||
|
||||
// Hash the new password
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(body.NewPassword), bcrypt.DefaultCost)
|
||||
if err != nil {
|
||||
log.Printf("Error hashing password: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to hash password",
|
||||
})
|
||||
}
|
||||
|
||||
// Update user's password and clear reset token
|
||||
user.Password = string(hashedPassword)
|
||||
user.ResetToken = ""
|
||||
user.ResetTokenExpiry = time.Time{}
|
||||
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
log.Printf("Error saving user: %v", err)
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{
|
||||
"error": "Failed to update password",
|
||||
})
|
||||
}
|
||||
|
||||
log.Printf("Password successfully reset for user: %s", user.Email)
|
||||
return c.JSON(fiber.Map{
|
||||
"message": "Password successfully reset",
|
||||
})
|
||||
}
|
||||
|
||||
func validateResetToken(token string) (bool, error) {
|
||||
var user models.User
|
||||
if err := database.DB().Where("reset_token = ?", token).First(&user).Error; err != nil {
|
||||
log.Printf("No user found with reset token: %s, error: %v", token, err)
|
||||
return false, err
|
||||
}
|
||||
|
||||
// Check if token has expired
|
||||
if user.ResetTokenExpiry.Before(time.Now()) {
|
||||
log.Printf("Token expired. Expiry: %v, Current time: %v", user.ResetTokenExpiry, time.Now())
|
||||
return false, fmt.Errorf("reset token has expired")
|
||||
}
|
||||
|
||||
log.Printf("Token validated successfully for user: %s", user.Email)
|
||||
return true, nil
|
||||
}
|
||||
|
||||
// Generic OAuth login handler
|
||||
func OAuthLogin(c *fiber.Ctx) error {
|
||||
provider := c.Params("provider")
|
||||
// Set provider in query for gothic
|
||||
req := c.Request()
|
||||
uri := req.URI()
|
||||
uri.SetQueryString("provider=" + provider)
|
||||
return adaptor.HTTPHandlerFunc(gothic.BeginAuthHandler)(c)
|
||||
}
|
||||
|
||||
func OAuthCallback(c *fiber.Ctx) error {
|
||||
provider := c.Params("provider")
|
||||
req := c.Request()
|
||||
req.URI().SetQueryString("provider=" + provider)
|
||||
|
||||
return adaptor.HTTPHandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
user, err := gothic.CompleteUserAuth(w, r)
|
||||
if err != nil {
|
||||
http.Error(w, err.Error(), http.StatusUnauthorized)
|
||||
return
|
||||
}
|
||||
|
||||
// Check if user with this email already exists
|
||||
var existingUser models.User
|
||||
email := strings.ToLower(user.Email)
|
||||
if err := database.DB().Where("LOWER(email) = ?", email).First(&existingUser).Error; err == nil {
|
||||
// User exists, do not allow OAuth sign up
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
json.NewEncoder(w).Encode(map[string]string{
|
||||
"error": "This email already exists, please sign in.",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// ...proceed with creating new user from OAuth profile...
|
||||
// (your existing logic here)
|
||||
json.NewEncoder(w).Encode(user)
|
||||
})(c)
|
||||
}
|
||||
224
internal/handlers/chatHandler.go
Normal file
224
internal/handlers/chatHandler.go
Normal file
@@ -0,0 +1,224 @@
|
||||
// // filepath: /home/cody/OpsMastery.v5/internal/handlers/chatHandler.go
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"github.com/gofiber/websocket/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
clients = make(map[*websocket.Conn]uint) // map connection to user ID
|
||||
broadcast = make(chan Message)
|
||||
mu sync.Mutex
|
||||
chatClients = make(map[uint]map[*websocket.Conn]bool) // chatID -> set of connections
|
||||
)
|
||||
|
||||
type Message struct {
|
||||
SenderID uint `json:"sender_id"`
|
||||
RecipientID uint `json:"recipient_id"`
|
||||
Content string `json:"content"`
|
||||
}
|
||||
|
||||
type Chat struct {
|
||||
ID uint `json:"id"`
|
||||
Name string `json:"name"`
|
||||
TicketID *uint `json:"ticket_id,omitempty"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
Users []models.User `json:"users"`
|
||||
Messages []models.ChatMessage `json:"messages"`
|
||||
}
|
||||
|
||||
// WebSocket chat endpoint
|
||||
func ChatWebSocket(c *fiber.Ctx) error {
|
||||
// Upgrade to WebSocket
|
||||
if websocket.IsWebSocketUpgrade(c) {
|
||||
return c.Next()
|
||||
}
|
||||
return fiber.ErrUpgradeRequired
|
||||
}
|
||||
|
||||
func HandleChat(c *websocket.Conn, claims map[string]interface{}) {
|
||||
senderID, _ := claims["userID"].(uint)
|
||||
|
||||
// Register connection
|
||||
if chatClients[0] == nil {
|
||||
chatClients[0] = make(map[*websocket.Conn]bool)
|
||||
}
|
||||
chatClients[0][c] = true
|
||||
defer func() {
|
||||
delete(chatClients[0], c)
|
||||
c.Close()
|
||||
}()
|
||||
|
||||
for {
|
||||
var msg struct {
|
||||
Content string `json:"content"`
|
||||
}
|
||||
if err := c.ReadJSON(&msg); err != nil {
|
||||
break
|
||||
}
|
||||
|
||||
// Save message to DB
|
||||
chatMsg := models.ChatMessage{
|
||||
ChatID: 0,
|
||||
SenderID: senderID,
|
||||
Content: msg.Content,
|
||||
SentAt: time.Now(),
|
||||
}
|
||||
database.DB().Create(&chatMsg)
|
||||
|
||||
// Broadcast to all clients in the chat
|
||||
for client := range chatClients[0] {
|
||||
client.WriteJSON(chatMsg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Broadcast messages to all clients
|
||||
func StartBroadcast() {
|
||||
for {
|
||||
msg := <-broadcast
|
||||
mu.Lock()
|
||||
for conn := range clients {
|
||||
if err := conn.WriteJSON(msg); err != nil {
|
||||
fmt.Println("Error broadcasting:", err)
|
||||
}
|
||||
}
|
||||
mu.Unlock()
|
||||
}
|
||||
}
|
||||
|
||||
// GET /api/v1/chats?user1=username1&user2=username2
|
||||
func GetChatHistory(c *fiber.Ctx) error {
|
||||
user1 := c.Query("user1")
|
||||
user2 := c.Query("user2")
|
||||
var messages []models.ChatMessage
|
||||
database.DB().Where(
|
||||
"(sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?)",
|
||||
user1, user2, user2, user1,
|
||||
).Order("created_at asc").Find(&messages)
|
||||
return c.JSON(messages) // Returns [] if no messages exist
|
||||
}
|
||||
|
||||
// GET /api/v1/chat_partners?user_id=some_user_id
|
||||
func GetChatPartners(c *fiber.Ctx) error {
|
||||
userID := c.Query("user_id")
|
||||
var partners []models.User
|
||||
database.DB().Raw(`
|
||||
SELECT DISTINCT u.*
|
||||
FROM users u
|
||||
JOIN chat_messages cm
|
||||
ON (cm.sender_id = u.id OR cm.recipient_id = u.id)
|
||||
WHERE (cm.sender_id = ? OR cm.recipient_id = ?) AND u.id != ?
|
||||
`, userID, userID, userID).Scan(&partners)
|
||||
return c.JSON(partners)
|
||||
}
|
||||
|
||||
// DELETE /api/v1/chats/delete?user1=username1&user2=username2
|
||||
func DeleteChatBetweenUsers(c *fiber.Ctx) error {
|
||||
user1 := c.Query("user1")
|
||||
user2 := c.Query("user2")
|
||||
result := database.DB().Where(
|
||||
"(sender_id = ? AND recipient_id = ?) OR (sender_id = ? AND recipient_id = ?)",
|
||||
user1, user2, user2, user1,
|
||||
).Delete(&models.ChatMessage{})
|
||||
if result.Error != nil {
|
||||
return c.Status(500).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
return c.JSON(fiber.Map{"deleted": result.RowsAffected})
|
||||
}
|
||||
|
||||
// Create a new chat (group or direct)
|
||||
func CreateChat(c *fiber.Ctx) error {
|
||||
var input struct {
|
||||
Name string `json:"name"`
|
||||
UserIDs []uint `json:"user_ids"`
|
||||
IsPrivate bool `json:"is_private"`
|
||||
TicketID *uint `json:"ticket_id"`
|
||||
}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
chat := models.Chat{
|
||||
Name: input.Name,
|
||||
IsPrivate: input.IsPrivate,
|
||||
TicketID: input.TicketID,
|
||||
}
|
||||
|
||||
if err := database.DB().Create(&chat).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not create chat"})
|
||||
}
|
||||
|
||||
if len(input.UserIDs) > 0 {
|
||||
var users []models.User
|
||||
if err := database.DB().Where("id IN ?", input.UserIDs).Find(&users).Error; err == nil {
|
||||
database.DB().Model(&chat).Association("Users").Append(users)
|
||||
}
|
||||
}
|
||||
|
||||
return c.Status(fiber.StatusCreated).JSON(chat)
|
||||
}
|
||||
|
||||
// Add users to an existing chat
|
||||
func AddUsersToChat(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
var input struct {
|
||||
UserIDs []uint `json:"user_ids"`
|
||||
}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
var chat models.Chat
|
||||
if err := database.DB().First(&chat, chatID).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Chat not found"})
|
||||
}
|
||||
var users []models.User
|
||||
if err := database.DB().Where("id IN ?", input.UserIDs).Find(&users).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Users not found"})
|
||||
}
|
||||
database.DB().Model(&chat).Association("Users").Append(users)
|
||||
return c.JSON(fiber.Map{"added": len(users)})
|
||||
}
|
||||
|
||||
// Get all chats for a user (for sidebar)
|
||||
func GetChatsForUser(c *fiber.Ctx) error {
|
||||
userID := c.Params("userId")
|
||||
var chats []models.Chat
|
||||
if err := database.DB().Joins("JOIN chat_users ON chat_users.chat_id = chats.id").
|
||||
Where("chat_users.user_id = ?", userID).
|
||||
Preload("Users").
|
||||
Preload("Messages").
|
||||
Find(&chats).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not fetch chats"})
|
||||
}
|
||||
return c.JSON(chats)
|
||||
}
|
||||
|
||||
// Get all messages for a chat
|
||||
func GetChatMessages(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
var messages []models.ChatMessage
|
||||
if err := database.DB().Where("chat_id = ?", chatID).Order("sent_at asc").Find(&messages).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not fetch messages"})
|
||||
}
|
||||
return c.JSON(messages)
|
||||
}
|
||||
|
||||
// Delete a chat (and its messages)
|
||||
func DeleteChat(c *fiber.Ctx) error {
|
||||
chatID := c.Params("chatId")
|
||||
if err := database.DB().Where("chat_id = ?", chatID).Delete(&models.ChatMessage{}).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not delete messages"})
|
||||
}
|
||||
if err := database.DB().Delete(&models.Chat{}, chatID).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not delete chat"})
|
||||
}
|
||||
return c.JSON(fiber.Map{"deleted": chatID})
|
||||
}
|
||||
327
internal/handlers/taskHandler.go
Normal file
327
internal/handlers/taskHandler.go
Normal file
@@ -0,0 +1,327 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type sectionPayload struct {
|
||||
Title string `json:"title"`
|
||||
TitleAlt string `json:"Title"`
|
||||
Position *int `json:"position"`
|
||||
PositionAlt *int `json:"Position"`
|
||||
}
|
||||
|
||||
type taskPayload struct {
|
||||
Title string `json:"title"`
|
||||
TitleAlt string `json:"Title"`
|
||||
Description string `json:"description"`
|
||||
DescriptionAlt string `json:"Description"`
|
||||
Position *int `json:"position"`
|
||||
PositionAlt *int `json:"Position"`
|
||||
SectionID *uint `json:"section_id"`
|
||||
SectionIDAlt *uint `json:"SectionID"`
|
||||
}
|
||||
|
||||
func (p sectionPayload) normalizedTitle() string {
|
||||
title := strings.TrimSpace(p.Title)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(p.TitleAlt)
|
||||
}
|
||||
return title
|
||||
}
|
||||
|
||||
func (p sectionPayload) normalizedPosition() int {
|
||||
if p.Position != nil {
|
||||
return *p.Position
|
||||
}
|
||||
if p.PositionAlt != nil {
|
||||
return *p.PositionAlt
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
func (p taskPayload) normalizedTask() models.Tasks {
|
||||
title := strings.TrimSpace(p.Title)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(p.TitleAlt)
|
||||
}
|
||||
|
||||
description := p.Description
|
||||
if description == "" {
|
||||
description = p.DescriptionAlt
|
||||
}
|
||||
|
||||
position := 0
|
||||
if p.Position != nil {
|
||||
position = *p.Position
|
||||
} else if p.PositionAlt != nil {
|
||||
position = *p.PositionAlt
|
||||
}
|
||||
|
||||
sectionID := uint(0)
|
||||
if p.SectionID != nil {
|
||||
sectionID = *p.SectionID
|
||||
} else if p.SectionIDAlt != nil {
|
||||
sectionID = *p.SectionIDAlt
|
||||
}
|
||||
|
||||
return models.Tasks{
|
||||
Title: title,
|
||||
Description: description,
|
||||
Position: position,
|
||||
SectionID: sectionID,
|
||||
}
|
||||
}
|
||||
|
||||
func sectionExists(sectionID uint) (bool, error) {
|
||||
var count int64
|
||||
err := database.DB().Model(&models.TaskSection{}).Where("id = ?", sectionID).Count(&count).Error
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return count > 0, nil
|
||||
}
|
||||
|
||||
func CreateTaskSection(c *fiber.Ctx) error {
|
||||
var payload sectionPayload
|
||||
if err := c.BodyParser(&payload); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
section := models.TaskSection{
|
||||
Title: payload.normalizedTitle(),
|
||||
Position: payload.normalizedPosition(),
|
||||
}
|
||||
|
||||
if section.Title == "" {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "title is required"})
|
||||
}
|
||||
|
||||
if err := database.DB().Create(§ion).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(section)
|
||||
}
|
||||
|
||||
func ListTaskSections(c *fiber.Ctx) error {
|
||||
var sections []models.TaskSection
|
||||
err := database.DB().
|
||||
Preload("Tasks", func(db *gorm.DB) *gorm.DB {
|
||||
return db.Order("position asc, id asc")
|
||||
}).
|
||||
Order("position asc, id asc").
|
||||
Find(§ions).Error
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(sections)
|
||||
}
|
||||
|
||||
func UpdateTaskSectionByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
sectionID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid section ID"})
|
||||
}
|
||||
|
||||
var payload sectionPayload
|
||||
if err := c.BodyParser(&payload); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
updates := map[string]any{}
|
||||
if title := payload.normalizedTitle(); title != "" {
|
||||
updates["title"] = title
|
||||
}
|
||||
if payload.Position != nil || payload.PositionAlt != nil {
|
||||
updates["position"] = payload.normalizedPosition()
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "at least one field is required"})
|
||||
}
|
||||
|
||||
result := database.DB().Model(&models.TaskSection{}).Where("id = ?", sectionID).Updates(updates)
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "Section not found"})
|
||||
}
|
||||
|
||||
var section models.TaskSection
|
||||
if err := database.DB().First(§ion, sectionID).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(section)
|
||||
}
|
||||
|
||||
func DeleteTaskSectionByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
sectionID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid section ID"})
|
||||
}
|
||||
|
||||
result := database.DB().Unscoped().Delete(&models.TaskSection{}, sectionID)
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Section not found"})
|
||||
}
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Section deleted successfully"})
|
||||
}
|
||||
|
||||
func CreateTask(c *fiber.Ctx) error {
|
||||
var payload taskPayload
|
||||
if err := c.BodyParser(&payload); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
task := payload.normalizedTask()
|
||||
if task.Title == "" || task.SectionID == 0 {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "title and section_id are required"})
|
||||
}
|
||||
|
||||
exists, err := sectionExists(task.SectionID)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
if !exists {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "section not found"})
|
||||
}
|
||||
|
||||
if err := database.DB().Create(&task).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(task)
|
||||
}
|
||||
|
||||
func ListTasks(c *fiber.Ctx) error {
|
||||
var tasks []models.Tasks
|
||||
err := database.DB().
|
||||
Preload("Section").
|
||||
Order("position asc, id asc").
|
||||
Find(&tasks).Error
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(tasks)
|
||||
}
|
||||
|
||||
func GetTaskByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var task models.Tasks
|
||||
|
||||
taskID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"})
|
||||
}
|
||||
|
||||
if err := database.DB().Preload("Section").First(&task, taskID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Task not found"})
|
||||
}
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(task)
|
||||
}
|
||||
|
||||
func DeleteTaskByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
taskID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"})
|
||||
}
|
||||
|
||||
result := database.DB().Unscoped().Delete(&models.Tasks{}, taskID)
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Task not found"})
|
||||
}
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Task deleted successfully"})
|
||||
}
|
||||
|
||||
func UpdateTaskByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
taskID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid task ID"})
|
||||
}
|
||||
|
||||
var payload taskPayload
|
||||
if err := c.BodyParser(&payload); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
updates := map[string]any{}
|
||||
title := strings.TrimSpace(payload.Title)
|
||||
if title == "" {
|
||||
title = strings.TrimSpace(payload.TitleAlt)
|
||||
}
|
||||
if title != "" {
|
||||
updates["title"] = title
|
||||
}
|
||||
|
||||
if payload.Description != "" || payload.DescriptionAlt != "" {
|
||||
description := payload.Description
|
||||
if description == "" {
|
||||
description = payload.DescriptionAlt
|
||||
}
|
||||
updates["description"] = description
|
||||
}
|
||||
|
||||
if payload.Position != nil {
|
||||
updates["position"] = *payload.Position
|
||||
} else if payload.PositionAlt != nil {
|
||||
updates["position"] = *payload.PositionAlt
|
||||
}
|
||||
|
||||
sectionID := uint(0)
|
||||
if payload.SectionID != nil {
|
||||
sectionID = *payload.SectionID
|
||||
} else if payload.SectionIDAlt != nil {
|
||||
sectionID = *payload.SectionIDAlt
|
||||
}
|
||||
if sectionID != 0 {
|
||||
exists, err := sectionExists(sectionID)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
if !exists {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "section not found"})
|
||||
}
|
||||
updates["section_id"] = sectionID
|
||||
}
|
||||
|
||||
if len(updates) == 0 {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "at least one field is required"})
|
||||
}
|
||||
|
||||
result := database.DB().Model(&models.Tasks{}).Where("id = ?", taskID).Updates(updates)
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "Task not found"})
|
||||
}
|
||||
|
||||
var task models.Tasks
|
||||
if err := database.DB().Preload("Section").First(&task, taskID).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
return c.JSON(task)
|
||||
}
|
||||
84
internal/handlers/ticketHandler.go
Normal file
84
internal/handlers/ticketHandler.go
Normal file
@@ -0,0 +1,84 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
func CreateTicket(c *fiber.Ctx) error {
|
||||
var ticket models.Ticket
|
||||
if err := c.BodyParser(&ticket); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
if err := database.DB().Create(&ticket).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(http.StatusCreated).JSON(ticket)
|
||||
}
|
||||
|
||||
func ListTickets(c *fiber.Ctx) error {
|
||||
var tickets []models.Ticket
|
||||
if err := database.DB().Find(&tickets).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(tickets)
|
||||
}
|
||||
|
||||
func GetTicketByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var ticket models.Ticket
|
||||
|
||||
ticketID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid ticket ID"})
|
||||
}
|
||||
|
||||
if err := database.DB().First(&ticket, ticketID).Error; err != nil {
|
||||
if err == gorm.ErrRecordNotFound {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Ticket not found"})
|
||||
}
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(ticket)
|
||||
}
|
||||
|
||||
func DeleteTicketByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
ticketID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid ticket ID"})
|
||||
}
|
||||
|
||||
result := database.DB().Unscoped().Delete(&models.Ticket{}, ticketID)
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Ticket not found"})
|
||||
}
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Ticket deleted successfully"})
|
||||
}
|
||||
|
||||
func UpdateTicketByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
ticketID, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid ticket ID"})
|
||||
}
|
||||
|
||||
var ticket models.Ticket
|
||||
if err := c.BodyParser(&ticket); err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
|
||||
if err := database.DB().Model(&models.Ticket{}).Where("id = ?", ticketID).Updates(ticket).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "Ticket updated successfully"})
|
||||
}
|
||||
191
internal/handlers/userHandler.go
Normal file
191
internal/handlers/userHandler.go
Normal file
@@ -0,0 +1,191 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"OpsMastery.v5/internal/database"
|
||||
"OpsMastery.v5/internal/models"
|
||||
"github.com/gofiber/fiber/v2"
|
||||
)
|
||||
|
||||
func ListUsers(c *fiber.Ctx) error {
|
||||
var users []models.User
|
||||
if err := database.DB().Unscoped().Find(&users).Error; err != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": err.Error()})
|
||||
}
|
||||
return c.JSON(users)
|
||||
}
|
||||
|
||||
func GetUserByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, id).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
func DeleteUserByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
userIDParsed, err := strconv.ParseUint(id, 10, 32)
|
||||
if err != nil {
|
||||
return c.Status(http.StatusBadRequest).JSON(fiber.Map{"error": "Invalid user ID"})
|
||||
}
|
||||
|
||||
result := database.DB().Unscoped().Delete(&models.User{}, userIDParsed)
|
||||
if result.RowsAffected == 0 {
|
||||
return c.Status(http.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
if result.Error != nil {
|
||||
return c.Status(http.StatusInternalServerError).JSON(fiber.Map{"error": result.Error.Error()})
|
||||
}
|
||||
return c.Status(http.StatusOK).JSON(fiber.Map{"message": "User deleted successfully"})
|
||||
}
|
||||
|
||||
func UpdateUserByID(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, id).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
var input models.User
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
// Only update fields if present in input
|
||||
if input.Name != "" {
|
||||
user.Name = input.Name
|
||||
}
|
||||
if input.Username != "" {
|
||||
user.Username = input.Username
|
||||
}
|
||||
if input.Address != "" {
|
||||
user.Address = input.Address
|
||||
}
|
||||
if input.PhoneNumber != "" {
|
||||
user.PhoneNumber = input.PhoneNumber
|
||||
}
|
||||
if input.Role != "" {
|
||||
user.Role = input.Role
|
||||
}
|
||||
if input.Email != "" {
|
||||
user.Email = input.Email
|
||||
}
|
||||
if input.Password != "" {
|
||||
user.Password = input.Password // Consider hashing here!
|
||||
}
|
||||
// ...handle other fields as needed...
|
||||
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not update user"})
|
||||
}
|
||||
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
func SetUserRole(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var user models.User
|
||||
|
||||
if err := database.DB().First(&user, id).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
var input struct {
|
||||
Role string `json:"role"`
|
||||
}
|
||||
if err := c.BodyParser(&input); err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid input"})
|
||||
}
|
||||
|
||||
user.Role = input.Role
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not update user role"})
|
||||
}
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
func GetCurrentUser(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(uint)
|
||||
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "User ID not found"})
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, userID).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
func UpdateCurrentUser(c *fiber.Ctx) error {
|
||||
userID, ok := c.Locals("userID").(uint)
|
||||
if !ok {
|
||||
return c.Status(fiber.StatusUnauthorized).JSON(fiber.Map{"error": "User ID not found"})
|
||||
}
|
||||
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, userID).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
|
||||
// Parse form fields using Fiber's API
|
||||
name := c.FormValue("name")
|
||||
email := c.FormValue("email")
|
||||
// Handle file upload (if needed)
|
||||
fileHeader, err := c.FormFile("avatar")
|
||||
if err == nil {
|
||||
// Process the uploaded file
|
||||
file, err := fileHeader.Open()
|
||||
if err != nil {
|
||||
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Failed to open uploaded file"})
|
||||
}
|
||||
defer file.Close()
|
||||
// ...handle file...
|
||||
}
|
||||
|
||||
// Update user fields
|
||||
user.Name = name
|
||||
user.Email = email
|
||||
|
||||
if err := database.DB().Save(&user).Error; err != nil {
|
||||
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Could not update user"})
|
||||
}
|
||||
return c.JSON(user)
|
||||
}
|
||||
|
||||
func GetUserProfilePhoto(c *fiber.Ctx) error {
|
||||
id := c.Params("id")
|
||||
var user models.User
|
||||
if err := database.DB().First(&user, id).Error; err != nil {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "User not found"})
|
||||
}
|
||||
if len(user.ProfilePhoto) == 0 {
|
||||
return c.Status(fiber.StatusNotFound).JSON(fiber.Map{"error": "Profile photo not found"})
|
||||
}
|
||||
// You may want to detect the image type; here we default to jpeg
|
||||
c.Set("Content-Type", "image/jpeg")
|
||||
return c.Send(user.ProfilePhoto)
|
||||
}
|
||||
|
||||
func SearchUsers(c *fiber.Ctx) error {
|
||||
q := c.Query("q")
|
||||
if q == "" {
|
||||
return c.JSON([]models.User{})
|
||||
}
|
||||
normalized := strings.ToLower(q)
|
||||
var users []models.User
|
||||
database.DB().Debug().Where(
|
||||
"LOWER(username) LIKE ? OR LOWER(email) LIKE ? OR LOWER(name) LIKE ?",
|
||||
"%"+normalized+"%", "%"+normalized+"%", "%"+normalized+"%",
|
||||
).Find(&users)
|
||||
return c.JSON(users)
|
||||
}
|
||||
Reference in New Issue
Block a user