diff --git a/api/server.go b/api/server.go index 044098e1..ff3b9428 100644 --- a/api/server.go +++ b/api/server.go @@ -418,6 +418,12 @@ func NewApiServer(config config.Config) *ApiServer { g.Get("/users/:userId/developer-apps", app.v1UsersDeveloperApps) g.Get("/users/:userId/withdrawals/download", app.requireAuthForUserId, app.v1UsersWithdrawalsDownloadCsv) g.Get("/users/:userId/withdrawals/download/json", app.requireAuthForUserId, app.v1UsersWithdrawalsDownloadJson) + g.Post("/users/:userId/follow", app.requireAuthMiddleware, app.postV1UserFollow) + g.Delete("/users/:userId/follow", app.requireAuthMiddleware, app.deleteV1UserFollow) + g.Post("/users/:userId/subscribe", app.requireAuthMiddleware, app.postV1UserSubscribe) + g.Delete("/users/:userId/subscribe", app.requireAuthMiddleware, app.deleteV1UserSubscribe) + g.Post("/users/:userId/mute", app.requireAuthMiddleware, app.postV1UserMute) + g.Delete("/users/:userId/mute", app.requireAuthMiddleware, app.deleteV1UserMute) // Tracks g.Get("/tracks", app.v1Tracks) @@ -444,8 +450,14 @@ func NewApiServer(config config.Config) *ApiServer { g.Get("/tracks/:trackId/remixes", app.v1TrackRemixes) g.Get("/tracks/:trackId/reposts", app.v1TrackReposts) g.Post("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.postV1TrackRepost) + g.Delete("/tracks/:trackId/reposts", app.requireAuthMiddleware, app.deleteV1TrackRepost) g.Get("/tracks/:trackId/stems", app.v1TrackStems) g.Get("/tracks/:trackId/favorites", app.v1TrackFavorites) + g.Post("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.postV1TrackFavorite) + g.Delete("/tracks/:trackId/favorites", app.requireAuthMiddleware, app.deleteV1TrackFavorite) + g.Post("/tracks/:trackId/shares", app.requireAuthMiddleware, app.postV1TrackShare) + g.Post("/tracks/:trackId/downloads", app.requireAuthMiddleware, app.postV1TrackDownload) + g.Delete("/tracks/:trackId", app.requireAuthMiddleware, app.deleteV1Track) g.Get("/tracks/:trackId/comments", app.v1TrackComments) g.Get("/tracks/:trackId/comment_count", app.v1TrackCommentCount) g.Get("/tracks/:trackId/comment-count", app.v1TrackCommentCount) @@ -470,7 +482,12 @@ func NewApiServer(config config.Config) *ApiServer { g.Get("/playlists/:playlistId", app.v1Playlist) g.Get("/playlists/:playlistId/stream", app.v1PlaylistStream) g.Get("/playlists/:playlistId/reposts", app.v1PlaylistReposts) + g.Post("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.postV1PlaylistRepost) + g.Delete("/playlists/:playlistId/reposts", app.requireAuthMiddleware, app.deleteV1PlaylistRepost) g.Get("/playlists/:playlistId/favorites", app.v1PlaylistFavorites) + g.Post("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.postV1PlaylistFavorite) + g.Delete("/playlists/:playlistId/favorites", app.requireAuthMiddleware, app.deleteV1PlaylistFavorite) + g.Post("/playlists/:playlistId/shares", app.requireAuthMiddleware, app.postV1PlaylistShare) g.Get("/playlists/:playlistId/tracks", app.v1PlaylistTracks) // Explore @@ -484,6 +501,9 @@ func NewApiServer(config config.Config) *ApiServer { // Developer Apps g.Get("/developer_apps/:address", app.v1DeveloperApps) g.Get("/developer-apps/:address", app.v1DeveloperApps) + g.Post("/developer-apps", app.requireAuthMiddleware, app.postV1DeveloperApp) + g.Put("/developer-apps/:address", app.requireAuthMiddleware, app.putV1DeveloperApp) + g.Delete("/developer-apps/:address", app.requireAuthMiddleware, app.deleteV1DeveloperApp) // Rewards g.Post("/rewards/claim", app.v1ClaimRewards) @@ -500,7 +520,15 @@ func NewApiServer(config config.Config) *ApiServer { // Comments g.Get("/comments/unclaimed_id", app.v1CommentsUnclaimedId) g.Get("/comments/unclaimed-id", app.v1CommentsUnclaimedId) + g.Post("/comments", app.requireAuthMiddleware, app.postV1Comment) g.Get("/comments/:commentId", app.v1Comment) + g.Put("/comments/:commentId", app.requireAuthMiddleware, app.putV1Comment) + g.Delete("/comments/:commentId", app.requireAuthMiddleware, app.deleteV1Comment) + g.Post("/comments/:commentId/react", app.requireAuthMiddleware, app.postV1CommentReact) + g.Delete("/comments/:commentId/react", app.requireAuthMiddleware, app.deleteV1CommentReact) + g.Post("/comments/:commentId/pin", app.requireAuthMiddleware, app.postV1CommentPin) + g.Delete("/comments/:commentId/pin", app.requireAuthMiddleware, app.deleteV1CommentPin) + g.Post("/comments/:commentId/report", app.requireAuthMiddleware, app.postV1CommentReport) // Tips g.Get("/tips", app.v1Tips) diff --git a/api/v1_comments.go b/api/v1_comments.go index 13197dc5..38c34438 100644 --- a/api/v1_comments.go +++ b/api/v1_comments.go @@ -1,9 +1,18 @@ package api import ( + "encoding/json" + "strconv" + "time" + "api.audius.co/api/dbv1" + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) type GetCommentsParams struct { @@ -12,6 +21,29 @@ type GetCommentsParams struct { Offset int `query:"offset" default:"0" validate:"min=0"` } +type CreateCommentRequest struct { + EntityType string `json:"entityType"` + EntityId string `json:"entityId"` + Body string `json:"body"` + CommentId *int `json:"commentId,omitempty"` + ParentId *int `json:"parentId,omitempty"` +} + +type UpdateCommentRequest struct { + TrackId string `json:"trackId"` + Body string `json:"body"` +} + +type ReactCommentRequest struct { + TrackId string `json:"trackId"` + IsLiked bool `json:"isLiked"` +} + +type PinCommentRequest struct { + TrackId string `json:"trackId"` + IsPin bool `json:"isPin"` +} + func (app *ApiServer) queryFullComments( c *fiber.Ctx, sql string, @@ -88,3 +120,423 @@ func (app *ApiServer) queryFullComments( }, }) } + +func (app *ApiServer) postV1Comment(c *fiber.Ctx) error { + userID := app.getUserId(c) + + var req CreateCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + commentID := 0 + if req.CommentId != nil { + commentID = *req.CommentId + } else { + // Generate random comment ID if not provided + commentID = int(nonce % 1000000) + } + + // Build metadata + metadataMap := map[string]interface{}{ + "entity_type": req.EntityType, + "entity_id": req.EntityId, + "body": req.Body, + } + if req.ParentId != nil { + metadataMap["parent_id"] = *req.ParentId + } + + metadataObj := map[string]interface{}{ + "cid": "", + "data": metadataMap, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Create, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment create transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to create comment", + }) + } + + encodedCommentID, _ := trashid.EncodeHashId(commentID) + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + "comment_id": encodedCommentID, + }) +} + +func (app *ApiServer) putV1Comment(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + var req UpdateCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "cid": "", + "data": map[string]interface{}{ + "entity_id": req.TrackId, + "body": req.Body, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Update, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment update transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to update comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1Comment(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Delete, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment delete transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to delete comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) postV1CommentReact(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + var req ReactCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + action := indexer.Action_React + if !req.IsLiked { + action = indexer.Action_Unreact + } + + metadataObj := map[string]interface{}{ + "cid": "", + "data": map[string]interface{}{ + "entity_id": req.TrackId, + "entity_type": indexer.Entity_Track, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: action, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment react transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to react to comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1CommentReact(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + var req ReactCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "cid": "", + "data": map[string]interface{}{ + "entity_id": req.TrackId, + "entity_type": indexer.Entity_Track, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Unreact, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment unreact transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unreact to comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) postV1CommentPin(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + var req PinCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + action := indexer.Action_Pin + if !req.IsPin { + action = indexer.Action_Unpin + } + + metadataObj := map[string]interface{}{ + "cid": "", + "data": map[string]interface{}{ + "entity_id": req.TrackId, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: action, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment pin transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to pin comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1CommentPin(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + var req PinCommentRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "cid": "", + "data": map[string]interface{}{ + "entity_id": req.TrackId, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Unpin, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment unpin transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unpin comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) postV1CommentReport(c *fiber.Ctx) error { + userID := app.getUserId(c) + commentID, err := trashid.DecodeHashId(c.Params("commentId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(commentID), + Action: indexer.Action_Report, + EntityType: indexer.Entity_Comment, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send comment report transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to report comment", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_developer_apps.go b/api/v1_developer_apps.go index aaa0c768..31788897 100644 --- a/api/v1_developer_apps.go +++ b/api/v1_developer_apps.go @@ -1,12 +1,35 @@ package api import ( + "encoding/json" + "strconv" "strings" + "time" "api.audius.co/api/dbv1" + "api.audius.co/indexer" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" + "go.uber.org/zap" ) +type CreateDeveloperAppRequest struct { + Name string `json:"name"` + Description string `json:"description"` + ImageUrl string `json:"imageUrl"` + AppSignature struct { + Message string `json:"message"` + Signature string `json:"signature"` + } `json:"appSignature"` +} + +type UpdateDeveloperAppRequest struct { + Name string `json:"name"` + Description string `json:"description"` + ImageUrl string `json:"imageUrl"` +} + func (app *ApiServer) v1DeveloperApps(c *fiber.Ctx) error { address := c.Params("address") if address == "" { @@ -34,3 +57,145 @@ func (app *ApiServer) v1DeveloperApps(c *fiber.Ctx) error { }) } + +func (app *ApiServer) postV1DeveloperApp(c *fiber.Ctx) error { + userID := app.getUserId(c) + + var req CreateDeveloperAppRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "name": req.Name, + "description": req.Description, + "image_url": req.ImageUrl, + "app_signature": map[string]interface{}{ + "message": req.AppSignature.Message, + "signature": req.AppSignature.Signature, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: 0, // Contract requires uint, but we don't actually need this field for this action + Action: indexer.Action_Create, + EntityType: indexer.Entity_DeveloperApp, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send developer app create transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to create developer app", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) putV1DeveloperApp(c *fiber.Ctx) error { + userID := app.getUserId(c) + address := c.Params("address") + + var req UpdateDeveloperAppRequest + if err := c.BodyParser(&req); err != nil { + return c.Status(fiber.StatusBadRequest).JSON(fiber.Map{ + "error": "Invalid request body", + }) + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "address": address, + "name": req.Name, + "description": req.Description, + "image_url": req.ImageUrl, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: 0, // Contract requires uint, but we don't actually need this field for this action + Action: indexer.Action_Update, + EntityType: indexer.Entity_DeveloperApp, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send developer app update transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to update developer app", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1DeveloperApp(c *fiber.Ctx) error { + userID := app.getUserId(c) + address := c.Params("address") + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + metadataObj := map[string]interface{}{ + "address": address, + } + metadataBytes, _ := json.Marshal(metadataObj) + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: 0, // Contract requires uint, but we don't actually need this field for this action + Action: indexer.Action_Delete, + EntityType: indexer.Entity_DeveloperApp, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: string(metadataBytes), + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send developer app delete transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to delete developer app", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_playlist_favorites.go b/api/v1_playlist_favorites.go index 9322244d..f14a9804 100644 --- a/api/v1_playlist_favorites.go +++ b/api/v1_playlist_favorites.go @@ -1,9 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1PlaylistFavorites(c *fiber.Ctx) error { @@ -30,3 +37,79 @@ func (app *ApiServer) v1PlaylistFavorites(c *fiber.Ctx) error { "playlistId": playlistId, }) } + +func (app *ApiServer) postV1PlaylistFavorite(c *fiber.Ctx) error { + userID := app.getUserId(c) + playlistID, err := trashid.DecodeHashId(c.Params("playlistId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(playlistID), + Action: indexer.Action_Save, + EntityType: indexer.Entity_Playlist, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send playlist favorite transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to favorite playlist", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1PlaylistFavorite(c *fiber.Ctx) error { + userID := app.getUserId(c) + playlistID, err := trashid.DecodeHashId(c.Params("playlistId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(playlistID), + Action: indexer.Action_Unsave, + EntityType: indexer.Entity_Playlist, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send playlist unfavorite transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unfavorite playlist", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_playlist_reposts.go b/api/v1_playlist_reposts.go index 2ad9d6db..695a2745 100644 --- a/api/v1_playlist_reposts.go +++ b/api/v1_playlist_reposts.go @@ -1,9 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1PlaylistReposts(c *fiber.Ctx) error { @@ -30,3 +37,79 @@ func (app *ApiServer) v1PlaylistReposts(c *fiber.Ctx) error { "playlistId": playlistId, }) } + +func (app *ApiServer) postV1PlaylistRepost(c *fiber.Ctx) error { + userID := app.getUserId(c) + playlistID, err := trashid.DecodeHashId(c.Params("playlistId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(playlistID), + Action: indexer.Action_Repost, + EntityType: indexer.Entity_Playlist, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send playlist repost transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to repost playlist", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1PlaylistRepost(c *fiber.Ctx) error { + userID := app.getUserId(c) + playlistID, err := trashid.DecodeHashId(c.Params("playlistId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(playlistID), + Action: indexer.Action_Unrepost, + EntityType: indexer.Entity_Playlist, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send playlist unrepost transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unrepost playlist", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_playlist_shares.go b/api/v1_playlist_shares.go new file mode 100644 index 00000000..c6ea934f --- /dev/null +++ b/api/v1_playlist_shares.go @@ -0,0 +1,51 @@ +package api + +import ( + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" + "github.com/gofiber/fiber/v2" + "go.uber.org/zap" +) + +func (app *ApiServer) postV1PlaylistShare(c *fiber.Ctx) error { + userID := app.getUserId(c) + playlistID, err := trashid.DecodeHashId(c.Params("playlistId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(playlistID), + Action: indexer.Action_Share, + EntityType: indexer.Entity_Playlist, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send playlist share transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to share playlist", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_track.go b/api/v1_track.go index a9c7e387..44d3aa48 100644 --- a/api/v1_track.go +++ b/api/v1_track.go @@ -1,8 +1,16 @@ package api import ( + "strconv" + "time" + "api.audius.co/api/dbv1" + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" + "go.uber.org/zap" ) func (app *ApiServer) v1Track(c *fiber.Ctx) error { @@ -27,3 +35,41 @@ func (app *ApiServer) v1Track(c *fiber.Ctx) error { return v1TrackResponse(c, track) } + +func (app *ApiServer) deleteV1Track(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Delete, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track delete transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to delete track", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_track_downloads.go b/api/v1_track_downloads.go new file mode 100644 index 00000000..041c8ef2 --- /dev/null +++ b/api/v1_track_downloads.go @@ -0,0 +1,86 @@ +package api + +import ( + "encoding/json" + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" + "github.com/gofiber/fiber/v2" + "go.uber.org/zap" +) + +type LocationMetadata struct { + City string `json:"city"` + Region string `json:"region"` + Country string `json:"country"` +} + +type DownloadMetadata struct { + CID string `json:"cid"` + Data LocationMetadata `json:"data"` +} + +func (app *ApiServer) postV1TrackDownload(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + // Parse optional location metadata from request body + var reqBody struct { + City string `json:"city"` + Region string `json:"region"` + Country string `json:"country"` + } + + metadata := "" + if err := c.BodyParser(&reqBody); err == nil { + if reqBody.City != "" || reqBody.Region != "" || reqBody.Country != "" { + metadataObj := DownloadMetadata{ + CID: "", + Data: LocationMetadata{ + City: reqBody.City, + Region: reqBody.Region, + Country: reqBody.Country, + }, + } + metadataBytes, _ := json.Marshal(metadataObj) + metadata = string(metadataBytes) + } + } + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Download, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: metadata, + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track download transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to record track download", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_track_favorites.go b/api/v1_track_favorites.go index 735d3671..7ee5ef6c 100644 --- a/api/v1_track_favorites.go +++ b/api/v1_track_favorites.go @@ -1,9 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1TrackFavorites(c *fiber.Ctx) error { @@ -30,3 +37,79 @@ func (app *ApiServer) v1TrackFavorites(c *fiber.Ctx) error { "trackId": trackId, }) } + +func (app *ApiServer) postV1TrackFavorite(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Save, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track favorite transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to favorite track", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1TrackFavorite(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Unsave, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track unfavorite transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unfavorite track", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_track_reposts.go b/api/v1_track_reposts.go index 376a4b68..6e512b4c 100644 --- a/api/v1_track_reposts.go +++ b/api/v1_track_reposts.go @@ -77,3 +77,41 @@ func (app *ApiServer) postV1TrackRepost(c *fiber.Ctx) error { "transaction_hash": response.Msg.GetTransaction().GetHash(), }) } + +func (app *ApiServer) deleteV1TrackRepost(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Unrepost, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track unrepost transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unrepost track", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_track_shares.go b/api/v1_track_shares.go new file mode 100644 index 00000000..58c7b310 --- /dev/null +++ b/api/v1_track_shares.go @@ -0,0 +1,51 @@ +package api + +import ( + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" + "github.com/gofiber/fiber/v2" + "go.uber.org/zap" +) + +func (app *ApiServer) postV1TrackShare(c *fiber.Ctx) error { + userID := app.getUserId(c) + trackID, err := trashid.DecodeHashId(c.Params("trackId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(trackID), + Action: indexer.Action_Share, + EntityType: indexer.Entity_Track, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send track share transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to share track", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_users_followers.go b/api/v1_users_followers.go index 9c29f829..e79699f8 100644 --- a/api/v1_users_followers.go +++ b/api/v1_users_followers.go @@ -1,8 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1UsersFollowers(c *fiber.Ctx) error { @@ -26,3 +34,79 @@ func (app *ApiServer) v1UsersFollowers(c *fiber.Ctx) error { }) return res } + +func (app *ApiServer) postV1UserFollow(c *fiber.Ctx) error { + userID := app.getUserId(c) + followeeUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(followeeUserID), + Action: indexer.Action_Follow, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user follow transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to follow user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1UserFollow(c *fiber.Ctx) error { + userID := app.getUserId(c) + followeeUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(followeeUserID), + Action: indexer.Action_Unfollow, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user unfollow transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unfollow user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_users_muted.go b/api/v1_users_muted.go index a03a8b3d..487cbbf2 100644 --- a/api/v1_users_muted.go +++ b/api/v1_users_muted.go @@ -1,8 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1UsersMuted(c *fiber.Ctx) error { @@ -20,3 +28,79 @@ func (app *ApiServer) v1UsersMuted(c *fiber.Ctx) error { "userId": app.getUserId(c), }) } + +func (app *ApiServer) postV1UserMute(c *fiber.Ctx) error { + userID := app.getUserId(c) + mutedUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(mutedUserID), + Action: indexer.Action_Mute, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user mute transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to mute user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1UserMute(c *fiber.Ctx) error { + userID := app.getUserId(c) + mutedUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(mutedUserID), + Action: indexer.Action_Unmute, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user unmute transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unmute user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/api/v1_users_subscribers.go b/api/v1_users_subscribers.go index 2bc5dbd6..6d67dacc 100644 --- a/api/v1_users_subscribers.go +++ b/api/v1_users_subscribers.go @@ -1,8 +1,16 @@ package api import ( + "strconv" + "time" + + "api.audius.co/indexer" + "api.audius.co/trashid" + corev1 "github.com/OpenAudio/go-openaudio/pkg/api/core/v1" + "github.com/ethereum/go-ethereum/common" "github.com/gofiber/fiber/v2" "github.com/jackc/pgx/v5" + "go.uber.org/zap" ) func (app *ApiServer) v1UsersSubscribers(c *fiber.Ctx) error { @@ -25,3 +33,79 @@ func (app *ApiServer) v1UsersSubscribers(c *fiber.Ctx) error { "userId": app.getUserId(c), }) } + +func (app *ApiServer) postV1UserSubscribe(c *fiber.Ctx) error { + userID := app.getUserId(c) + subscribeeUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(subscribeeUserID), + Action: indexer.Action_Subscribe, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user subscribe transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to subscribe to user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} + +func (app *ApiServer) deleteV1UserSubscribe(c *fiber.Ctx) error { + userID := app.getUserId(c) + subscribeeUserID, err := trashid.DecodeHashId(c.Params("userId")) + if err != nil { + return err + } + + signer, err := app.getApiSigner(c) + if err != nil { + return err + } + + nonce := time.Now().UnixNano() + + manageEntityTx := &corev1.ManageEntityLegacy{ + Signer: common.HexToAddress(signer.Address).String(), + UserId: int64(userID), + EntityId: int64(subscribeeUserID), + Action: indexer.Action_Unsubscribe, + EntityType: indexer.Entity_User, + Nonce: strconv.FormatInt(nonce, 10), + Metadata: "", + } + + response, err := app.sendTransactionWithSigner(manageEntityTx, signer.PrivateKey) + if err != nil { + app.logger.Error("Failed to send user unsubscribe transaction", zap.Error(err)) + return c.Status(fiber.StatusInternalServerError).JSON(fiber.Map{ + "error": "Failed to unsubscribe from user", + }) + } + + return c.JSON(fiber.Map{ + "success": true, + "transaction_hash": response.Msg.GetTransaction().GetHash(), + }) +} diff --git a/indexer/constants.go b/indexer/constants.go index 2923b212..aed32b98 100644 --- a/indexer/constants.go +++ b/indexer/constants.go @@ -25,6 +25,7 @@ const ( Action_Unmute = "Unmute" Action_React = "React" Action_Unreact = "Unreact" + Action_Report = "Report" ) const ( @@ -35,4 +36,5 @@ const ( Entity_Notification = "Notification" Entity_AssociatedWallet = "AssociatedWallet" Entity_Grant = "Grant" + Entity_DeveloperApp = "DeveloperApp" )