diff --git a/backend/api/internal/database/comment_queries.go b/backend/api/internal/database/comment_queries.go index 580b1d2..f6814c8 100644 --- a/backend/api/internal/database/comment_queries.go +++ b/backend/api/internal/database/comment_queries.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "net/http" + "strconv" "time" "backend/api/internal/types" @@ -463,3 +464,163 @@ func QueryUpdateCommentContent(id int, newContent string) (int16, error) { return 200, nil } + +// CreateCommentLike creates a like relationship between a user and a comment. +// +// Parameters: +// - username: The username of the user creating the like. +// - strCommentID: The ID of the comment to like (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the comment. +func CreateCommentLike(username string, strCommentId string) (int, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse comment ID + commentId, err := strconv.Atoi(strCommentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing user id: %v", err) + } + + // verify comment exists + _, err = QueryComment(commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the comment exists: %v", err) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM CommentLikes WHERE user_id = ? AND comment_id = ? + )` + err = DB.QueryRow(query, user_id, commentId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + // like already exists, but we return success to keep it idempotent + return http.StatusOK, nil + } + + // insert the like + insertQuery := `INSERT INTO CommentLikes (user_id, comment_id) VALUES (?, ?)` + _, err = DB.Exec(insertQuery, user_id, commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to insert comment like: %v", err) + } + + // update the likes column + updateQuery := `UPDATE Comments SET likes = likes + 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusCreated, nil +} + +// RemoveCommentLike deletes a like relationship between a user and a comment. +// +// Parameters: +// - username: The username of the user removing the like. +// - strPostId: The ID of the comment to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the comment. +func RemoveCommentLike(username string, strCommentId string) (int, error) { + // get user ID + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse post ID + commentId, err := strconv.Atoi(strCommentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing username id: %v", err) + } + + // verify post exists + _, err = QueryPost(commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the comment exists: %v", err) + } + + // perform the delete operation + deleteQuery := `DELETE FROM CommentLikes WHERE user_id = ? AND comment_id = ?` + result, err := DB.Exec(deleteQuery, user_id, commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to delete comment like: %v", err) + } + + // check if any rows were actually deleted + rowsAffected, err := result.RowsAffected() + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to check rows affected: %v", err) + } + + if rowsAffected == 0 { + // if no rows were deleted, return success to keep idempotency + return http.StatusNoContent, nil + } + + // update the likes column + updateQuery := `UPDATE Comments SET likes = likes - 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, commentId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusOK, nil +} + +// QueryCommentLike queries for a like relationship between a user and a post. +// +// Parameters: +// - username: The username of the user removing the like. +// - postID: The ID of the post to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or. +func QueryCommentLike(username string, strCommId string) (int, bool, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse post ID + commId, err := strconv.Atoi(strCommId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred parsing comment_id: %v", err) + } + + // verify post exists + _, err = QueryComment(commId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred verifying the comment exists: %v", err) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM CommentLikes WHERE user_id = ? AND comment_id = ? + )` + err = DB.QueryRow(query, user_id, commId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + return http.StatusOK, true, nil + } else { + return http.StatusOK, false, nil + } + +} diff --git a/backend/api/internal/database/create_tables.sql b/backend/api/internal/database/create_tables.sql index 28e5c08..9f844ab 100644 --- a/backend/api/internal/database/create_tables.sql +++ b/backend/api/internal/database/create_tables.sql @@ -86,7 +86,7 @@ CREATE TABLE Comments ( id INTEGER PRIMARY KEY AUTOINCREMENT, content TEXT NOT NULL, parent_comment_id INTEGER, - likes INTEGER NOT NULL, + likes INTEGER DEFAULT 0, creation_date TIMESTAMP NOT NULL, user_id INTEGER NOT NULL, FOREIGN KEY (parent_comment_id) REFERENCES Comments(id) ON DELETE CASCADE, diff --git a/backend/api/internal/database/dev.sqlite3 b/backend/api/internal/database/dev.sqlite3 index ffe85d4..eaf8fdb 100644 Binary files a/backend/api/internal/database/dev.sqlite3 and b/backend/api/internal/database/dev.sqlite3 differ diff --git a/backend/api/internal/database/post_queries.go b/backend/api/internal/database/post_queries.go index a8e1531..1a9da36 100644 --- a/backend/api/internal/database/post_queries.go +++ b/backend/api/internal/database/post_queries.go @@ -4,6 +4,7 @@ import ( "database/sql" "fmt" "net/http" + "strconv" "time" "backend/api/internal/types" @@ -159,7 +160,7 @@ func QueryPostsByUserId(userId int) ([]types.Post, int, error) { } return nil, http.StatusInternalServerError, err } - posts = append(posts, post) + posts = append(posts, post) } return posts, http.StatusOK, nil @@ -201,7 +202,169 @@ func QueryPostsByProjectId(projId int) ([]types.Post, int, error) { } return nil, http.StatusInternalServerError, err } - posts = append(posts, post) + posts = append(posts, post) } return posts, http.StatusOK, nil } + +// CreatePostLike creates a like relationship between a user and a post. +// +// Parameters: +// - username: The username of the user creating the like. +// - strPostID: The ID of the project to like (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the post. +func CreatePostLike(username string, strPostId string) (int, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse post ID + postId, err := strconv.Atoi(strPostId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing post_id id: %v", err) + } + + // verify post exists + post, err := QueryPost(postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the post exists: %v", err) + } else if post == nil { + return http.StatusNotFound, fmt.Errorf("Post ID %d does not exist", postId) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM PostLikes WHERE user_id = ? AND post_id = ? + )` + err = DB.QueryRow(query, user_id, postId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + // like already exists, but we return success to keep it idempotent + return http.StatusOK, nil + } + + // insert the like + insertQuery := `INSERT INTO PostLikes (user_id, post_id) VALUES (?, ?)` + _, err = DB.Exec(insertQuery, user_id, postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to insert post like: %v", err) + } + + // update the likes column + updateQuery := `UPDATE Posts SET likes = likes + 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusCreated, nil +} + +// RemovePostLike deletes a like relationship between a user and a post. +// +// Parameters: +// - username: The username of the user removing the like. +// - strPostID: The ID of the post to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the post. +func RemovePostLike(username string, strPostId string) (int, error) { + // get user ID + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse post ID + postId, err := strconv.Atoi(strPostId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing username id: %v", err) + } + + // verify post exists + _, err = QueryPost(postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the post exists: %v", err) + } + + // perform the delete operation + deleteQuery := `DELETE FROM PostLikes WHERE user_id = ? AND post_id = ?` + result, err := DB.Exec(deleteQuery, user_id, postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to delete post like: %v", err) + } + + // check if any rows were actually deleted + rowsAffected, err := result.RowsAffected() + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to check rows affected: %v", err) + } + + if rowsAffected == 0 { + // if no rows were deleted, return success to keep idempotency + return http.StatusNoContent, nil + } + + // update the likes column + updateQuery := `UPDATE Posts SET likes = likes - 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, postId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusOK, nil +} + +// QueryPostLike queries for a like relationship between a user and a post. +// +// Parameters: +// - username: The username of the user removing the like. +// - postID: The ID of the post to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or. +func QueryPostLike(username string, strPostId string) (int, bool, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse post ID + postId, err := strconv.Atoi(strPostId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred parsing post_id: %v", err) + } + + // verify post exists + _, err = QueryPost(postId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred verifying the post exists: %v", err) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM PostLikes WHERE user_id = ? AND post_id = ? + )` + err = DB.QueryRow(query, user_id, postId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + return http.StatusOK, true, nil + } else { + return http.StatusOK, false, nil + } + +} diff --git a/backend/api/internal/database/project_queries.go b/backend/api/internal/database/project_queries.go index a1906b9..4cff323 100644 --- a/backend/api/internal/database/project_queries.go +++ b/backend/api/internal/database/project_queries.go @@ -429,3 +429,163 @@ func RemoveProjectFollow(username string, projectID string) (int, error) { return http.StatusOK, nil } + +// CreateProjectLike creates a like relationship between a user and a project. +// +// Parameters: +// - username: The username of the user creating the like. +// - projectID: The ID of the project to like (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the project. +func CreateProjectLike(username string, strProjId string) (int, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse project ID + projId, err := strconv.Atoi(strProjId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing proj_id: %v", err) + } + + // verify project exists + _, err = QueryProject(projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the project exists: %v", err) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM ProjectLikes WHERE user_id = ? AND project_id = ? + )` + err = DB.QueryRow(query, user_id, projId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + // like already exists, but we return success to keep it idempotent + return http.StatusOK, nil + } + + // insert the like + insertQuery := `INSERT INTO ProjectLikes (user_id, project_id) VALUES (?, ?)` + _, err = DB.Exec(insertQuery, user_id, projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to insert project like: %v", err) + } + + // update the likes column + updateQuery := `UPDATE Projects SET likes = likes + 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusCreated, nil +} + +// RemoveProjectLike deletes a like relationship between a user and a project. +// +// Parameters: +// - username: The username of the user removing the like. +// - projectID: The ID of the project to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or the user is not liking the project. +func RemoveProjectLike(username string, strProjId string) (int, error) { + // get user ID + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse project ID + projId, err := strconv.Atoi(strProjId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred parsing username id: %v", err) + } + + // verify project exists + _, err = QueryProject(projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("An error occurred verifying the project exists: %v", err) + } + + // perform the delete operation + deleteQuery := `DELETE FROM ProjectLikes WHERE user_id = ? AND project_id = ?` + result, err := DB.Exec(deleteQuery, user_id, projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to delete project like: %v", err) + } + + // check if any rows were actually deleted + rowsAffected, err := result.RowsAffected() + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to check rows affected: %v", err) + } + + if rowsAffected == 0 { + // if no rows were deleted, return success to keep idempotency + return http.StatusNoContent, nil + } + + // update the likes column + updateQuery := `UPDATE Projects SET likes = likes - 1 WHERE id = ?` + _, err = DB.Exec(updateQuery, projId) + if err != nil { + return http.StatusInternalServerError, fmt.Errorf("Failed to update likes count: %v", err) + } + + return http.StatusOK, nil +} + +// QueryProjectLike queries for a like relationship between a user and a project. +// +// Parameters: +// - username: The username of the user removing the like. +// - projectID: The ID of the project to unlike (as a string, converted internally). +// +// Returns: +// - int: HTTP-like status code indicating the result of the operation. +// - error: An error if the operation fails or. +func QueryProjectLike(username string, strProjId string) (int, bool, error) { + // get user ID from username, implicitly checks if user exists + user_id, err := GetUserIdByUsername(username) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred getting id for username: %v", err) + } + + // parse project ID + projId, err := strconv.Atoi(strProjId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred parsing proj_id: %v", err) + } + + // verify project exists + _, err = QueryProject(projId) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred verifying the project exists: %v", err) + } + + // check if the like already exists + var exists bool + query := `SELECT EXISTS ( + SELECT 1 FROM ProjectLikes WHERE user_id = ? AND project_id = ? + )` + err = DB.QueryRow(query, user_id, projId).Scan(&exists) + if err != nil { + return http.StatusInternalServerError, false, fmt.Errorf("An error occurred checking like existence: %v", err) + } + if exists { + return http.StatusOK, true, nil + } else { + return http.StatusOK, false, nil + } + +} diff --git a/backend/api/internal/handlers/comment_routes.go b/backend/api/internal/handlers/comment_routes.go index 7ba0565..4f37c0e 100644 --- a/backend/api/internal/handlers/comment_routes.go +++ b/backend/api/internal/handlers/comment_routes.go @@ -411,3 +411,54 @@ func UpdateCommentContent(context *gin.Context) { "comment": updatedComment, }) } + +// LikeComment handles POST requests to like a comment. +// It expects the `username` and `comment_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func LikeComment(context *gin.Context) { + username := context.Param("username") + commentId := context.Param("comment_id") + + httpcode, err := database.CreateCommentLike(username, commentId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to like comment: %v", err)) + return + } + context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v likes %v", username, commentId)}) +} + +// UnlikeComment handles POST requests to unlike a comment. +// It expects the `username` and `comment_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func UnlikeComment(context *gin.Context) { + username := context.Param("username") + commentId := context.Param("comment_id") + + httpcode, err := database.RemoveCommentLike(username, commentId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to unlike comment: %v", err)) + return + } + context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v unliked %v", username, commentId)}) +} + +// IsCommentLiked handles GET requests to query for a comment like. +// It expects the `username` and `comment_id` parameters in the URL. +// Returns: +// - Appropriate error code for database failures or invalid input. +// On success, responds with a 200 OK status and a status message. +func IsCommentLiked(context *gin.Context) { + username := context.Param("username") + commentId := context.Param("comment_id") + + httpcode, exists, err := database.QueryCommentLike(username, commentId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to query for comment like: %v", err)) + return + } + context.JSON(httpcode, gin.H{"status": exists}) +} diff --git a/backend/api/internal/handlers/post_routes.go b/backend/api/internal/handlers/post_routes.go index e2c0e46..9b68aca 100644 --- a/backend/api/internal/handlers/post_routes.go +++ b/backend/api/internal/handlers/post_routes.go @@ -268,3 +268,54 @@ func UpdatePostInfo(context *gin.Context) { "post": updatedPost, }) } + +// LikePost handles POST requests to like a post. +// It expects the `username` and `post_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func LikePost(context *gin.Context) { + username := context.Param("username") + postId := context.Param("post_id") + + httpcode, err := database.CreatePostLike(username, postId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to like post: %v", err)) + return + } + context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v likes %v", username, postId)}) +} + +// UnlikePost handles POST requests to unlike a post. +// It expects the `username` and `post_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func UnlikePost(context *gin.Context) { + username := context.Param("username") + postId := context.Param("post_id") + + httpcode, err := database.RemovePostLike(username, postId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to unlike post: %v", err)) + return + } + context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v unliked %v", username, postId)}) +} + +// IsPostLiked handles GET requests to query for a post like. +// It expects the `username` and `post_id` parameters in the URL. +// Returns: +// - Appropriate error code for database failures or invalid input. +// On success, responds with a 200 OK status and a status message. +func IsPostLiked(context *gin.Context) { + username := context.Param("username") + postId := context.Param("post_id") + + httpcode, exists, err := database.QueryPostLike(username, postId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to query for post like: %v", err)) + return + } + context.JSON(httpcode, gin.H{"status": exists}) +} diff --git a/backend/api/internal/handlers/project_routes.go b/backend/api/internal/handlers/project_routes.go index d34eb78..54f8d06 100644 --- a/backend/api/internal/handlers/project_routes.go +++ b/backend/api/internal/handlers/project_routes.go @@ -330,3 +330,54 @@ func UnfollowProject(context *gin.Context) { } context.JSON(http.StatusOK, gin.H{"message": fmt.Sprintf("%v unfollowed %v", username, projectId)}) } + +// LikeProject handles POST requests to like a project. +// It expects the `username` and `project_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func LikeProject(context *gin.Context) { + username := context.Param("username") + projectId := context.Param("project_id") + + httpcode, err := database.CreateProjectLike(username, projectId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to like project: %v", err)) + return + } + context.JSON(httpcode, gin.H{"message": fmt.Sprintf("%v likes %v", username, projectId)}) +} + +// UnlikeProject handles POST requests to unlike a project. +// It expects the `username` and `project_id` parameters in the URL. +// Returns: +// - Appropriate error code (404 if missing data, 500 if error) for database failures or invalid input. +// On success, responds with a 200 OK status and a confirmation message. +func UnlikeProject(context *gin.Context) { + username := context.Param("username") + projectId := context.Param("project_id") + + httpcode, err := database.RemoveProjectLike(username, projectId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to unlike project: %v", err)) + return + } + context.JSON(httpcode, gin.H{"message": fmt.Sprintf("%v unliked %v", username, projectId)}) +} + +// IsProjectLiked handles GET requests to query for a project like. +// It expects the `username` and `project_id` parameters in the URL. +// Returns: +// - Appropriate error code for database failures or invalid input. +// On success, responds with a 200 OK status and a status message. +func IsProjectLiked(context *gin.Context) { + username := context.Param("username") + projectId := context.Param("project_id") + + httpcode, exists, err := database.QueryProjectLike(username, projectId) + if err != nil { + RespondWithError(context, httpcode, fmt.Sprintf("Failed to query for project like: %v", err)) + return + } + context.JSON(httpcode, gin.H{"status": exists}) +} diff --git a/backend/api/main.go b/backend/api/main.go index 98383f2..6af8ed8 100644 --- a/backend/api/main.go +++ b/backend/api/main.go @@ -20,6 +20,10 @@ func HealthCheck(context *gin.Context) { } func main() { + if DEBUG { + gin.SetMode(gin.DebugMode) + } + log.SetOutput(os.Stdout) logger.InitLogger() router := gin.Default() @@ -35,57 +39,64 @@ func main() { router.GET("/health", HealthCheck) - // Users router.GET("/users/:username", handlers.GetUserByUsername) router.POST("/users", handlers.CreateUser) router.PUT("/users/:username", handlers.UpdateUserInfo) router.DELETE("/users/:username", handlers.DeleteUser) router.GET("/users/:username/followers", handlers.GetUsersFollowers) - router.GET("/users/:username/following", handlers.GetUsersFollowing) + router.GET("/users/:username/follows", handlers.GetUsersFollowing) router.GET("/users/:username/followers/usernames", handlers.GetUsersFollowersUsernames) - router.GET("/users/:username/following/usernames", handlers.GetUsersFollowingUsernames) + router.GET("/users/:username/follows/usernames", handlers.GetUsersFollowingUsernames) - router.POST("/users/:username/follow/:target_user", handlers.FollowUser) - router.POST("/users/:username/unfollow/:target_user", handlers.UnfollowUser) + router.POST("/users/:username/follow/:new_follow", handlers.FollowUser) + router.POST("/users/:username/unfollow/:unfollow", handlers.UnfollowUser) - // Projects router.GET("/projects/:project_id", handlers.GetProjectById) router.POST("/projects", handlers.CreateProject) router.PUT("/projects/:project_id", handlers.UpdateProjectInfo) router.DELETE("/projects/:project_id", handlers.DeleteProject) - - router.GET("/users/:user_id/projects", handlers.GetProjectsByUserId) + router.GET("/projects/by-user/:user_id", handlers.GetProjectsByUserId) router.GET("/projects/:project_id/followers", handlers.GetProjectFollowers) - router.GET("/users/:username/projects/following", handlers.GetProjectFollowing) + router.GET("/projects/follows/:username", handlers.GetProjectFollowing) router.GET("/projects/:project_id/followers/usernames", handlers.GetProjectFollowersUsernames) - router.GET("/users/:username/projects/following/names", handlers.GetProjectFollowingNames) + router.GET("/projects/follows/:username/names", handlers.GetProjectFollowingNames) - router.POST("/users/:username/follow/project/:project_id", handlers.FollowProject) - router.POST("/users/:username/unfollow/project/:project_id", handlers.UnfollowProject) + router.POST("/projects/:username/follow/:project_id", handlers.FollowProject) + router.POST("/projects/:username/unfollow/:project_id", handlers.UnfollowProject) + + router.POST("/projects/:username/likes/:project_id", handlers.LikeProject) + router.POST("/projects/:username/unlikes/:project_id", handlers.UnlikeProject) + router.GET("/projects/does-like/:username/:project_id", handlers.IsProjectLiked) - // Posts router.GET("/posts/:post_id", handlers.GetPostById) router.POST("/posts", handlers.CreatePost) router.PUT("/posts/:post_id", handlers.UpdatePostInfo) router.DELETE("/posts/:post_id", handlers.DeletePost) - router.GET("/users/:user_id/posts", handlers.GetPostsByUserId) - router.GET("/projects/:project_id/posts", handlers.GetPostsByProjectId) + router.GET("/posts/by-user/:user_id", handlers.GetPostsByUserId) + router.GET("/posts/by-project/:project_id", handlers.GetPostsByProjectId) + + router.POST("/posts/:username/likes/:post_id", handlers.LikePost) + router.POST("/posts/:username/unlikes/:post_id", handlers.UnlikePost) + router.GET("/posts/does-like/:username/:post_id", handlers.IsPostLiked) - // Comments - router.POST("/posts/:post_id/comments", handlers.CreateCommentOnPost) - router.POST("/projects/:project_id/comments", handlers.CreateCommentOnProject) - router.POST("/comments/:comment_id/reply", handlers.CreateCommentOnComment) + router.POST("/comments/for-post/:post_id", handlers.CreateCommentOnPost) + router.POST("/comments/for-project/:project_id", handlers.CreateCommentOnProject) + router.POST("/comments/for-comment/:comment_id", handlers.CreateCommentOnComment) router.GET("/comments/:comment_id", handlers.GetCommentById) router.PUT("/comments/:comment_id", handlers.UpdateCommentContent) router.DELETE("/comments/:comment_id", handlers.DeleteComment) - router.GET("/users/:user_id/comments", handlers.GetCommentsByUserId) - router.GET("/posts/:post_id/comments", handlers.GetCommentsByPostId) - router.GET("/projects/:project_id/comments", handlers.GetCommentsByProjectId) - router.GET("/comments/:comment_id/replies", handlers.GetCommentsByCommentId) + router.GET("/comments/by-user/:user_id", handlers.GetCommentsByUserId) + router.GET("/comments/by-post/:post_id", handlers.GetCommentsByPostId) + router.GET("/comments/by-project/:project_id", handlers.GetCommentsByProjectId) + router.GET("/comments/by-comment/:comment_id", handlers.GetCommentsByCommentId) + + router.POST("/comments/:username/likes/:comment_id", handlers.LikeComment) + router.POST("/comments/:username/unlikes/:comment_id", handlers.UnlikeComment) + router.GET("/comments/does-like/:username/:comment_id", handlers.IsCommentLiked) var dbinfo, dbtype string if DEBUG {