Skip to content

Commit

Permalink
Merge pull request #277 from keep-starknet-strange/feat/websockets
Browse files Browse the repository at this point in the history
Feat: Full Homescreen, ws server, ...
  • Loading branch information
b-j-roberts authored Jan 13, 2025
2 parents 273546b + bad6cb1 commit 53a0683
Show file tree
Hide file tree
Showing 31 changed files with 773 additions and 135 deletions.
22 changes: 22 additions & 0 deletions backend/Dockerfile.websocket
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM golang:1.22.2-alpine

RUN apk add --no-cache bash curl git jq

# Copy over the configs
WORKDIR /configs
COPY ./configs/ .
COPY ./configs/docker-database.config.json ./database.config.json
COPY ./configs/docker-backend.config.json ./backend.config.json

# Copy over the app
WORKDIR /app
COPY ./backend/go.mod ./backend/go.sum ./
RUN go mod download
COPY ./backend .

# Build the app & run it
RUN go build -o web-sockets ./cmd/web-sockets/web-sockets.go

EXPOSE 8082

CMD ["./web-sockets"]
22 changes: 22 additions & 0 deletions backend/Dockerfile.websocket.prod
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
FROM --platform=linux/amd64 golang:1.22.2-alpine

RUN apk add --no-cache bash curl git jq

# Copy over the configs
WORKDIR /configs
COPY ./configs/ .
COPY ./configs/prod-database.config.json ./database.config.json
COPY ./configs/prod-backend.config.json ./backend.config.json

# Copy over the app
WORKDIR /app
COPY ./backend/go.mod ./backend/go.sum ./
RUN go mod download
COPY ./backend .

# Build the app & run it
RUN go build -o web-sockets ./cmd/web-sockets/web-sockets.go

EXPOSE 8082

