added delete contact route

This commit is contained in:
slawk0
2025-02-01 17:05:43 +01:00
parent 86c43fffc0
commit cd83870ab4
8 changed files with 163 additions and 37 deletions

17
.idea/dataSources.xml generated
View File

@@ -1,17 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
<data-source source="LOCAL" name="postgres@192.168.0.47" uuid="fc534921-0a7d-4120-bac3-e6f8ac7fb7a1">
<driver-ref>postgresql</driver-ref>
<synchronize>true</synchronize>
<jdbc-driver>org.postgresql.Driver</jdbc-driver>
<jdbc-url>jdbc:postgresql://192.168.0.47:5432/postgres</jdbc-url>
<jdbc-additional-properties>
<property name="com.intellij.clouds.kubernetes.db.host.port" />
<property name="com.intellij.clouds.kubernetes.db.enabled" value="false" />
<property name="com.intellij.clouds.kubernetes.db.container.port" />
</jdbc-additional-properties>
<working-dir>$ProjectFileDir$</working-dir>
</data-source>
</component>
</project>

2
.idea/sqldialects.xml generated
View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="SqlDialectMappings"> <component name="SqlDialectMappings">
<file url="file://$PROJECT_DIR$/database/db.go" dialect="GenericSQL" /> <file url="file://$PROJECT_DIR$/database/auth.go" dialect="PostgreSQL" />
<file url="PROJECT" dialect="PostgreSQL" /> <file url="PROJECT" dialect="PostgreSQL" />
</component> </component>
</project> </project>

View File

@@ -52,6 +52,9 @@ func InsertUser(db *sql.DB, username string, passwordHash string) (string, error
if err != nil { if err != nil {
return "", fmt.Errorf("error inserting user: %v", err) return "", fmt.Errorf("error inserting user: %v", err)
} }
log.Printf("Inserted user: %v", username)
return userId, err return userId, err
} }
@@ -64,8 +67,8 @@ func GetPasswordHash(db *sql.DB, username string) (string, error) {
var passwordHash string var passwordHash string
err := db.QueryRow(query, username).Scan(&passwordHash) err := db.QueryRow(query, username).Scan(&passwordHash)
if err != nil { if err != nil {
fmt.Printf("error getting password: %v\n", err) fmt.Printf("error getting password hash: %v\n", err)
return "", fmt.Errorf("error getting password: %v", err) return "", fmt.Errorf("error getting password hash: %v", err)
} }
return passwordHash, err return passwordHash, err

88
database/contacts.go Normal file
View File

@@ -0,0 +1,88 @@
package database
import (
"database/sql"
"errors"
"fmt"
"github.com/google/uuid"
"log"
)
func DeleteContact(db *sql.DB, userID uuid.UUID, conversationID uuid.UUID) (string, error) {
// Check conversation type
var conversationType string
err := db.QueryRow(
"SELECT conversation_type FROM Conversations WHERE conversation_id = $1",
conversationID,
).Scan(&conversationType)
if err != nil {
if errors.Is(err, sql.ErrNoRows) {
return "no conversation found for this id", nil
}
return "", fmt.Errorf("error checking conversation type: %w", err)
}
if conversationType == "group" {
// Delete from Contacts
res, err := db.Exec(
"DELETE FROM Contacts WHERE conversation_id = $1 AND user_id = $2",
conversationID,
userID,
)
if err != nil {
return "", fmt.Errorf("error deleting contact: %w", err)
}
rowsAffected, err := res.RowsAffected()
if err != nil {
return "", fmt.Errorf("error checking contact deletion: %w", err)
}
if rowsAffected == 0 {
return fmt.Sprintf("no matching contact found with conversation id: %s, user id: %s", conversationID, userID), nil
}
// Delete from Memberships
res, err = db.Exec(
"DELETE FROM Memberships WHERE conversation_id = $1 AND user_id = $2",
conversationID,
userID,
)
if err != nil {
return "", fmt.Errorf("error deleting membership: %w", err)
}
rowsAffected, err = res.RowsAffected()
if err != nil {
return "", fmt.Errorf("error checking membership deletion: %w", err)
}
if rowsAffected == 0 {
return "", fmt.Errorf("no matching membership found with conversation id: %s, user id: %s", conversationID, userID)
}
log.Printf("Successfully removed user %s from group %s", userID, conversationID)
} else {
// Handle direct conversation
res, err := db.Exec(
"DELETE FROM Contacts WHERE user_id = $1 AND conversation_id = $2",
userID,
conversationID,
)
if err != nil {
log.Printf("Error deleting contact: %v", err)
return "", fmt.Errorf("error deleting contact: %w", err)
}
rowsAffected, err := res.RowsAffected()
if err != nil {
log.Printf("Error checking contact deletion: %v", err)
return "", fmt.Errorf("error checking contact deletion: %w", err)
}
if rowsAffected == 0 {
return fmt.Sprintf("no matching contact found with user id: %s, conversation id: %s", userID, conversationID), nil
}
log.Printf("Successfully deleted contact for user %s in conversation %s", userID, conversationID)
}
return "", nil
}

View File

@@ -9,27 +9,34 @@ import (
"relay-server/config" "relay-server/config"
"relay-server/database" "relay-server/database"
"relay-server/helpers" "relay-server/helpers"
"relay-server/model"
"time" "time"
) )
func Signup(c *fiber.Ctx) error { func Signup(c *fiber.Ctx) error {
type SignupStruct struct {
Username string `json:"username" xml:"username" form:"username"`
Password string `json:"password" xml:"password" form:"password"`
}
db := database.DB db := database.DB
u := new(model.SignupStruct) u := new(SignupStruct)
if err := c.BodyParser(u); err != nil { if err := c.BodyParser(u); err != nil {
return err return err
} }
// Checks if username or passwords are empty // Checks if username or passwords are empty
if u.Username == "" { if u.Username == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"})
} else if u.Password == "" { }
if u.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"})
} }
// Checks if passwords or username have valid length and characters // Checks if passwords or username have valid length and characters
if !helpers.IsValidPassword(u.Password) { if !helpers.IsValidPassword(u.Password) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"})
} else if !helpers.IsValidUsername(u.Username) { }
if !helpers.IsValidUsername(u.Username) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"})
} }
@@ -74,8 +81,14 @@ func Signup(c *fiber.Ctx) error {
} }
func Login(c *fiber.Ctx) error { func Login(c *fiber.Ctx) error {
type loginStruct struct {
Username string `json:"username" xml:"username" form:"username"`
Password string `json:"password" xml:"password" form:"password"`
}
db := database.DB db := database.DB
u := new(model.LoginStruct) u := new(loginStruct)
if err := c.BodyParser(u); err != nil { if err := c.BodyParser(u); err != nil {
return err return err
@@ -84,14 +97,16 @@ func Login(c *fiber.Ctx) error {
// Checks if username or passwords are empty // Checks if username or passwords are empty
if u.Username == "" { if u.Username == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "username is empty"})
} else if u.Password == "" { }
if u.Password == "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "password is empty"})
} }
// Checks if username or passwords have valid length and characters // Checks if username or passwords have valid length and characters
if !helpers.IsValidUsername(u.Username) { if !helpers.IsValidUsername(u.Username) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid username"})
} else if !helpers.IsValidPassword(u.Password) { }
if !helpers.IsValidPassword(u.Password) {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"}) return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "invalid password"})
} }

