diff --git a/backend/cmd/consumer/consumer.go b/backend/cmd/consumer/consumer.go index e0d4722d..ae8dffe6 100644 --- a/backend/cmd/consumer/consumer.go +++ b/backend/cmd/consumer/consumer.go @@ -55,7 +55,7 @@ func main() { indexer.InitIndexerRoutes() routes.InitWebsocketRoutes() routes.InitNFTStaticRoutes() - routes.InitWorldsStaticRoutes() + routes.InitWorldsStaticRoutes() indexer.StartMessageProcessor() core.ArtPeaceBackend.Start(core.ArtPeaceBackend.BackendConfig.ConsumerPort) diff --git a/backend/config/backend.go b/backend/config/backend.go index 2b4002dc..e4e92fdf 100644 --- a/backend/config/backend.go +++ b/backend/config/backend.go @@ -25,11 +25,11 @@ type BackendScriptsConfig struct { CreateCanvasDevnet string `json:"create_canvas_devnet"` FavoriteWorldDevnet string `json:"favorite_world_devnet"` UnfavoriteWorldDevnet string `json:"unfavorite_world_devnet"` - PlaceWorldPixelDevnet string `json:"place_world_pixel_devnet"` - AddStencilDevnet string `json:"add_stencil_devnet"` - RemoveStencilDevnet string `json:"remove_stencil_devnet"` - FavoriteStencilDevnet string `json:"favorite_stencil_devnet"` - UnfavoriteStencilDevnet string `json:"unfavorite_stencil_devnet"` + PlaceWorldPixelDevnet string `json:"place_world_pixel_devnet"` + AddStencilDevnet string `json:"add_stencil_devnet"` + RemoveStencilDevnet string `json:"remove_stencil_devnet"` + FavoriteStencilDevnet string `json:"favorite_stencil_devnet"` + UnfavoriteStencilDevnet string `json:"unfavorite_stencil_devnet"` } type WebSocketConfig struct { @@ -77,11 +77,11 @@ var DefaultBackendConfig = BackendConfig{ CreateCanvasDevnet: "../scripts/create_canvas.sh", FavoriteWorldDevnet: "../scripts/favorite_world.sh", UnfavoriteWorldDevnet: "../scripts/unfavorite_world.sh", - PlaceWorldPixelDevnet: "../scripts/place_world_pixel.sh", - AddStencilDevnet: "../scripts/add_stencil.sh", - RemoveStencilDevnet: "../scripts/remove_stencil.sh", - FavoriteStencilDevnet: "../scripts/favorite_stencil.sh", - UnfavoriteStencilDevnet: "../scripts/unfavorite_stencil.sh", + PlaceWorldPixelDevnet: "../scripts/place_world_pixel.sh", + AddStencilDevnet: "../scripts/add_stencil.sh", + RemoveStencilDevnet: "../scripts/remove_stencil.sh", + FavoriteStencilDevnet: "../scripts/favorite_stencil.sh", + UnfavoriteStencilDevnet: "../scripts/unfavorite_stencil.sh", }, Production: false, WebSocket: WebSocketConfig{ diff --git a/backend/routes/indexer/route.go b/backend/routes/indexer/route.go index 13c30c15..c321c36c 100644 --- a/backend/routes/indexer/route.go +++ b/backend/routes/indexer/route.go @@ -90,12 +90,12 @@ const ( canvasBasicPixelPlacedEvent = "0x03066baa9c37a42082799e6bc6426ff7d4dc8a635ed9dfc444d0d3c51e605a6b" canvasExtraPixelsPlacedEvent = "0x01e42e4d6ca5843bfd4e86e344db6c418b295c23bed38831a7ec9b4a83148830" canvasHostAwardedUserEvent = "0x01bf6ede8c6c232cee1830a5227fd638383f5af669701289d113492b1d41fda5" - canvasFavoritedEvent = "0x032105bd4f21a32bc92e45a49b30eab9355f7f89619d87e9801628e3acc5b502" - canvasUnfavoritedEvent = "0x014ee6480f95acb4b7286d3a7f95b6033299e66e502cfb4b207ccf088b5f601d" - stencilAddedEvent = "0x03384fcf8ff5c539c31feec6626511aa15ae53dba7459fd3a3c67af615ef6b5d" - stencilRemovedEvent = "0x023c933ed3ee3f94b5b82f8e2e570c8354e6f5036c3a079092ceeed15979e7fa" - stencilFavoritedEvent = "0x007cb4ae927fb597834e194e2c950a2d813461c72f372f78d0610ea246f53017" - stencilUnfavoritedEvent = "0x00a5477c7df6522316b652e56317e69e52429ab43a6772fb6f6c2a574f7e196f" + canvasFavoritedEvent = "0x032105bd4f21a32bc92e45a49b30eab9355f7f89619d87e9801628e3acc5b502" + canvasUnfavoritedEvent = "0x014ee6480f95acb4b7286d3a7f95b6033299e66e502cfb4b207ccf088b5f601d" + stencilAddedEvent = "0x03384fcf8ff5c539c31feec6626511aa15ae53dba7459fd3a3c67af615ef6b5d" + stencilRemovedEvent = "0x023c933ed3ee3f94b5b82f8e2e570c8354e6f5036c3a079092ceeed15979e7fa" + stencilFavoritedEvent = "0x007cb4ae927fb597834e194e2c950a2d813461c72f372f78d0610ea246f53017" + stencilUnfavoritedEvent = "0x00a5477c7df6522316b652e56317e69e52429ab43a6772fb6f6c2a574f7e196f" ) var eventProcessors = map[string](func(IndexerEvent)){ @@ -135,12 +135,12 @@ var eventProcessors = map[string](func(IndexerEvent)){ canvasBasicPixelPlacedEvent: processCanvasBasicPixelPlacedEvent, canvasExtraPixelsPlacedEvent: processCanvasExtraPixelsPlacedEvent, canvasHostAwardedUserEvent: processCanvasHostAwardedUserEvent, - canvasFavoritedEvent: processCanvasFavoritedEvent, - canvasUnfavoritedEvent: processCanvasUnfavoritedEvent, - stencilAddedEvent: processStencilAddedEvent, - stencilRemovedEvent: processStencilRemovedEvent, - stencilFavoritedEvent: processStencilFavoritedEvent, - stencilUnfavoritedEvent: processStencilUnfavoritedEvent, + canvasFavoritedEvent: processCanvasFavoritedEvent, + canvasUnfavoritedEvent: processCanvasUnfavoritedEvent, + stencilAddedEvent: processStencilAddedEvent, + stencilRemovedEvent: processStencilRemovedEvent, + stencilFavoritedEvent: processStencilFavoritedEvent, + stencilUnfavoritedEvent: processStencilUnfavoritedEvent, } var eventReverters = map[string](func(IndexerEvent)){ @@ -180,12 +180,12 @@ var eventReverters = map[string](func(IndexerEvent)){ canvasBasicPixelPlacedEvent: revertCanvasBasicPixelPlacedEvent, canvasExtraPixelsPlacedEvent: revertCanvasExtraPixelsPlacedEvent, canvasHostAwardedUserEvent: revertCanvasHostAwardedUserEvent, - canvasFavoritedEvent: revertCanvasFavoritedEvent, - canvasUnfavoritedEvent: revertCanvasUnfavoritedEvent, - stencilAddedEvent: revertStencilAddedEvent, - stencilRemovedEvent: revertStencilRemovedEvent, - stencilFavoritedEvent: revertStencilFavoritedEvent, - stencilUnfavoritedEvent: revertStencilUnfavoritedEvent, + canvasFavoritedEvent: revertCanvasFavoritedEvent, + canvasUnfavoritedEvent: revertCanvasUnfavoritedEvent, + stencilAddedEvent: revertStencilAddedEvent, + stencilRemovedEvent: revertStencilRemovedEvent, + stencilFavoritedEvent: revertStencilFavoritedEvent, + stencilUnfavoritedEvent: revertStencilUnfavoritedEvent, } // TODO: Rethink this ( & look at values before multicanvas PR ) @@ -226,12 +226,12 @@ var eventRequiresOrdering = map[string]bool{ canvasBasicPixelPlacedEvent: true, canvasExtraPixelsPlacedEvent: true, canvasHostAwardedUserEvent: true, - canvasFavoritedEvent: true, - canvasUnfavoritedEvent: true, - stencilAddedEvent: true, - stencilRemovedEvent: true, - stencilFavoritedEvent: true, - stencilUnfavoritedEvent: true, + canvasFavoritedEvent: true, + canvasUnfavoritedEvent: true, + stencilAddedEvent: true, + stencilRemovedEvent: true, + stencilFavoritedEvent: true, + stencilUnfavoritedEvent: true, } const ( diff --git a/backend/routes/indexer/stencil.go b/backend/routes/indexer/stencil.go index 32966aab..31354c01 100644 --- a/backend/routes/indexer/stencil.go +++ b/backend/routes/indexer/stencil.go @@ -8,233 +8,233 @@ import ( ) func processStencilAddedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - hashHex := event.Event.Data[0][2:] // Remove the 0x prefix - widthHex := event.Event.Data[1] - heightHex := event.Event.Data[2] - positionHex := event.Event.Data[3] - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilWidth, err := strconv.ParseInt(widthHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to parse widthHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilHeight, err := strconv.ParseInt(heightHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to parse heightHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilPosition, err := strconv.ParseInt(positionHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to parse positionHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Stencils (stencil_id, world_id, hash, width, height, position) VALUES ($1, $2, $3, $4, $5, $6)", stencilId, canvasId, hashHex, stencilWidth, stencilHeight, stencilPosition) - if err != nil { - PrintIndexerError("processStencilAddedEvent", "Failed to insert into Stencils", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + hashHex := event.Event.Data[0][2:] // Remove the 0x prefix + widthHex := event.Event.Data[1] + heightHex := event.Event.Data[2] + positionHex := event.Event.Data[3] + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilWidth, err := strconv.ParseInt(widthHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to parse widthHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilHeight, err := strconv.ParseInt(heightHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to parse heightHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilPosition, err := strconv.ParseInt(positionHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to parse positionHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Stencils (stencil_id, world_id, hash, width, height, position) VALUES ($1, $2, $3, $4, $5, $6)", stencilId, canvasId, hashHex, stencilWidth, stencilHeight, stencilPosition) + if err != nil { + PrintIndexerError("processStencilAddedEvent", "Failed to insert into Stencils", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } } func revertStencilAddedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilAddedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilAddedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM Stencils WHERE stencil_id = $1 AND world_id = $2", stencilId, canvasId) - if err != nil { - PrintIndexerError("revertStencilAddedEvent", "Failed to delete from Stencils", canvasIdHex, stencilIdHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilAddedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilAddedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM Stencils WHERE stencil_id = $1 AND world_id = $2", stencilId, canvasId) + if err != nil { + PrintIndexerError("revertStencilAddedEvent", "Failed to delete from Stencils", canvasIdHex, stencilIdHex, err) + return + } } func processStencilRemovedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilRemovedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilRemovedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM Stencils WHERE stencil_id = $1 AND world_id = $2", stencilId, canvasId) - if err != nil { - PrintIndexerError("processStencilRemovedEvent", "Failed to delete from Stencils", canvasIdHex, stencilIdHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilRemovedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilRemovedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM Stencils WHERE stencil_id = $1 AND world_id = $2", stencilId, canvasId) + if err != nil { + PrintIndexerError("processStencilRemovedEvent", "Failed to delete from Stencils", canvasIdHex, stencilIdHex, err) + return + } } func revertStencilRemovedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - hashHex := event.Event.Data[0] - widthHex := event.Event.Data[1] - heightHex := event.Event.Data[2] - positionHex := event.Event.Data[3] - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilWidth, err := strconv.ParseInt(widthHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to parse widthHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilHeight, err := strconv.ParseInt(heightHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to parse heightHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - stencilPosition, err := strconv.ParseInt(positionHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to parse positionHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Stencils (stencil_id, world_id, hash, width, height, position) VALUES ($1, $2, $3, $4, $5, $6)", stencilId, canvasId, hashHex, stencilWidth, stencilHeight, stencilPosition) - if err != nil { - PrintIndexerError("revertStencilRemovedEvent", "Failed to insert into Stencils", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + hashHex := event.Event.Data[0] + widthHex := event.Event.Data[1] + heightHex := event.Event.Data[2] + positionHex := event.Event.Data[3] + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilWidth, err := strconv.ParseInt(widthHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to parse widthHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilHeight, err := strconv.ParseInt(heightHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to parse heightHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + stencilPosition, err := strconv.ParseInt(positionHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to parse positionHex", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO Stencils (stencil_id, world_id, hash, width, height, position) VALUES ($1, $2, $3, $4, $5, $6)", stencilId, canvasId, hashHex, stencilWidth, stencilHeight, stencilPosition) + if err != nil { + PrintIndexerError("revertStencilRemovedEvent", "Failed to insert into Stencils", canvasIdHex, stencilIdHex, hashHex, widthHex, heightHex, positionHex, err) + return + } } func processStencilFavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilFavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO StencilFavorites (stencil_id, world_id, user_address) VALUES ($1, $2, $3)", stencilId, canvasId, userAddressHex) - if err != nil { - PrintIndexerError("processStencilFavoritedEvent", "Failed to insert into StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilFavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO StencilFavorites (stencil_id, world_id, user_address) VALUES ($1, $2, $3)", stencilId, canvasId, userAddressHex) + if err != nil { + PrintIndexerError("processStencilFavoritedEvent", "Failed to insert into StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } } func revertStencilFavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilFavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM StencilFavorites WHERE stencil_id = $1 AND world_id = $2 AND user_address = $3", stencilId, canvasId, userAddressHex) - if err != nil { - PrintIndexerError("revertStencilFavoritedEvent", "Failed to delete from StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilFavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM StencilFavorites WHERE stencil_id = $1 AND world_id = $2 AND user_address = $3", stencilId, canvasId, userAddressHex) + if err != nil { + PrintIndexerError("revertStencilFavoritedEvent", "Failed to delete from StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } } func processStencilUnfavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("processStencilUnfavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM StencilFavorites WHERE stencil_id = $1 AND world_id = $2 AND user_address = $3", stencilId, canvasId, userAddressHex) - if err != nil { - PrintIndexerError("processStencilUnfavoritedEvent", "Failed to delete from StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("processStencilUnfavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM StencilFavorites WHERE stencil_id = $1 AND world_id = $2 AND user_address = $3", stencilId, canvasId, userAddressHex) + if err != nil { + PrintIndexerError("processStencilUnfavoritedEvent", "Failed to delete from StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } } func revertStencilUnfavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - stencilIdHex := event.Event.Keys[2] - userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO StencilFavorites (stencil_id, world_id, user_address) VALUES ($1, $2, $3)", stencilId, canvasId, userAddressHex) - if err != nil { - PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to insert into StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) - return - } + canvasIdHex := event.Event.Keys[1] + stencilIdHex := event.Event.Keys[2] + userAddressHex := event.Event.Keys[3][2:] // Remove the 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + stencilId, err := strconv.ParseInt(stencilIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to parse stencilIdHex", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO StencilFavorites (stencil_id, world_id, user_address) VALUES ($1, $2, $3)", stencilId, canvasId, userAddressHex) + if err != nil { + PrintIndexerError("revertStencilUnfavoritedEvent", "Failed to insert into StencilFavorites", canvasIdHex, stencilIdHex, userAddressHex, err) + return + } } diff --git a/backend/routes/indexer/worlds.go b/backend/routes/indexer/worlds.go index fc13af08..851959f2 100644 --- a/backend/routes/indexer/worlds.go +++ b/backend/routes/indexer/worlds.go @@ -109,62 +109,62 @@ func processCanvasCreatedEvent(event IndexerEvent) { } else { PrintIndexerError("processCanvasCreatedEvent", "Canvas already exists in redis", canvasIdHex, host, nameHex, widthHex, heightHex, timeBetweenPixelsHex, colorPaletteLenHex, err) } - - // Create base directories if they don't exist - dirs := []string{ - "worlds", - "worlds/images", - } - - for _, dir := range dirs { - if _, err := os.Stat(dir); os.IsNotExist(err) { - err = os.MkdirAll(dir, os.ModePerm) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to create directory", dir, err) - return - } - } - } - - generatedWorldImage := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) + + // Create base directories if they don't exist + dirs := []string{ + "worlds", + "worlds/images", + } + + for _, dir := range dirs { + if _, err := os.Stat(dir); os.IsNotExist(err) { + err = os.MkdirAll(dir, os.ModePerm) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to create directory", dir, err) + return + } + } + } + + generatedWorldImage := image.NewRGBA(image.Rect(0, 0, int(width), int(height))) baseColorHex := event.Event.Data[6] - baseColor := baseColorHex[len(baseColorHex)-6:] // Remove prefix - baseColorR, err := strconv.ParseInt(baseColor[0:2], 16, 64) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorR", baseColor, err) - return - } - baseColorG, err := strconv.ParseInt(baseColor[2:4], 16, 64) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorG", baseColor, err) - return - } - baseColorB, err := strconv.ParseInt(baseColor[4:6], 16, 64) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorB", baseColor, err) - return - } - color := color.RGBA{R: uint8(baseColorR), G: uint8(baseColorG), B: uint8(baseColorB), A: 255} - for y := 0; y < int(height); y++ { - for x := 0; x < int(width); x++ { - generatedWorldImage.Set(x, y, color) - } - } - - // Create world image - filename := "worlds/images/world-" + strconv.Itoa(int(canvasId)) + ".png" - file, err := os.Create(filename) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to create file", filename, err) - return - } - defer file.Close() - - err = png.Encode(file, generatedWorldImage) - if err != nil { - PrintIndexerError("processCanvasCreatedEvent", "Failed to encode image", filename, err) - return - } + baseColor := baseColorHex[len(baseColorHex)-6:] // Remove prefix + baseColorR, err := strconv.ParseInt(baseColor[0:2], 16, 64) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorR", baseColor, err) + return + } + baseColorG, err := strconv.ParseInt(baseColor[2:4], 16, 64) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorG", baseColor, err) + return + } + baseColorB, err := strconv.ParseInt(baseColor[4:6], 16, 64) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to parse baseColorB", baseColor, err) + return + } + color := color.RGBA{R: uint8(baseColorR), G: uint8(baseColorG), B: uint8(baseColorB), A: 255} + for y := 0; y < int(height); y++ { + for x := 0; x < int(width); x++ { + generatedWorldImage.Set(x, y, color) + } + } + + // Create world image + filename := "worlds/images/world-" + strconv.Itoa(int(canvasId)) + ".png" + file, err := os.Create(filename) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to create file", filename, err) + return + } + defer file.Close() + + err = png.Encode(file, generatedWorldImage) + if err != nil { + PrintIndexerError("processCanvasCreatedEvent", "Failed to encode image", filename, err) + return + } } func revertCanvasCreatedEvent(event IndexerEvent) { @@ -363,108 +363,107 @@ func processCanvasPixelPlacedEvent(event IndexerEvent) { return } - // Check # of total pixels placed on this world - totalPixelsPlaced, err := core.PostgresQueryOne[int]("SELECT COUNT(*) FROM WorldsPixels WHERE world_id = $1", canvasId) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query totalPixelsPlaced", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - - lastPixelPlacedTime, err := core.PostgresQueryOne[*time.Time]("SELECT time FROM WorldsPixels WHERE world_id = $1 ORDER BY time DESC LIMIT 1", canvasId) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query lastPixelPlacedTime", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - timeSinceLastPixelPlaced := time.Now().Unix() - (*lastPixelPlacedTime).Unix() - threeHours := int64(3 * 60 * 60) - - // TODO: Improve this & collect snapshots for a timelapse - // Snapshot canvas image every 100 pixels placed || if last pixel placed was more than 3 hours ago - if uint(*totalPixelsPlaced) % 100 == 0 || timeSinceLastPixelPlaced > threeHours { - worldWidth, err := core.PostgresQueryOne[int]("SELECT width FROM Worlds WHERE world_id = $1", canvasId) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query worldWidth", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - worldHeight, err := core.PostgresQueryOne[int]("SELECT height FROM Worlds WHERE world_id = $1", canvasId) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query worldHeight", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - - ctx := context.Background() - canvasRedisKey := "canvas-" + strconv.Itoa(int(canvasId)) - canvas, err := core.ArtPeaceBackend.Databases.Redis.Get(ctx, canvasRedisKey).Result() - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to get canvas", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - - colorPaletteHex, err := core.PostgresQuery[string]("SELECT hex FROM colors ORDER BY color_key") - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query colorPaletteHex", canvasIdHex, placedBy, posHex, colorHex, err) - return - } - - colorPalette := make([]color.RGBA, len(colorPaletteHex)) - for idx, colorHex := range colorPaletteHex { - r, err := strconv.ParseInt(colorHex[0:2], 16, 64) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) - return - } - g, err := strconv.ParseInt(colorHex[2:4], 16, 64) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) - return - } - b, err := strconv.ParseInt(colorHex[4:6], 16, 64) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) - return - } - colorPalette[idx] = color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255} - } - - // Create world image - generatedWorldImage := image.NewRGBA(image.Rect(0, 0, *worldWidth, *worldHeight)) - bitWidth := uint(core.ArtPeaceBackend.CanvasConfig.ColorsBitWidth) - oneByteBitOffset := uint(8 - bitWidth) - twoByteBitOffset := uint(16 - bitWidth) - - for y := 0; y < *worldHeight; y++ { - for x := 0; x < *worldWidth; x++ { - innerPos := uint(y*(*worldWidth) + x) - bitPos := innerPos * bitWidth - bytePos := bitPos / 8 - bitOffset := bitPos % 8 - - if bitOffset <= oneByteBitOffset { - colorIdx := (canvas[bytePos] >> (oneByteBitOffset - bitOffset)) & 0b11111 - generatedWorldImage.Set(x, y, colorPalette[colorIdx]) - } else { - colorIdx := (((uint16(canvas[bytePos]) << 8) | uint16(canvas[bytePos+1])) >> (twoByteBitOffset - bitOffset)) & 0b11111 - generatedWorldImage.Set(x, y, colorPalette[colorIdx]) - } - } - } - - // Create world image - filename := "worlds/images/world-" + strconv.Itoa(int(canvasId)) + ".png" - file, err := os.Create(filename) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to create file", filename, err) - return - } - defer file.Close() - - err = png.Encode(file, generatedWorldImage) - if err != nil { - PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to encode image", filename, err) - return - } -} + // Check # of total pixels placed on this world + totalPixelsPlaced, err := core.PostgresQueryOne[int]("SELECT COUNT(*) FROM WorldsPixels WHERE world_id = $1", canvasId) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query totalPixelsPlaced", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + + lastPixelPlacedTime, err := core.PostgresQueryOne[*time.Time]("SELECT time FROM WorldsPixels WHERE world_id = $1 ORDER BY time DESC LIMIT 1", canvasId) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query lastPixelPlacedTime", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + timeSinceLastPixelPlaced := time.Now().Unix() - (*lastPixelPlacedTime).Unix() + threeHours := int64(3 * 60 * 60) + + // TODO: Improve this & collect snapshots for a timelapse + // Snapshot canvas image every 100 pixels placed || if last pixel placed was more than 3 hours ago + if uint(*totalPixelsPlaced)%100 == 0 || timeSinceLastPixelPlaced > threeHours { + worldWidth, err := core.PostgresQueryOne[int]("SELECT width FROM Worlds WHERE world_id = $1", canvasId) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query worldWidth", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + worldHeight, err := core.PostgresQueryOne[int]("SELECT height FROM Worlds WHERE world_id = $1", canvasId) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query worldHeight", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + + ctx := context.Background() + canvasRedisKey := "canvas-" + strconv.Itoa(int(canvasId)) + canvas, err := core.ArtPeaceBackend.Databases.Redis.Get(ctx, canvasRedisKey).Result() + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to get canvas", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + + colorPaletteHex, err := core.PostgresQuery[string]("SELECT hex FROM colors ORDER BY color_key") + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to query colorPaletteHex", canvasIdHex, placedBy, posHex, colorHex, err) + return + } + + colorPalette := make([]color.RGBA, len(colorPaletteHex)) + for idx, colorHex := range colorPaletteHex { + r, err := strconv.ParseInt(colorHex[0:2], 16, 64) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) + return + } + g, err := strconv.ParseInt(colorHex[2:4], 16, 64) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) + return + } + b, err := strconv.ParseInt(colorHex[4:6], 16, 64) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to parse colorHex", colorHex, err) + return + } + colorPalette[idx] = color.RGBA{R: uint8(r), G: uint8(g), B: uint8(b), A: 255} + } + + // Create world image + generatedWorldImage := image.NewRGBA(image.Rect(0, 0, *worldWidth, *worldHeight)) + bitWidth := uint(core.ArtPeaceBackend.CanvasConfig.ColorsBitWidth) + oneByteBitOffset := uint(8 - bitWidth) + twoByteBitOffset := uint(16 - bitWidth) + + for y := 0; y < *worldHeight; y++ { + for x := 0; x < *worldWidth; x++ { + innerPos := uint(y*(*worldWidth) + x) + bitPos := innerPos * bitWidth + bytePos := bitPos / 8 + bitOffset := bitPos % 8 + + if bitOffset <= oneByteBitOffset { + colorIdx := (canvas[bytePos] >> (oneByteBitOffset - bitOffset)) & 0b11111 + generatedWorldImage.Set(x, y, colorPalette[colorIdx]) + } else { + colorIdx := (((uint16(canvas[bytePos]) << 8) | uint16(canvas[bytePos+1])) >> (twoByteBitOffset - bitOffset)) & 0b11111 + generatedWorldImage.Set(x, y, colorPalette[colorIdx]) + } + } + } + // Create world image + filename := "worlds/images/world-" + strconv.Itoa(int(canvasId)) + ".png" + file, err := os.Create(filename) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to create file", filename, err) + return + } + defer file.Close() + + err = png.Encode(file, generatedWorldImage) + if err != nil { + PrintIndexerError("processCanvasPixelPlacedEvent", "Failed to encode image", filename, err) + return + } + } var message = map[string]interface{}{ "worldId": canvasId, @@ -642,69 +641,69 @@ func revertCanvasHostAwardedUserEvent(event IndexerEvent) { } func processCanvasFavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - user := event.Event.Keys[2][2:] // Remove 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processCanvasFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO WorldFavorites (world_id, user_address) VALUES ($1, $2)", canvasId, user) - if err != nil { - PrintIndexerError("processCanvasFavoritedEvent", "Failed to insert into WorldFavorites", canvasIdHex, user, err) - return - } + canvasIdHex := event.Event.Keys[1] + user := event.Event.Keys[2][2:] // Remove 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processCanvasFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO WorldFavorites (world_id, user_address) VALUES ($1, $2)", canvasId, user) + if err != nil { + PrintIndexerError("processCanvasFavoritedEvent", "Failed to insert into WorldFavorites", canvasIdHex, user, err) + return + } } func revertCanvasFavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - user := event.Event.Keys[2][2:] // Remove 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertCanvasFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM WorldFavorites WHERE world_id = $1 AND user_address = $2", canvasId, user) - if err != nil { - PrintIndexerError("revertCanvasFavoritedEvent", "Failed to delete from WorldFavorites", canvasIdHex, user, err) - return - } + canvasIdHex := event.Event.Keys[1] + user := event.Event.Keys[2][2:] // Remove 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertCanvasFavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM WorldFavorites WHERE world_id = $1 AND user_address = $2", canvasId, user) + if err != nil { + PrintIndexerError("revertCanvasFavoritedEvent", "Failed to delete from WorldFavorites", canvasIdHex, user, err) + return + } } func processCanvasUnfavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - user := event.Event.Keys[2][2:] // Remove 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("processCanvasUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM WorldFavorites WHERE world_id = $1 AND user_address = $2", canvasId, user) - if err != nil { - PrintIndexerError("processCanvasUnfavoritedEvent", "Failed to delete from WorldFavorites", canvasIdHex, user, err) - return - } + canvasIdHex := event.Event.Keys[1] + user := event.Event.Keys[2][2:] // Remove 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("processCanvasUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "DELETE FROM WorldFavorites WHERE world_id = $1 AND user_address = $2", canvasId, user) + if err != nil { + PrintIndexerError("processCanvasUnfavoritedEvent", "Failed to delete from WorldFavorites", canvasIdHex, user, err) + return + } } func revertCanvasUnfavoritedEvent(event IndexerEvent) { - canvasIdHex := event.Event.Keys[1] - user := event.Event.Keys[2][2:] // Remove 0x prefix - - canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) - if err != nil { - PrintIndexerError("revertCanvasUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) - return - } - - _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO WorldFavorites (world_id, user_address) VALUES ($1, $2)", canvasId, user) - if err != nil { - PrintIndexerError("revertCanvasUnfavoritedEvent", "Failed to insert into WorldFavorites", canvasIdHex, user, err) - return - } + canvasIdHex := event.Event.Keys[1] + user := event.Event.Keys[2][2:] // Remove 0x prefix + + canvasId, err := strconv.ParseInt(canvasIdHex, 0, 64) + if err != nil { + PrintIndexerError("revertCanvasUnfavoritedEvent", "Failed to parse canvasIdHex", canvasIdHex, user, err) + return + } + + _, err = core.ArtPeaceBackend.Databases.Postgres.Exec(context.Background(), "INSERT INTO WorldFavorites (world_id, user_address) VALUES ($1, $2)", canvasId, user) + if err != nil { + PrintIndexerError("revertCanvasUnfavoritedEvent", "Failed to insert into WorldFavorites", canvasIdHex, user, err) + return + } } diff --git a/backend/routes/routes.go b/backend/routes/routes.go index ed2994b2..18aaefb7 100644 --- a/backend/routes/routes.go +++ b/backend/routes/routes.go @@ -26,6 +26,6 @@ func InitRoutes() { InitColorsRoutes() InitVotableColorsRoutes() InitWorldsRoutes() - InitStencilsRoutes() - InitStencilsStaticRoutes() + InitStencilsRoutes() + InitStencilsStaticRoutes() } diff --git a/backend/routes/stencils.go b/backend/routes/stencils.go index 8d9fb835..b8c60e92 100644 --- a/backend/routes/stencils.go +++ b/backend/routes/stencils.go @@ -24,38 +24,38 @@ func InitStencilsRoutes() { // TODO: Hot/top use user interactivity instead of favorite count http.HandleFunc("/get-top-stencils", getTopStencils) http.HandleFunc("/get-hot-stencils", getHotStencils) - http.HandleFunc("/add-stencil-img", addStencilImg) - http.HandleFunc("/add-stencil-data", addStencilData) + http.HandleFunc("/add-stencil-img", addStencilImg) + http.HandleFunc("/add-stencil-data", addStencilData) if !core.ArtPeaceBackend.BackendConfig.Production { - http.HandleFunc("/add-stencil-devnet", addStencilDevnet) - http.HandleFunc("/remove-stencil-devnet", removeStencilDevnet) - http.HandleFunc("/favorite-stencil-devnet", favoriteStencilDevnet) - http.HandleFunc("/unfavorite-stencil-devnet", unfavoriteStencilDevnet) + http.HandleFunc("/add-stencil-devnet", addStencilDevnet) + http.HandleFunc("/remove-stencil-devnet", removeStencilDevnet) + http.HandleFunc("/favorite-stencil-devnet", favoriteStencilDevnet) + http.HandleFunc("/unfavorite-stencil-devnet", unfavoriteStencilDevnet) } } func InitStencilsStaticRoutes() { - http.Handle("/stencils/", http.StripPrefix("/stencils/", http.FileServer(http.Dir("./stencils")))) + http.Handle("/stencils/", http.StripPrefix("/stencils/", http.FileServer(http.Dir("./stencils")))) } type StencilData struct { - StencilId int `json:"stencilId"` - WorldId int `json:"worldId"` - Name string `json:"name"` - Hash string `json:"hash"` - Width int `json:"width"` - Height int `json:"height"` - Position int `json:"position"` - Favorites int `json:"favorites"` - Favorited bool `json:"favorited"` + StencilId int `json:"stencilId"` + WorldId int `json:"worldId"` + Name string `json:"name"` + Hash string `json:"hash"` + Width int `json:"width"` + Height int `json:"height"` + Position int `json:"position"` + Favorites int `json:"favorites"` + Favorited bool `json:"favorited"` } func getStencil(w http.ResponseWriter, r *http.Request) { - stencilId := r.URL.Query().Get("stencilId") - if stencilId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing stencilId") - return - } + stencilId := r.URL.Query().Get("stencilId") + if stencilId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing stencilId") + return + } worldId := r.URL.Query().Get("worldId") if worldId == "" { @@ -73,20 +73,20 @@ func getStencil(w http.ResponseWriter, r *http.Request) { } func getStencils(w http.ResponseWriter, r *http.Request) { - worldId := r.URL.Query().Get("worldId") - if worldId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") - return - } - worldIdInt, err := strconv.Atoi(worldId) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } - address := r.URL.Query().Get("address") - if address == "" { - address = "0" - } + worldId := r.URL.Query().Get("worldId") + if worldId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") + return + } + worldIdInt, err := strconv.Atoi(worldId) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } + address := r.URL.Query().Get("address") + if address == "" { + address = "0" + } pageLength, err := strconv.Atoi(r.URL.Query().Get("pageLength")) if err != nil || pageLength <= 0 { pageLength = 25 @@ -128,16 +128,16 @@ func getStencils(w http.ResponseWriter, r *http.Request) { } func getNewStencils(w http.ResponseWriter, r *http.Request) { - worldId := r.URL.Query().Get("worldId") - if worldId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") - return - } - worldIdInt, err := strconv.Atoi(worldId) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } + worldId := r.URL.Query().Get("worldId") + if worldId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") + return + } + worldIdInt, err := strconv.Atoi(worldId) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } address := r.URL.Query().Get("address") if address == "" { address = "0" @@ -176,7 +176,7 @@ func getNewStencils(w http.ResponseWriter, r *http.Request) { LIMIT $3 OFFSET $4` stencils, err := core.PostgresQueryJson[StencilData](query, address, worldIdInt, pageLength, offset) if err != nil { - fmt.Println(err) + fmt.Println(err) routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve Worlds") return } @@ -184,16 +184,16 @@ func getNewStencils(w http.ResponseWriter, r *http.Request) { } func getHotStencils(w http.ResponseWriter, r *http.Request) { - worldId := r.URL.Query().Get("worldId") - if worldId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") - return - } - worldIdInt, err := strconv.Atoi(worldId) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } + worldId := r.URL.Query().Get("worldId") + if worldId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") + return + } + worldIdInt, err := strconv.Atoi(worldId) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } address := r.URL.Query().Get("address") if address == "" { address = "0" @@ -256,16 +256,16 @@ func getHotStencils(w http.ResponseWriter, r *http.Request) { } func getTopStencils(w http.ResponseWriter, r *http.Request) { - worldId := r.URL.Query().Get("worldId") - if worldId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") - return - } - worldIdInt, err := strconv.Atoi(worldId) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } + worldId := r.URL.Query().Get("worldId") + if worldId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") + return + } + worldIdInt, err := strconv.Atoi(worldId) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } address := r.URL.Query().Get("address") if address == "" { address = "0" @@ -312,16 +312,16 @@ func getTopStencils(w http.ResponseWriter, r *http.Request) { } func getFavoriteStencils(w http.ResponseWriter, r *http.Request) { - worldId := r.URL.Query().Get("worldId") - if worldId == "" { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") - return - } - worldIdInt, err := strconv.Atoi(worldId) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } + worldId := r.URL.Query().Get("worldId") + if worldId == "" { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Missing worldId") + return + } + worldIdInt, err := strconv.Atoi(worldId) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } address := r.URL.Query().Get("address") if address == "" { address = "0" @@ -448,11 +448,11 @@ func addStencilData(w http.ResponseWriter, r *http.Request) { return } - worldId, err := strconv.Atoi((*jsonBody)["worldId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } + worldId, err := strconv.Atoi((*jsonBody)["worldId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } width, err := strconv.Atoi((*jsonBody)["width"]) if err != nil { @@ -602,109 +602,109 @@ func addStencilDevnet(w http.ResponseWriter, r *http.Request) { } func removeStencilDevnet(w http.ResponseWriter, r *http.Request) { - // Disable this in production - if routeutils.NonProductionMiddleware(w, r) { - return - } - - jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") - return - } - - worldId, err := strconv.Atoi((*jsonBody)["worldId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") - return - } - - stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") - return - } - - shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.RemoveStencilDevnet - contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") - cmd := exec.Command(shellCmd, contract, "remove_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) - _, err = cmd.Output() - if err != nil { - routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to remove stencil from devnet") - return - } - - routeutils.WriteResultJson(w, "Stencil removed from devnet") + // Disable this in production + if routeutils.NonProductionMiddleware(w, r) { + return + } + + jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") + return + } + + worldId, err := strconv.Atoi((*jsonBody)["worldId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") + return + } + + stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") + return + } + + shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.RemoveStencilDevnet + contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") + cmd := exec.Command(shellCmd, contract, "remove_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) + _, err = cmd.Output() + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to remove stencil from devnet") + return + } + + routeutils.WriteResultJson(w, "Stencil removed from devnet") } func favoriteStencilDevnet(w http.ResponseWriter, r *http.Request) { - // Disable this in production - if routeutils.NonProductionMiddleware(w, r) { - return - } - - jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") - return - } - - worldId, err := strconv.Atoi((*jsonBody)["worldId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") - return - } - - stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") - return - } - - shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.FavoriteStencilDevnet - contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") - cmd := exec.Command(shellCmd, contract, "favorite_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) - _, err = cmd.Output() - if err != nil { - routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to favorite stencil in devnet") - return - } - - routeutils.WriteResultJson(w, "Stencil favorited in devnet") + // Disable this in production + if routeutils.NonProductionMiddleware(w, r) { + return + } + + jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") + return + } + + worldId, err := strconv.Atoi((*jsonBody)["worldId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") + return + } + + stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") + return + } + + shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.FavoriteStencilDevnet + contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") + cmd := exec.Command(shellCmd, contract, "favorite_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) + _, err = cmd.Output() + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to favorite stencil in devnet") + return + } + + routeutils.WriteResultJson(w, "Stencil favorited in devnet") } func unfavoriteStencilDevnet(w http.ResponseWriter, r *http.Request) { - // Disable this in production - if routeutils.NonProductionMiddleware(w, r) { - return - } - - jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") - return - } - - worldId, err := strconv.Atoi((*jsonBody)["worldId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") - return - } - - stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") - return - } - - shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.UnfavoriteStencilDevnet - contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") - cmd := exec.Command(shellCmd, contract, "unfavorite_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) - _, err = cmd.Output() - if err != nil { - routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to unfavorite stencil in devnet") - return - } - - routeutils.WriteResultJson(w, "Stencil unfavorited in devnet") + // Disable this in production + if routeutils.NonProductionMiddleware(w, r) { + return + } + + jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Failed to read request body") + return + } + + worldId, err := strconv.Atoi((*jsonBody)["worldId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid faction ID") + return + } + + stencilId, err := strconv.Atoi((*jsonBody)["stencilId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid stencil ID") + return + } + + shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.UnfavoriteStencilDevnet + contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") + cmd := exec.Command(shellCmd, contract, "unfavorite_stencil", strconv.Itoa(worldId), strconv.Itoa(stencilId)) + _, err = cmd.Output() + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to unfavorite stencil in devnet") + return + } + + routeutils.WriteResultJson(w, "Stencil unfavorited in devnet") } diff --git a/backend/routes/worlds.go b/backend/routes/worlds.go index c34631dd..fb98fa72 100644 --- a/backend/routes/worlds.go +++ b/backend/routes/worlds.go @@ -33,12 +33,12 @@ func InitWorldsRoutes() { http.HandleFunc("/create-canvas-devnet", createCanvasDevnet) http.HandleFunc("/favorite-world-devnet", favoriteWorldDevnet) http.HandleFunc("/unfavorite-world-devnet", unfavoriteWorldDevnet) - http.HandleFunc("/place-world-pixel-devnet", placeWorldPixelDevnet) + http.HandleFunc("/place-world-pixel-devnet", placeWorldPixelDevnet) } } func InitWorldsStaticRoutes() { - http.Handle("/worlds/", http.StripPrefix("/worlds/", http.FileServer(http.Dir("./worlds")))) + http.Handle("/worlds/", http.StripPrefix("/worlds/", http.FileServer(http.Dir("./worlds")))) } func getWorldCanvas(w http.ResponseWriter, r *http.Request) { @@ -63,16 +63,16 @@ func getWorldCanvas(w http.ResponseWriter, r *http.Request) { } type WorldData struct { - WorldId int `json:"worldId"` - Host string `json:"host"` - Name string `json:"name"` - Width int `json:"width"` - Height int `json:"height"` - TimeBetweenPixels int `json:"timeBetweenPixels"` - StartTime *time.Time `json:"startTime"` - EndTime *time.Time `json:"endTime"` - Favorites int `json:"favorites"` - Favorited bool `json:"favorited"` + WorldId int `json:"worldId"` + Host string `json:"host"` + Name string `json:"name"` + Width int `json:"width"` + Height int `json:"height"` + TimeBetweenPixels int `json:"timeBetweenPixels"` + StartTime *time.Time `json:"startTime"` + EndTime *time.Time `json:"endTime"` + Favorites int `json:"favorites"` + Favorited bool `json:"favorited"` } func getWorld(w http.ResponseWriter, r *http.Request) { @@ -173,7 +173,7 @@ func getNewWorlds(w http.ResponseWriter, r *http.Request) { LIMIT $2 OFFSET $3` worlds, err := core.PostgresQueryJson[WorldData](query, address, pageLength, offset) if err != nil { - fmt.Println(err) + fmt.Println(err) routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve Worlds") return } @@ -490,12 +490,12 @@ func createCanvasDevnet(w http.ResponseWriter, r *http.Request) { routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid color palette") return } - for i, color := range palette { - colorFormatted := strings.TrimSpace(color) - colorFormatted = "0x" + colorFormatted - palette[i] = colorFormatted - } - paletteInput := strings.Join(palette, " ") + for i, color := range palette { + colorFormatted := strings.TrimSpace(color) + colorFormatted = "0x" + colorFormatted + palette[i] = colorFormatted + } + paletteInput := strings.Join(palette, " ") startTime, err := strconv.Atoi((*jsonBody)["start_time"]) if err != nil || startTime <= 0 { @@ -577,63 +577,63 @@ func unfavoriteWorldDevnet(w http.ResponseWriter, r *http.Request) { } func placeWorldPixelDevnet(w http.ResponseWriter, r *http.Request) { - // Disable this in production - if routeutils.NonProductionMiddleware(w, r) { - return - } - - jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid JSON request body") - return - } - - worldId, err := strconv.Atoi((*jsonBody)["worldId"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") - return - } - - position, err := strconv.Atoi((*jsonBody)["position"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid position") - return - } - - color, err := strconv.Atoi((*jsonBody)["color"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid color") - return - } - - timestamp, err := strconv.Atoi((*jsonBody)["timestamp"]) - if err != nil { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid time") - return - } - - //TODO: Validate position range - - // Validate color format (e.g., validate against allowed colors) - colorsLength, err := core.PostgresQueryOne[int]("SELECT COUNT(*) FROM WorldsColors") - if err != nil { - routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get colors count") - return - } - if color < 0 || color > *colorsLength { - routeutils.WriteErrorJson(w, http.StatusBadRequest, "Color out of range") - return - } - - shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.PlaceWorldPixelDevnet - contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") - - cmd := exec.Command(shellCmd, contract, "place_pixel", strconv.Itoa(worldId), strconv.Itoa(position), strconv.Itoa(color), strconv.Itoa(timestamp)) - _, err = cmd.Output() - if err != nil { - routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to place pixel on devnet") - return - } - - routeutils.WriteResultJson(w, "Pixel placed world") + // Disable this in production + if routeutils.NonProductionMiddleware(w, r) { + return + } + + jsonBody, err := routeutils.ReadJsonBody[map[string]string](r) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid JSON request body") + return + } + + worldId, err := strconv.Atoi((*jsonBody)["worldId"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid worldId") + return + } + + position, err := strconv.Atoi((*jsonBody)["position"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid position") + return + } + + color, err := strconv.Atoi((*jsonBody)["color"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid color") + return + } + + timestamp, err := strconv.Atoi((*jsonBody)["timestamp"]) + if err != nil { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid time") + return + } + + //TODO: Validate position range + + // Validate color format (e.g., validate against allowed colors) + colorsLength, err := core.PostgresQueryOne[int]("SELECT COUNT(*) FROM WorldsColors") + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to get colors count") + return + } + if color < 0 || color > *colorsLength { + routeutils.WriteErrorJson(w, http.StatusBadRequest, "Color out of range") + return + } + + shellCmd := core.ArtPeaceBackend.BackendConfig.Scripts.PlaceWorldPixelDevnet + contract := os.Getenv("CANVAS_FACTORY_CONTRACT_ADDRESS") + + cmd := exec.Command(shellCmd, contract, "place_pixel", strconv.Itoa(worldId), strconv.Itoa(position), strconv.Itoa(color), strconv.Itoa(timestamp)) + _, err = cmd.Output() + if err != nil { + routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to place pixel on devnet") + return + } + + routeutils.WriteResultJson(w, "Pixel placed world") }