CMD ["./web-sockets"]
1 change: 0 additions & 1 deletion backend/cmd/consumer/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,6 @@ func main() {

routes.InitBaseRoutes()
indexer.InitIndexerRoutes()
routes.InitWebsocketRoutes()
routes.InitNFTStaticRoutes()
routes.InitWorldsStaticRoutes()
indexer.StartMessageProcessor()
Expand Down
64 changes: 64 additions & 0 deletions backend/cmd/web-sockets/web-sockets.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package main

import (
"flag"

"github.com/keep-starknet-strange/art-peace/backend/config"
"github.com/keep-starknet-strange/art-peace/backend/core"
"github.com/keep-starknet-strange/art-peace/backend/routes"
)

func isFlagSet(name string) bool {
found := false
flag.Visit(func(f *flag.Flag) {
if f.Name == name {
found = true
}
})
return found
}

func main() {
roundsConfigFilename := flag.String("rounds-config", config.DefaultRoundsConfigPath, "Rounds config file")
canvasConfigFilename := flag.String("canvas-config", config.DefaultCanvasConfigPath, "Canvas config file")
databaseConfigFilename := flag.String("database-config", config.DefaultDatabaseConfigPath, "Database config file")
backendConfigFilename := flag.String("backend-config", config.DefaultBackendConfigPath, "Backend config file")
production := flag.Bool("production", false, "Production mode")

flag.Parse()

roundsConfig, err := config.LoadRoundsConfig(*roundsConfigFilename)
if err != nil {
panic(err)
}

canvasConfig, err := config.LoadCanvasConfig(*canvasConfigFilename)
if err != nil {
panic(err)
}

databaseConfig, err := config.LoadDatabaseConfig(*databaseConfigFilename)
if err != nil {
panic(err)
}

backendConfig, err := config.LoadBackendConfig(*backendConfigFilename)
if err != nil {
panic(err)
}

if isFlagSet("production") {
backendConfig.Production = *production
}

databases := core.NewDatabases(databaseConfig)
defer databases.Close()

core.ArtPeaceBackend = core.NewBackend(databases, roundsConfig, canvasConfig, backendConfig, false)

routes.InitBaseRoutes()
routes.InitWebsocketRoutes()
routes.StartWebsocketServer()

core.ArtPeaceBackend.Start(core.ArtPeaceBackend.BackendConfig.WsPort)
}
4 changes: 4 additions & 0 deletions backend/config/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,8 @@ type BackendConfig struct {
Host string `json:"host"`
Port int `json:"port"`
ConsumerPort int `json:"consumer_port"`
WsHost string `json:"ws_host"`
WsPort int `json:"ws_port"`
Scripts BackendScriptsConfig `json:"scripts"`
Production bool `json:"production"`
WebSocket WebSocketConfig `json:"websocket"`
Expand All @@ -57,6 +59,8 @@ var DefaultBackendConfig = BackendConfig{
Host: "localhost",
Port: 8080,
ConsumerPort: 8081,
WsHost: "localhost",
WsPort: 8082,
Scripts: BackendScriptsConfig{
PlacePixelDevnet: "../scripts/place_pixel.sh",
PlaceExtraPixelsDevnet: "../scripts/place_extra_pixels.sh",
Expand Down
6 changes: 3 additions & 3 deletions backend/routes/indexer/nft.go
Original file line number Diff line number Diff line change
Expand Up @@ -250,12 +250,12 @@ func processNFTMintedEvent(event IndexerEvent) {
return
}

message := map[string]interface{}{
"token_id": tokenId,
message := map[string]string{
"token_id": strconv.FormatUint(tokenId, 10),
"minter": minter,
"messageType": "nftMinted",
}
routeutils.SendWebSocketMessage(message)
routeutils.SendMessageToWSS(message)

// TODO: Response?
}
Expand Down
16 changes: 8 additions & 8 deletions backend/routes/indexer/pixel.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,12 +61,12 @@ func processPixelPlacedEvent(event IndexerEvent) {
}

// Send message to all connected clients
var message = map[string]interface{}{
"position": position,
"color": color,
var message = map[string]string{
"position": strconv.FormatInt(position, 10),
"color": strconv.FormatInt(color, 10),
"messageType": "colorPixel",
}
routeutils.SendWebSocketMessage(message)
routeutils.SendMessageToWSS(message)
}

func revertPixelPlacedEvent(event IndexerEvent) {
Expand Down Expand Up @@ -105,12 +105,12 @@ func revertPixelPlacedEvent(event IndexerEvent) {
}

// Send message to all connected clients
var message = map[string]interface{}{
"position": position,
"color": oldColor,
var message = map[string]string{
"position": strconv.FormatInt(position, 10),
"color": strconv.Itoa(*oldColor),
"messageType": "colorPixel",
}
routeutils.SendWebSocketMessage(message)
routeutils.SendMessageToWSS(message)
}

func processBasicPixelPlacedEvent(event IndexerEvent) {
Expand Down
29 changes: 9 additions & 20 deletions backend/routes/indexer/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -184,22 +184,11 @@ func processCanvasCreatedEvent(event IndexerEvent) {
}

// After world creation
var message = map[string]interface{}{
var message = map[string]string{
"messageType": "newWorld",
"worldId": canvasId,
"world": map[string]interface{}{
"worldId": canvasId,
"host": host,
"name": name,
"uniqueName": uniqueName,
"width": width,
"height": height,
"timeBetweenPixels": timeBetweenPixels,
"startTime": startTime,
"endTime": endTime,
},
}
routeutils.SendWebSocketMessage(message)
"worldId": strconv.Itoa(int(canvasId)),
}
routeutils.SendMessageToWSS(message)
}

func revertCanvasCreatedEvent(event IndexerEvent) {
Expand Down Expand Up @@ -500,13 +489,13 @@ func processCanvasPixelPlacedEvent(event IndexerEvent) {
}
}

var message = map[string]interface{}{
"worldId": canvasId,
"position": pos,
"color": colorVal,
var message = map[string]string{
"worldId": strconv.Itoa(int(canvasId)),
"position": strconv.Itoa(int(pos)),
"color": strconv.Itoa(int(colorVal)),
"messageType": "colorWorldPixel",
}
routeutils.SendWebSocketMessage(message)
routeutils.SendMessageToWSS(message)
}

func revertCanvasPixelPlacedEvent(event IndexerEvent) {
Expand Down
16 changes: 15 additions & 1 deletion backend/routes/utils/responses.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"net/http"
"strconv"
"strings"

"github.com/gorilla/websocket"
Expand Down Expand Up @@ -59,7 +60,7 @@ func WriteDataJson(w http.ResponseWriter, data string) {
w.Write(BasicDataJson(data))
}

func SendWebSocketMessage(message map[string]interface{}) {
func SendWebSocketMessage(message map[string]string) {
messageBytes, err := json.Marshal(message)
if err != nil {
fmt.Println("Failed to marshal websocket message")
Expand All @@ -80,3 +81,16 @@ func SendWebSocketMessage(message map[string]interface{}) {
}
core.ArtPeaceBackend.WSConnectionsLock.Unlock()
}

func SendMessageToWSS(message map[string]string) {
websocketHost := core.ArtPeaceBackend.BackendConfig.WsHost + ":" + strconv.Itoa(core.ArtPeaceBackend.BackendConfig.WsPort) + "/ws-msg"
messageBytes, err := json.Marshal(message)
if err != nil {
fmt.Println("Failed to marshal websocket message")
return
}
_, err = http.Post("http://"+websocketHost, "application/json", strings.NewReader(string(messageBytes)))
if err != nil {
fmt.Println("Failed to send message to websocket server", err)
}
}
31 changes: 31 additions & 0 deletions backend/routes/websocket.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,41 @@ import (
"github.com/gorilla/websocket"

"github.com/keep-starknet-strange/art-peace/backend/core"
routeutils "github.com/keep-starknet-strange/art-peace/backend/routes/utils"
)

var WsMsgQueue chan map[string]string

func InitWebsocketRoutes() {
WsMsgQueue = make(chan map[string]string, 10000)
http.HandleFunc("/ws", wsEndpoint)
http.HandleFunc("/ws-msg", wsMsgEndpoint)
}

func wsMsgEndpoint(w http.ResponseWriter, r *http.Request) {
// TODO: Only allow consumer to send messages
msg, err := routeutils.ReadJsonBody[map[string]string](r)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusBadRequest, "Invalid request body")
return
}

WsMsgQueue <- *msg
routeutils.WriteResultJson(w, "WS message added to queue")
}

func wsWriter() {
for {
msg := <-WsMsgQueue
routeutils.SendWebSocketMessage(msg)
}
}

func StartWebsocketServer() {
go wsWriter()
go wsWriter()
go wsWriter()
go wsWriter()
}

func wsReader(conn *websocket.Conn) {
Expand Down
26 changes: 25 additions & 1 deletion backend/routes/worlds.go
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,31 @@ func getWorld(w http.ResponseWriter, r *http.Request) {
return
}

world, err := core.PostgresQueryOneJson[WorldData]("SELECT * FROM worlds WHERE world_id = $1", worldId)
address := r.URL.Query().Get("address")
if address == "" {
address = "0"
}

query := `
SELECT
worlds.*,
COALESCE(worldfavorites.favorite_count, 0) AS favorites,
COALESCE((SELECT true FROM worldfavorites WHERE user_address = $1 AND worldfavorites.world_id = worlds.world_id), false) as favorited
FROM
worlds
LEFT JOIN (
SELECT
world_id,
COUNT(*) AS favorite_count
FROM
worldfavorites
GROUP BY
world_id
) worldfavorites ON worlds.world_id = worldfavorites.world_id
WHERE
worlds.world_id = $2`

world, err := core.PostgresQueryOneJson[WorldData](query, address, worldId)
if err != nil {
routeutils.WriteErrorJson(w, http.StatusInternalServerError, "Failed to retrieve World")
return
Expand Down
2 changes: 2 additions & 0 deletions configs/backend.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"host": "localhost",
"port": 8080,
"consumer_port": 8081,
"ws_host": "localhost",
"ws_port": 8082,
"scripts": {
"place_pixel_devnet": "../tests/integration/local/place_pixel.sh",
"place_extra_pixels_devnet": "../tests/integration/local/place_extra_pixels.sh",
Expand Down
2 changes: 1 addition & 1 deletion configs/canvas.config.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"canvas": {
"width": 518,
"width": 528,
"height": 396
},
"colors": [
Expand Down
2 changes: 2 additions & 0 deletions configs/docker-backend.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"host": "localhost",
"port": 8080,
"consumer_port": 8081,
"ws_host": "art-peace-websockets-1",
"ws_port": 8082,
"scripts": {
"place_pixel_devnet": "/scripts/place_pixel.sh",
"place_extra_pixels_devnet": "/scripts/place_extra_pixels.sh",
Expand Down
2 changes: 2 additions & 0 deletions configs/prod-backend.config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"host": "backend.art-peace-sepolia.svc.cluster.local",
"port": 8080,
"consumer_port": 8081,
"ws_host": "websockets.art-peace-sepolia.svc.cluster.local",
"ws_port": 8082,
"scripts": {
"place_pixel_devnet": "/scripts/place_pixel.sh",
"place_extra_pixels_devnet": "/scripts/place_extra_pixels.sh",
Expand Down
9 changes: 9 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ services:
volumes:
- nfts:/app/nfts
- worlds:/app/worlds
websockets:
build:
dockerfile: backend/Dockerfile.websocket
context: .
ports:
- 8082:8082
links:
- consumer
restart: always
devnet:
image: shardlabs/starknet-devnet-rs:0.0.3
command:
Expand Down
Loading

0 comments on commit 53a0683

Please sign in to comment.