42
handlers/contacts.go Normal file
View File

@@ -0,0 +1,42 @@
package handlers
import (
"github.com/gofiber/fiber/v2"
"github.com/google/uuid"
"log"
"relay-server/database"
)
func DeleteContact(c *fiber.Ctx) error {
type params struct {
ContactId uuid.UUID `params:"contact_id"`
ConversationId uuid.UUID `params:"conversation_id"`
}
p := new(params)
if err := c.ParamsParser(p); err != nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"error": "Invalid params"})
}
db := database.DB
if p.ContactId == uuid.Nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "contact_id is empty"})
}
if p.ConversationId == uuid.Nil {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": "conversation_id is empty"})
}
msg, err := database.DeleteContact(db, p.ContactId, p.ConversationId)
if err != nil {
log.Println(err)
return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{"error": "Failed to delete contact"})
}
if msg != "" {
return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{"message": msg})
}
log.Println("Contact deleted")
return c.Status(fiber.StatusOK).JSON(fiber.Map{"message": "Contact deleted"})
}

View File

@@ -2,16 +2,6 @@ package model
import "github.com/golang-jwt/jwt/v5" import "github.com/golang-jwt/jwt/v5"
type LoginStruct struct {
Username string `json:"username" xml:"username" form:"username"`
Password string `json:"password" xml:"password" form:"password"`
}
type SignupStruct struct {
Username string `json:"username" xml:"username" form:"username"`
Password string `json:"password" xml:"password" form:"password"`
}
type UserClaims struct { type UserClaims struct {
Username string `json:"username"` Username string `json:"username"`
UserId string `json:"user_id"` UserId string `json:"user_id"`

View File

@@ -12,10 +12,15 @@ func SetupRoutes(app *fiber.App) {
return c.SendString("Hello, World!") return c.SendString("Hello, World!")
}) })
api := app.Group("/api", logger.New()) api := app.Group("/api", logger.New())
chat := api.Group("/chat", middleware.Protected(), logger.New())
// Auth group // Auth group
auth := api.Group("/auth", middleware.Protected(), handlers.ValidateToken) auth := api.Group("/auth", middleware.Protected(), handlers.ValidateToken)
auth.Post("/signup", handlers.Signup) auth.Post("/signup", handlers.Signup)
auth.Post("/login", handlers.Login) auth.Post("/login", handlers.Login)
auth.Get("/validate", handlers.ValidateToken) auth.Get("/validate", handlers.ValidateToken)
// Contacts group
contacts := chat.Group("/contacts", middleware.Protected(), logger.New())
contacts.Delete("/:contact_id/:conversation_id", handlers.DeleteContact)
} }