Draft: big refactor
This commit is contained in:
3
Makefile
3
Makefile
@@ -3,6 +3,7 @@
|
|||||||
SWAG ?= ~/go/bin/swag
|
SWAG ?= ~/go/bin/swag
|
||||||
|
|
||||||
TARGET := $(shell find . -type f -name '*.go')
|
TARGET := $(shell find . -type f -name '*.go')
|
||||||
|
DEST := server/docs
|
||||||
|
|
||||||
all: swagger docker install
|
all: swagger docker install
|
||||||
|
|
||||||
@@ -17,7 +18,7 @@ game: $(TARGET)
|
|||||||
|
|
||||||
swagger:
|
swagger:
|
||||||
$(SWAG) fmt
|
$(SWAG) fmt
|
||||||
$(SWAG) init -o backend/docs -g cmd/serve/backend.go -pdl 1
|
$(SWAG) init -o $(DEST) -g cmd/serve/backend.go -pdl 1
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
-rm -f game
|
-rm -f game
|
||||||
|
|||||||
@@ -1,29 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
|
||||||
"github.com/uptrace/bunrouter"
|
|
||||||
)
|
|
||||||
|
|
||||||
// GetLobbyRooms
|
|
||||||
//
|
|
||||||
// @Router /api/lobby/rooms [get]
|
|
||||||
func (self *Handlers) GetLobbyRooms(
|
|
||||||
w http.ResponseWriter,
|
|
||||||
req bunrouter.Request,
|
|
||||||
) error {
|
|
||||||
ctx := req.Context()
|
|
||||||
|
|
||||||
rooms, err := self.db.GetRooms(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return middlewares.HTTPError{
|
|
||||||
StatusCode: http.StatusInternalServerError,
|
|
||||||
Message: "failed to get room",
|
|
||||||
OriginError: err,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return bunrouter.JSON(w, rooms)
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
package api
|
|
||||||
|
|
||||||
import (
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/interfaces"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Handlers struct {
|
|
||||||
db interfaces.Database
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewHandlers(db interfaces.Database) *Handlers {
|
|
||||||
return &Handlers{db: db}
|
|
||||||
}
|
|
||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
tea "github.com/charmbracelet/bubbletea"
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
"github.com/go-resty/resty/v2"
|
"github.com/go-resty/resty/v2"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var RootCmd = &cobra.Command{
|
var RootCmd = &cobra.Command{
|
||||||
@@ -15,25 +16,35 @@ var RootCmd = &cobra.Command{
|
|||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
appname := "game-tui"
|
appname := "game-tui"
|
||||||
tracing.InitTracer(appname)
|
tracing.InitTracer(appname)
|
||||||
|
defer tracing.DeferTracer()
|
||||||
|
|
||||||
|
tracing.Logger.
|
||||||
|
Info("client up")
|
||||||
|
|
||||||
client := resty.New().
|
client := resty.New().
|
||||||
SetBaseURL(args[0]).
|
SetBaseURL(args[0]).
|
||||||
SetDisableWarn(true)
|
SetDisableWarn(true)
|
||||||
|
|
||||||
queue := []*tea.Program{}
|
queue := []*tea.Program{}
|
||||||
queue = append(queue,
|
queue = append(queue, tea.NewProgram(
|
||||||
tea.NewProgram(plays.NewLanding(plays.NewBase(client))))
|
plays.NewLanding(plays.NewBase(client))))
|
||||||
|
|
||||||
for len(queue) > 0 {
|
for len(queue) > 0 {
|
||||||
program := queue[0]
|
program := queue[0]
|
||||||
queue = queue[1:]
|
queue = queue[1:]
|
||||||
|
|
||||||
|
tracing.Logger.Info("run program")
|
||||||
|
|
||||||
res, err := program.Run()
|
res, err := program.Run()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tracing.Logger.Error("program failed",
|
||||||
|
zap.Error(err))
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
err = res.(plays.Next).Next(&queue)
|
err = res.(plays.Next).Next(&queue)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
tracing.Logger.Error("failed to generate next program",
|
||||||
|
zap.Error(err))
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -41,4 +52,7 @@ var RootCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
RootCmd.Flags().
|
||||||
|
String("otel-endpoint", "localhost:4317",
|
||||||
|
"endpoint for otlp exporter to connect")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,14 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/implements"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/backend/api"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/backend/auth"
|
||||||
|
_ "gitea.konchin.com/ytshih/inp2025/game/server/docs"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/rooms"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
httpSwagger "github.com/swaggo/http-swagger"
|
httpSwagger "github.com/swaggo/http-swagger"
|
||||||
@@ -15,36 +23,29 @@ import (
|
|||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
"go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
_ "gitea.konchin.com/ytshih/inp2025/game/backend/docs"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/handlers/api"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/handlers/auth"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/implements"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// @title Intro. to Network Programming Game
|
// @title Intro. to Network Programming Game
|
||||||
// @version 0.0.1-alpha
|
// @version 0.0.1-alpha
|
||||||
// @license.name 0BSD
|
// @license.name 0BSD
|
||||||
// @securityDefinitions.basic BasicAuth
|
// @securityDefinitions.basic BasicAuth
|
||||||
// @BasePath /
|
// @BasePath /
|
||||||
var backendCmd = &cobra.Command{
|
var backendCmd = &cobra.Command{
|
||||||
Use: "backend",
|
Use: "backend",
|
||||||
Short: "Game backend",
|
Short: "Game backend",
|
||||||
Run: func(cmd *cobra.Command, args []string) {
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
ctx := cmd.Context()
|
ctx := cmd.Context()
|
||||||
tracing.InitTracer("game backend")
|
tracing.InitTracer("game backend")
|
||||||
|
defer tracing.DeferTracer()
|
||||||
|
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Debug("connect to sql",
|
Debug("connect to sql",
|
||||||
zap.String("sql.url",
|
zap.String("sql.url",
|
||||||
"file:"+viper.GetString("sqlite-file")+"?cache=shared&mode=rwc"))
|
"file:"+viper.GetString("sqlite-file")+"?cache=shared&mode=rwc"))
|
||||||
sqldb, err := sql.Open(sqliteshim.ShimName,
|
sqldb, err := sql.Open(sqliteshim.ShimName,
|
||||||
"file:"+viper.GetString("sqlite-file")+"?cache=shared&mode=rwc")
|
"file:"+viper.GetString("sqlite-file")+"?cache=shared&mode=rwc")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Panic("failed to init sqlite",
|
Panic("failed to init sqlite",
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -52,15 +53,16 @@ var backendCmd = &cobra.Command{
|
|||||||
bunDB := bun.NewDB(sqldb, sqlitedialect.New())
|
bunDB := bun.NewDB(sqldb, sqlitedialect.New())
|
||||||
bunDB.AddQueryHook(bunotel.NewQueryHook(bunotel.WithDBName("sqlite")))
|
bunDB.AddQueryHook(bunotel.NewQueryHook(bunotel.WithDBName("sqlite")))
|
||||||
if err := utils.InitDB(ctx, bunDB); err != nil {
|
if err := utils.InitDB(ctx, bunDB); err != nil {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Panic("failed to init db schema",
|
Panic("failed to init db schema",
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
db := implements.NewBunDatabase(bunDB)
|
db := implements.NewBunDatabase(bunDB)
|
||||||
|
roomManager := rooms.NewRoomManager()
|
||||||
authHandlers := auth.NewHandlers(db)
|
authHandlers := auth.NewHandlers(db)
|
||||||
apiHandlers := api.NewHandlers(db)
|
apiHandlers := api.NewHandlers(db, roomManager)
|
||||||
middlewareHandlers := middlewares.NewHandlers(db)
|
middlewareHandlers := middlewares.NewHandlers(db)
|
||||||
|
|
||||||
router := bunrouter.New()
|
router := bunrouter.New()
|
||||||
@@ -90,8 +92,20 @@ var backendCmd = &cobra.Command{
|
|||||||
Use(middlewareHandlers.Auth)
|
Use(middlewareHandlers.Auth)
|
||||||
apiGroup.GET("/lobby/users",
|
apiGroup.GET("/lobby/users",
|
||||||
apiHandlers.GetLobbyUsers)
|
apiHandlers.GetLobbyUsers)
|
||||||
|
apiGroup.GET("/lobby/rooms",
|
||||||
|
apiHandlers.GetLobbyRooms)
|
||||||
|
apiGroup.POST("/rooms",
|
||||||
|
apiHandlers.CreateRoom)
|
||||||
|
apiGroup.GET("/rooms/:id",
|
||||||
|
apiHandlers.GetRoom)
|
||||||
|
apiGroup.POST("/rooms/:id/join",
|
||||||
|
apiHandlers.JoinRoom)
|
||||||
|
apiGroup.GET("/rooms/:id/state",
|
||||||
|
apiHandlers.WSGetRoomState)
|
||||||
|
apiGroup.POST("/rooms/:id/guess",
|
||||||
|
apiHandlers.PostGuess)
|
||||||
|
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Info("http server up",
|
Info("http server up",
|
||||||
zap.String("http.port", viper.GetString("port")))
|
zap.String("http.port", viper.GetString("port")))
|
||||||
|
|
||||||
@@ -101,6 +115,8 @@ var backendCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
backendCmd.Flags().
|
||||||
|
String("otel-endpoint", "backend-otelcol:4317", "otelcol endpoint")
|
||||||
backendCmd.Flags().
|
backendCmd.Flags().
|
||||||
String("port", "8080", "Port to listen on")
|
String("port", "8080", "Port to listen on")
|
||||||
backendCmd.Flags().
|
backendCmd.Flags().
|
||||||
|
|||||||
@@ -1,9 +1,28 @@
|
|||||||
---
|
---
|
||||||
services:
|
services:
|
||||||
|
backend-otelcol:
|
||||||
|
image: otel/opentelemetry-collector-contrib
|
||||||
|
command:
|
||||||
|
- --config=/etc/otelcol/config.yaml
|
||||||
|
volumes:
|
||||||
|
- ./docker/otelcol/config.yaml:/etc/otelcol/config.yaml
|
||||||
|
|
||||||
backend:
|
backend:
|
||||||
build:
|
build:
|
||||||
context: .
|
context: .
|
||||||
env_file: ./backend/.env
|
env_file: ./server/.env
|
||||||
ports:
|
ports:
|
||||||
- 8081:8080
|
- 8081:8080
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
|
depends_on:
|
||||||
|
- backend-otelcol
|
||||||
|
|
||||||
|
client-otelcol:
|
||||||
|
image: otel/opentelemetry-collector-contrib
|
||||||
|
command:
|
||||||
|
- --config=/etc/otelcol/config.yaml
|
||||||
|
ports:
|
||||||
|
- 4317:4317
|
||||||
|
- 4318:4318
|
||||||
|
volumes:
|
||||||
|
- ./docker/otelcol/config.yaml:/etc/otelcol/config.yaml
|
||||||
|
|||||||
@@ -1,19 +1,17 @@
|
|||||||
receivers:
|
receivers:
|
||||||
otlp:
|
otlp:
|
||||||
protocols:
|
protocols:
|
||||||
http: 0.0.0.0:4138
|
grpc:
|
||||||
grpc: 0.0.0.0:4137
|
endpoint: 0.0.0.0:4317
|
||||||
|
http:
|
||||||
processors:
|
endpoint: 0.0.0.0:4318
|
||||||
batch:
|
|
||||||
|
|
||||||
exporters:
|
exporters:
|
||||||
logging:
|
debug:
|
||||||
loglevel: debug
|
verbosity: detailed
|
||||||
|
|
||||||
service:
|
service:
|
||||||
pipelines:
|
pipelines:
|
||||||
logs:
|
logs:
|
||||||
receivers: [otlp]
|
receivers: [otlp]
|
||||||
processors: [batch]
|
exporters: [debug]
|
||||||
exporters: [logging]
|
|
||||||
|
|||||||
45
go.mod
45
go.mod
@@ -3,6 +3,12 @@ module gitea.konchin.com/ytshih/inp2025/game
|
|||||||
go 1.24.5
|
go 1.24.5
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/charmbracelet/bubbles v0.21.0
|
||||||
|
github.com/charmbracelet/bubbletea v1.3.7
|
||||||
|
github.com/charmbracelet/lipgloss v1.1.0
|
||||||
|
github.com/go-resty/resty/v2 v2.16.5
|
||||||
|
github.com/google/uuid v1.6.0
|
||||||
|
github.com/gorilla/websocket v1.5.3
|
||||||
github.com/spf13/cobra v1.10.1
|
github.com/spf13/cobra v1.10.1
|
||||||
github.com/spf13/viper v1.20.1
|
github.com/spf13/viper v1.20.1
|
||||||
github.com/swaggo/http-swagger v1.3.4
|
github.com/swaggo/http-swagger v1.3.4
|
||||||
@@ -12,9 +18,12 @@ require (
|
|||||||
github.com/uptrace/bun/driver/sqliteshim v1.2.15
|
github.com/uptrace/bun/driver/sqliteshim v1.2.15
|
||||||
github.com/uptrace/bun/extra/bunotel v1.2.15
|
github.com/uptrace/bun/extra/bunotel v1.2.15
|
||||||
github.com/uptrace/bunrouter v1.0.23
|
github.com/uptrace/bunrouter v1.0.23
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2
|
github.com/vmihailenco/msgpack/v5 v5.4.1
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0
|
||||||
go.opentelemetry.io/otel v1.38.0
|
go.opentelemetry.io/otel v1.38.0
|
||||||
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0
|
||||||
|
go.opentelemetry.io/otel/sdk/log v0.14.0
|
||||||
go.opentelemetry.io/otel/trace v1.38.0
|
go.opentelemetry.io/otel/trace v1.38.0
|
||||||
go.uber.org/zap v1.27.0
|
go.uber.org/zap v1.27.0
|
||||||
)
|
)
|
||||||
@@ -23,11 +32,8 @@ require (
|
|||||||
github.com/KyleBanks/depth v1.2.1 // indirect
|
github.com/KyleBanks/depth v1.2.1 // indirect
|
||||||
github.com/atotto/clipboard v0.1.4 // indirect
|
github.com/atotto/clipboard v0.1.4 // indirect
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||||
github.com/cenkalti/backoff/v5 v5.0.2 // indirect
|
github.com/cenkalti/backoff/v5 v5.0.3 // indirect
|
||||||
github.com/charmbracelet/bubbles v0.21.0 // indirect
|
|
||||||
github.com/charmbracelet/bubbletea v1.3.7 // indirect
|
|
||||||
github.com/charmbracelet/colorprofile v0.3.2 // indirect
|
github.com/charmbracelet/colorprofile v0.3.2 // indirect
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 // indirect
|
|
||||||
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
github.com/charmbracelet/x/ansi v0.10.1 // indirect
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
github.com/charmbracelet/x/cellbuf v0.0.13 // indirect
|
||||||
github.com/charmbracelet/x/term v0.2.1 // indirect
|
github.com/charmbracelet/x/term v0.2.1 // indirect
|
||||||
@@ -41,10 +47,8 @@ require (
|
|||||||
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
github.com/go-openapi/jsonreference v0.20.2 // indirect
|
||||||
github.com/go-openapi/spec v0.20.9 // indirect
|
github.com/go-openapi/spec v0.20.9 // indirect
|
||||||
github.com/go-openapi/swag v0.22.3 // indirect
|
github.com/go-openapi/swag v0.22.3 // indirect
|
||||||
github.com/go-resty/resty/v2 v2.16.5 // indirect
|
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.2.1 // indirect
|
||||||
github.com/google/uuid v1.6.0 // indirect
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 // indirect
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 // indirect
|
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/jinzhu/inflection v1.0.0 // indirect
|
github.com/jinzhu/inflection v1.0.0 // indirect
|
||||||
github.com/josharian/intern v1.0.0 // indirect
|
github.com/josharian/intern v1.0.0 // indirect
|
||||||
@@ -71,34 +75,23 @@ require (
|
|||||||
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
|
github.com/swaggo/files v0.0.0-20220610200504-28940afbdbfe // indirect
|
||||||
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
github.com/tmthrgd/go-hex v0.0.0-20190904060850-447a3041c3bc // indirect
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 // indirect
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 // indirect
|
|
||||||
github.com/uptrace/uptrace-go v1.37.0 // indirect
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 // indirect
|
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
|
||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0 // indirect
|
go.opentelemetry.io/otel/log v0.14.0 // indirect
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.13.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/log v0.13.0 // indirect
|
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
go.opentelemetry.io/otel/sdk v1.38.0 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/log v0.13.0 // indirect
|
go.opentelemetry.io/proto/otlp v1.7.1 // indirect
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 // indirect
|
|
||||||
go.opentelemetry.io/proto/otlp v1.7.0 // indirect
|
|
||||||
go.uber.org/multierr v1.11.0 // indirect
|
go.uber.org/multierr v1.11.0 // indirect
|
||||||
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect
|
golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc // indirect
|
||||||
golang.org/x/net v0.42.0 // indirect
|
golang.org/x/net v0.43.0 // indirect
|
||||||
golang.org/x/sys v0.36.0 // indirect
|
golang.org/x/sys v0.36.0 // indirect
|
||||||
golang.org/x/text v0.28.0 // indirect
|
golang.org/x/text v0.28.0 // indirect
|
||||||
golang.org/x/tools v0.35.0 // indirect
|
golang.org/x/tools v0.35.0 // indirect
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 // indirect
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 // indirect
|
||||||
google.golang.org/grpc v1.73.0 // indirect
|
google.golang.org/grpc v1.75.0 // indirect
|
||||||
google.golang.org/protobuf v1.36.6 // indirect
|
google.golang.org/protobuf v1.36.8 // indirect
|
||||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||||
modernc.org/libc v1.66.3 // indirect
|
modernc.org/libc v1.66.3 // indirect
|
||||||
modernc.org/mathutil v1.7.1 // indirect
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
|||||||
85
go.sum
85
go.sum
@@ -4,22 +4,18 @@ github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z
|
|||||||
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||||
github.com/cenkalti/backoff/v5 v5.0.2 h1:rIfFVxEf1QsI7E1ZHfp/B4DF/6QBAUhmgkxc0H7Zss8=
|
github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=
|
||||||
github.com/cenkalti/backoff/v5 v5.0.2/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=
|
||||||
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
github.com/charmbracelet/bubbles v0.21.0 h1:9TdC97SdRVg/1aaXNVWfFH3nnLAwOXr8Fn6u6mfQdFs=
|
||||||
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
github.com/charmbracelet/bubbles v0.21.0/go.mod h1:HF+v6QUR4HkEpz62dx7ym2xc71/KBHg+zKwJtMw+qtg=
|
||||||
github.com/charmbracelet/bubbletea v1.3.7 h1:FNaEEFEenOEPnZsY9MI64thl2c84MI66+1QaQbxGOl4=
|
github.com/charmbracelet/bubbletea v1.3.7 h1:FNaEEFEenOEPnZsY9MI64thl2c84MI66+1QaQbxGOl4=
|
||||||
github.com/charmbracelet/bubbletea v1.3.7/go.mod h1:PEOcbQCNzJ2BYUd484kHPO5g3kLO28IffOdFeI2EWus=
|
github.com/charmbracelet/bubbletea v1.3.7/go.mod h1:PEOcbQCNzJ2BYUd484kHPO5g3kLO28IffOdFeI2EWus=
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
|
||||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
|
||||||
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
|
github.com/charmbracelet/colorprofile v0.3.2 h1:9J27WdztfJQVAQKX2WOlSSRB+5gaKqqITmrvb1uTIiI=
|
||||||
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
|
github.com/charmbracelet/colorprofile v0.3.2/go.mod h1:mTD5XzNeWHj8oqHb+S1bssQb7vIHbepiebQ2kPKVKbI=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||||
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
github.com/charmbracelet/x/ansi v0.10.1 h1:rL3Koar5XvX0pHGfovN03f5cxLbCF2YvLeyz7D2jVDQ=
|
||||||
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
github.com/charmbracelet/x/ansi v0.10.1/go.mod h1:3RQDQ6lDnROptfpWuUVIUG64bD2g2BgntdxH0Ya5TeE=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
github.com/charmbracelet/x/cellbuf v0.0.13 h1:/KBBKHuVRbq1lYx5BzEHBAFBP8VcQzJejZ/IA3iR28k=
|
||||||
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
github.com/charmbracelet/x/cellbuf v0.0.13/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||||
@@ -61,14 +57,18 @@ github.com/go-resty/resty/v2 v2.16.5 h1:hBKqmWrr7uRc3euHVqmh1HTHcKn99Smr7o5spptd
|
|||||||
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
github.com/go-resty/resty/v2 v2.16.5/go.mod h1:hkJtXbA2iKHzJheXYvQ8snQES5ZLGKMwQ07xAwp/fiA=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
github.com/go-viper/mapstructure/v2 v2.2.1 h1:ZAaOCxANMuZx5RCeg0mBdEZk7DZasvvZIxtHqx8aGss=
|
||||||
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
github.com/go-viper/mapstructure/v2 v2.2.1/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||||
|
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
|
||||||
|
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
||||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
|
||||||
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1 h1:X5VWvz21y3gzm9Nw/kaUeku/1+uBhcekkmy4IkffJww=
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.1/go.mod h1:Zanoh4+gvIgluNqcfMVTJueD4wSS5hT7zTt4Mrutd90=
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU=
|
||||||
|
github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||||
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
|
||||||
@@ -167,12 +167,6 @@ github.com/uptrace/bunrouter v1.0.23 h1:Bi7NKw3uCQkcA/GUCtDNPq5LE5UdR9pe+UyWbjHB
|
|||||||
github.com/uptrace/bunrouter v1.0.23/go.mod h1:O3jAcl+5qgnF+ejhgkmbceEk0E/mqaK+ADOocdNpY8M=
|
github.com/uptrace/bunrouter v1.0.23/go.mod h1:O3jAcl+5qgnF+ejhgkmbceEk0E/mqaK+ADOocdNpY8M=
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c=
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2 h1:ZjUj9BLYf9PEqBn8W/OapxhPjVRdC6CsXTdULHsyk5c=
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ=
|
github.com/uptrace/opentelemetry-go-extra/otelsql v0.3.2/go.mod h1:O8bHQfyinKwTXKkiKNGmLQS7vRsqRxIQTFZpYpHK3IQ=
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2 h1:3/aHKUq7qaFMWxyQV0W2ryNgg8x8rVeKVA20KJUkfS0=
|
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelutil v0.3.2/go.mod h1:Zit4b8AQXaXvA68+nzmbyDzqiyFRISyw1JiD5JqUBjw=
|
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2 h1:cj/Z6FKTTYBnstI0Lni9PA+k2foounKIPUmj1LBwNiQ=
|
|
||||||
github.com/uptrace/opentelemetry-go-extra/otelzap v0.3.2/go.mod h1:LDaXk90gKEC2nC7JH3Lpnhfu+2V7o/TsqomJJmqA39o=
|
|
||||||
github.com/uptrace/uptrace-go v1.37.0 h1:9ohbWB0qZEfcPLFbfqAAt5wz2rcBmL60/QqkOkvqYOs=
|
|
||||||
github.com/uptrace/uptrace-go v1.37.0/go.mod h1:3xAdXLVyEoqvRwuj3D/n1s9bLl7Ok+OnNaW889fvtDQ=
|
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
github.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=
|
||||||
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
github.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=
|
||||||
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
github.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=
|
||||||
@@ -181,38 +175,32 @@ github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavM
|
|||||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0 h1:aBKdhLVieqvwWe9A79UHI/0vgp2t/s2euY8X59pGRlw=
|
||||||
|
go.opentelemetry.io/contrib/bridges/otelzap v0.13.0/go.mod h1:SYqtxLQE7iINgh6WFuVi2AI70148B8EI35DSk0Wr8m4=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg=
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0 h1:ZIt0ya9/y4WyRIzfLC8hQRRsWg0J9M9GyaGtIMiElZI=
|
|
||||||
go.opentelemetry.io/contrib/instrumentation/runtime v0.62.0/go.mod h1:F1aJ9VuiKWOlWwKdTYDUp1aoS0HzQxg38/VLxKmhm5U=
|
|
||||||
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
|
||||||
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.13.0 h1:zUfYw8cscHHLwaY8Xz3fiJu+R59xBnkgq2Zr1lwmK/0=
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0 h1:OMqPldHt79PqWKOMYIAQs3CxAi7RLgPxwfFSwr4ZxtM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploghttp v0.13.0/go.mod h1:514JLMCcFLQFS8cnTepOk6I09cKWJ5nGHBxHrMJ8Yfg=
|
go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc v0.14.0/go.mod h1:1biG4qiqTxKiUCtoWDPpL3fB3KxVwCiGw81j3nKMuHE=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0 h1:9PgnL3QNlj10uGxExowIDIZu66aVBwWhXmbOp1pa6RA=
|
go.opentelemetry.io/otel/log v0.14.0 h1:2rzJ+pOAZ8qmZ3DDHg73NEKzSZkhkGIua9gXtxNGgrM=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.37.0/go.mod h1:0ineDcLELf6JmKfuo0wvvhAVMuxWFYvkTin2iV4ydPQ=
|
go.opentelemetry.io/otel/log v0.14.0/go.mod h1:5jRG92fEAgx0SU/vFPxmJvhIuDU9E1SUnEQrMlJpOno=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM=
|
go.opentelemetry.io/otel/log/logtest v0.14.0 h1:BGTqNeluJDK2uIHAY8lRqxjVAYfqgcaTbVk1n3MWe5A=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M=
|
go.opentelemetry.io/otel/log/logtest v0.14.0/go.mod h1:IuguGt8XVP4XA4d2oEEDMVDBBCesMg8/tSGWDjuKfoA=
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 h1:bDMKF3RUSxshZ5OjOTi8rsHGaPKsAt76FaqgvIUySLc=
|
|
||||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0/go.mod h1:dDT67G/IkA46Mr2l9Uj7HsQVwsjASyV9SjGofsiUZDA=
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0 h1:SNhVp/9q4Go/XHBkQ1/d5u9P/U+L1yaGPoi0x+mStaI=
|
|
||||||
go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.37.0/go.mod h1:tx8OOlGH6R4kLV67YaYO44GFXloEjGPZuMjEkaaqIp4=
|
|
||||||
go.opentelemetry.io/otel/log v0.6.0 h1:nH66tr+dmEgW5y+F9LanGJUBYPrRgP4g2EkmPE3LeK8=
|
|
||||||
go.opentelemetry.io/otel/log v0.6.0/go.mod h1:KdySypjQHhP069JX0z/t26VHwa8vSwzgaKmXtIB3fJM=
|
|
||||||
go.opentelemetry.io/otel/log v0.13.0 h1:yoxRoIZcohB6Xf0lNv9QIyCzQvrtGZklVbdCoyb7dls=
|
|
||||||
go.opentelemetry.io/otel/log v0.13.0/go.mod h1:INKfG4k1O9CL25BaM1qLe0zIedOpvlS5Z7XgSbmN83E=
|
|
||||||
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
|
||||||
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E=
|
||||||
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg=
|
||||||
go.opentelemetry.io/otel/sdk/log v0.13.0 h1:I3CGUszjM926OphK8ZdzF+kLqFvfRY/IIoFq/TjwfaQ=
|
go.opentelemetry.io/otel/sdk/log v0.14.0 h1:JU/U3O7N6fsAXj0+CXz21Czg532dW2V4gG1HE/e8Zrg=
|
||||||
go.opentelemetry.io/otel/sdk/log v0.13.0/go.mod h1:lOrQyCCXmpZdN7NchXb6DOZZa1N5G1R2tm5GMMTpDBw=
|
go.opentelemetry.io/otel/sdk/log v0.14.0/go.mod h1:imQvII+0ZylXfKU7/wtOND8Hn4OpT3YUoIgqJVksUkM=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0 h1:Ijbtz+JKXl8T2MngiwqBlPaHqc4YCaP/i13Qrow6gAM=
|
||||||
|
go.opentelemetry.io/otel/sdk/log/logtest v0.14.0/go.mod h1:dCU8aEL6q+L9cYTqcVOk8rM9Tp8WdnHOPLiBgp0SGOA=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM=
|
||||||
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
|
||||||
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
|
||||||
go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os=
|
go.opentelemetry.io/proto/otlp v1.7.1 h1:gTOMpGDb0WTBOP8JaO72iL3auEZhVmAQg4ipjOVAtj4=
|
||||||
go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo=
|
go.opentelemetry.io/proto/otlp v1.7.1/go.mod h1:b2rVh6rfI/s2pHWNlB7ILJcRALpcNDzKhACevjI+ZnE=
|
||||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||||
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
|
||||||
@@ -224,36 +212,35 @@ golang.org/x/exp v0.0.0-20250711185948-6ae5c78190dc/go.mod h1:A+z0yzpGtvnG90cToK
|
|||||||
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
golang.org/x/mod v0.26.0 h1:EGMPT//Ezu+ylkCijjPc+f4Aih7sZvaAr+O3EHBxvZg=
|
||||||
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
golang.org/x/mod v0.26.0/go.mod h1:/j6NAhSk8iQ723BGAUyoAcn7SlD7s15Dp9Nd/SfeaFQ=
|
||||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs=
|
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
|
||||||
golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8=
|
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
|
||||||
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw=
|
||||||
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA=
|
||||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.35.0 h1:vz1N37gP5bs89s7He8XuIYXpyY0+QlsKmzipCbUtyxI=
|
|
||||||
golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
|
||||||
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
|
||||||
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||||
golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4=
|
|
||||||
golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU=
|
|
||||||
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
golang.org/x/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng=
|
||||||
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU=
|
||||||
|
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
||||||
|
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||||
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0=
|
||||||
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw=
|
||||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7 h1:FiusG7LWj+4byqhbvmB+Q93B/mOxJLN2DTozDuZm4EU=
|
gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:kXqgZtrWaf6qS3jZOCnCH7WYfrvFjkC51bM8fz3RsCA=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY=
|
google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE=
|
||||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE=
|
||||||
google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok=
|
google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc=
|
||||||
google.golang.org/grpc v1.73.0/go.mod h1:50sbHOUqWoCQGI8V2HQLJM0B+LMlIUjNSZmow7EVBQc=
|
google.golang.org/grpc v1.75.0 h1:+TW+dqTd2Biwe6KKfhE5JpiYIBWq865PhKGSXiivqt4=
|
||||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
google.golang.org/grpc v1.75.0/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ=
|
||||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
google.golang.org/protobuf v1.36.8 h1:xHScyCOEuuwZEc6UtSOvPbAT4zRh0xcNRYekJwfqyMc=
|
||||||
|
google.golang.org/protobuf v1.36.8/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU=
|
||||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||||
|
|||||||
14
go.work.sum
14
go.work.sum
@@ -1,6 +1,7 @@
|
|||||||
cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g=
|
cel.dev/expr v0.16.1 h1:NR0+oFYzR1CqLFhTAqg3ql59G9VfN8fKq1TCHJ6gq1g=
|
||||||
cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8=
|
cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8=
|
||||||
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
cel.dev/expr v0.23.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||||
|
cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw=
|
||||||
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
cloud.google.com/go v0.116.0 h1:B3fRrSDkLRt5qSHWe40ERJvhvnQwdZiHu0bJOpldweE=
|
||||||
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
cloud.google.com/go v0.116.0/go.mod h1:cEPSRWPzZEswwdr9BxE6ChEn01dWlTaF05LiC2Xs70U=
|
||||||
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
|
cloud.google.com/go/auth v0.13.0 h1:8Fu8TZy167JkW8Tj3q7dIkr2v4cndv41ouecJx0PAHs=
|
||||||
@@ -9,6 +10,7 @@ cloud.google.com/go/auth/oauth2adapt v0.2.6 h1:V6a6XDu2lTwPZWOawrAa9HUK+DB2zfJyT
|
|||||||
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
cloud.google.com/go/auth/oauth2adapt v0.2.6/go.mod h1:AlmsELtlEBnaNTL7jCj8VQFLy6mbZv0s4Q7NGBeQ5E8=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I=
|
||||||
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg=
|
||||||
|
cloud.google.com/go/compute/metadata v0.7.0/go.mod h1:j5MvL9PprKL39t166CoB1uVHfQMs4tFQZZcKwksXUjo=
|
||||||
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
cloud.google.com/go/iam v1.2.2 h1:ozUSofHUGf/F4tCNy/mu9tHLTaxZFLOUiKzjcgWHGIA=
|
||||||
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
cloud.google.com/go/iam v1.2.2/go.mod h1:0Ys8ccaZHdI1dEUilwzqng/6ps2YB6vRsjIe00/+6JY=
|
||||||
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
|
cloud.google.com/go/monitoring v1.21.2 h1:FChwVtClH19E7pJ+e0xUhJPGksctZNVOk2UhMmblmdU=
|
||||||
@@ -18,6 +20,7 @@ cloud.google.com/go/storage v1.49.0/go.mod h1:k1eHhhpLvrPjVGfo0mOUPEJ4Y2+a/Hv5Pi
|
|||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0 h1:3c8yed4lgqTt+oTQ+JNMDo+F4xprBf+O/il4ZC0nRLw=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.27.0/go.mod h1:yAZHSGnqScoU556rBOVkwLze6WP5N+U11RHuWaGVxwY=
|
||||||
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1 h1:UQ0AhxogsIRZDkElkblfnwjc3IaltCm2HUMvezQaL7s=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.48.1/go.mod h1:jyqM3eLpJ3IbIFDTKVz2rF9T/xWGW0rIriGwnz8l9Tk=
|
||||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU=
|
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.48.1 h1:8nn+rsCvTq9axyEh382S0PFLBeaFwNsT43IrPWzctRU=
|
||||||
@@ -42,6 +45,7 @@ github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod
|
|||||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
|
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78 h1:QVw89YDxXxEe+l8gU8ETbOasdwEV+avkR75ZzsVV9WI=
|
||||||
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||||
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
github.com/cncf/xds/go v0.0.0-20250326154945-ae57f3c0d45f/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||||
|
github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8=
|
||||||
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0=
|
||||||
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
|
||||||
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
|
github.com/envoyproxy/go-control-plane v0.13.1 h1:vPfJZCkob6yTMEgS+0TwfTUfbHjfy/6vOJ8hUWX/uXE=
|
||||||
@@ -55,10 +59,11 @@ github.com/envoyproxy/protoc-gen-validate v1.2.1/go.mod h1:d/C80l/jxXLdfEIhX1W2T
|
|||||||
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk=
|
||||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
||||||
|
github.com/go-jose/go-jose/v4 v4.1.1/go.mod h1:BdsZGqgdO3b6tTc6LSE56wcDbMMLuPsw5d4ZD5f94kA=
|
||||||
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||||
|
github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
|
||||||
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||||
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
|
|
||||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
github.com/google/s2a-go v0.1.8 h1:zZDs9gcbt9ZPLV0ndSyQk6Kacx2g/X+SKYovpnz3SMM=
|
||||||
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
github.com/google/s2a-go v0.1.8/go.mod h1:6iNWHTpQ+nfNRN5E00MSdfDwVesa8hhS32PhPO8deJA=
|
||||||
@@ -109,6 +114,7 @@ go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
|||||||
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ=
|
go.opentelemetry.io/contrib/detectors/gcp v1.29.0 h1:TiaiXB4DpGD3sdzNlYQxruQngn5Apwzi1X0DRhuGvDQ=
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU=
|
go.opentelemetry.io/contrib/detectors/gcp v1.29.0/go.mod h1:GW2aWZNwR2ZxDLdv8OyC2G8zkRoQBuURgV7RPQgcPoU=
|
||||||
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
|
go.opentelemetry.io/contrib/detectors/gcp v1.35.0/go.mod h1:qGWP8/+ILwMRIUf9uIVLloR1uo5ZYAslM4O6OqUi1DA=
|
||||||
|
go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0 h1:r6I7RJCN86bpD/FQwedZ0vSixDpwuWREjW9oRMsmqDc=
|
||||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.54.0/go.mod h1:B9yO6b04uB80CzjedvewuqDhxJxi11s7/GtiGa8bAjI=
|
||||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.54.0/go.mod h1:L7UH0GbB0p47T4Rri3uHjbpCFYrVrwc1I25QhNPiGK8=
|
||||||
@@ -119,9 +125,11 @@ go.opentelemetry.io/otel/sdk/log/logtest v0.13.0/go.mod h1:QOGiAJHl+fob8Nu85ifXf
|
|||||||
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
|
go.opentelemetry.io/otel/sdk/metric v1.29.0/go.mod h1:6zZLdCl2fkauYoZIOn/soQIDSWFmNSRcICarHfuhNJQ=
|
||||||
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
go.opentelemetry.io/otel/trace v1.29.0/go.mod h1:eHl3w0sp3paPkYstJOmAimxhiFXPg+MMTlEh3nsQgWQ=
|
||||||
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||||
|
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||||
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc=
|
||||||
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc=
|
||||||
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY=
|
||||||
|
golang.org/x/crypto v0.41.0/go.mod h1:pO5AFd7FA68rFak7rOAGVuygIISepHftHnr8dr6+sUc=
|
||||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||||
@@ -131,13 +139,13 @@ golang.org/x/telemetry v0.0.0-20250710130107-8d8967aff50b/go.mod h1:4ZwOYna0/zsO
|
|||||||
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
golang.org/x/term v0.27.0 h1:WP60Sv1nlK1T6SupCHbXzSaN0b9wUmsPoRS9b61A23Q=
|
||||||
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
|
||||||
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
golang.org/x/term v0.33.0/go.mod h1:s18+ql9tYWp1IfpV9DmCtQDDSRBUjKaw9M1eAv5UeF0=
|
||||||
golang.org/x/time v0.8.0 h1:9i3RxcPv3PZnitoVGMPDKZSq1xW1gK1Xy3ArNOGZfEg=
|
golang.org/x/term v0.34.0/go.mod h1:5jC53AEywhIVebHgPVeg0mj8OD3VO9OzclacVrqpaAw=
|
||||||
golang.org/x/time v0.8.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
|
||||||
golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
golang.org/x/tools/go/expect v0.1.0-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY=
|
||||||
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8=
|
||||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||||
google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0=
|
google.golang.org/api v0.215.0 h1:jdYF4qnyczlEz2ReWIsosNLDuzXyvFHJtI5gcr0J7t0=
|
||||||
google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
|
google.golang.org/api v0.215.0/go.mod h1:fta3CVtuJYOEdugLNWm6WodzOS8KdFckABwN4I40hzY=
|
||||||
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk=
|
||||||
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
google.golang.org/genproto v0.0.0-20241118233622-e639e219e697/go.mod h1:JJrvXBWRZaFMxBufik1a4RpFw4HhgVtBBWQeQgUj2cc=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576 h1:CkkIfIt50+lT6NHAVoRYEyAvQGFM7xEwXUUywFvEb3Q=
|
||||||
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08=
|
||||||
|
|||||||
@@ -38,16 +38,6 @@ func (self *BunDatabase) GetUserStatuses(
|
|||||||
return ret, err
|
return ret, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BunDatabase) GetRooms(
|
|
||||||
ctx context.Context,
|
|
||||||
) ([]models.Room, error) {
|
|
||||||
var ret []models.Room
|
|
||||||
err := self.db.NewSelect().
|
|
||||||
Model(&ret).
|
|
||||||
Scan(ctx)
|
|
||||||
return ret, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *BunDatabase) InsertUser(
|
func (self *BunDatabase) InsertUser(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
user models.User,
|
user models.User,
|
||||||
@@ -81,16 +71,6 @@ func (self *BunDatabase) InsertUserStatus(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BunDatabase) InsertRoom(
|
|
||||||
ctx context.Context,
|
|
||||||
room models.Room,
|
|
||||||
) error {
|
|
||||||
_, err := self.db.NewInsert().
|
|
||||||
Model(&room).
|
|
||||||
Exec(ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self *BunDatabase) DeleteUserStatus(
|
func (self *BunDatabase) DeleteUserStatus(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
username string,
|
username string,
|
||||||
@@ -101,14 +81,3 @@ func (self *BunDatabase) DeleteUserStatus(
|
|||||||
Exec(ctx)
|
Exec(ctx)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (self *BunDatabase) DeleteRoom(
|
|
||||||
ctx context.Context,
|
|
||||||
roomId int,
|
|
||||||
) error {
|
|
||||||
_, err := self.db.NewDelete().
|
|
||||||
Model(&models.Room{Id: roomId}).
|
|
||||||
WherePK().
|
|
||||||
Exec(ctx)
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -16,10 +16,6 @@ type Database interface {
|
|||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
) ([]models.UserStatus, error)
|
) ([]models.UserStatus, error)
|
||||||
|
|
||||||
GetRooms(
|
|
||||||
ctx context.Context,
|
|
||||||
) ([]models.Room, error)
|
|
||||||
|
|
||||||
InsertUser(
|
InsertUser(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
user models.User,
|
user models.User,
|
||||||
@@ -30,18 +26,8 @@ type Database interface {
|
|||||||
userStatus models.UserStatus,
|
userStatus models.UserStatus,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
InsertRoom(
|
|
||||||
ctx context.Context,
|
|
||||||
room models.Room,
|
|
||||||
) error
|
|
||||||
|
|
||||||
DeleteUserStatus(
|
DeleteUserStatus(
|
||||||
ctx context.Context,
|
ctx context.Context,
|
||||||
username string,
|
username string,
|
||||||
) error
|
) error
|
||||||
|
|
||||||
DeleteRoom(
|
|
||||||
ctx context.Context,
|
|
||||||
roomId int,
|
|
||||||
) error
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,47 +0,0 @@
|
|||||||
package models
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
|
|
||||||
"github.com/uptrace/bun"
|
|
||||||
)
|
|
||||||
|
|
||||||
type RoomType int
|
|
||||||
|
|
||||||
const (
|
|
||||||
RoomTypeWordle RoomType = iota
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
RoomTypeStr = []string{"Wordle"}
|
|
||||||
)
|
|
||||||
|
|
||||||
type RoomStatus int
|
|
||||||
|
|
||||||
const (
|
|
||||||
RoomStatusWaiting RoomStatus = iota
|
|
||||||
RoomStatusPlaying
|
|
||||||
RoomStatusEnded
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
RoomStatusStr = []string{"Waiting", "Playing", "Ended"}
|
|
||||||
)
|
|
||||||
|
|
||||||
type Room struct {
|
|
||||||
bun.BaseModel `bun:"table:room"`
|
|
||||||
|
|
||||||
Id int `bun:"id,pk,autoincrement"`
|
|
||||||
Creater string `bun:"creater"`
|
|
||||||
Type RoomType `bun:"type"`
|
|
||||||
Status RoomStatus `bun:"status"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func (self Room) View() string {
|
|
||||||
return fmt.Sprintf("%2d. %10s %10s [%s]",
|
|
||||||
self.Id,
|
|
||||||
self.Creater,
|
|
||||||
RoomTypeStr[self.Type],
|
|
||||||
RoomStatusStr[self.Status],
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -20,12 +20,14 @@ func Tick(d time.Duration) tea.Cmd {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Base struct {
|
type Base struct {
|
||||||
client *resty.Client
|
client *resty.Client
|
||||||
|
username string
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewBase(client *resty.Client) *Base {
|
func NewBase(client *resty.Client) *Base {
|
||||||
return &Base{
|
return &Base{
|
||||||
client: client,
|
client: client,
|
||||||
|
username: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
128
plays/game.go
Normal file
128
plays/game.go
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/url"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/wordle"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Game struct {
|
||||||
|
*Base
|
||||||
|
server *url.URL
|
||||||
|
roomID string
|
||||||
|
|
||||||
|
mutex sync.RWMutex
|
||||||
|
state types.WordleState
|
||||||
|
|
||||||
|
input string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Game) GetState() {
|
||||||
|
urlStr := m.server.String()
|
||||||
|
tracing.Logger.Info("dialing websocket", zap.String("url", urlStr))
|
||||||
|
c, _, err := websocket.DefaultDialer.Dial(urlStr, m.Base.client.Header)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Errorf("failed to dial, %w", err))
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
_, b, err := c.ReadMessage()
|
||||||
|
switch err.(type) {
|
||||||
|
case nil:
|
||||||
|
var state types.WordleState
|
||||||
|
if err := msgpack.Unmarshal(b, &state); err != nil {
|
||||||
|
panic(fmt.Errorf("failed to unmarshal state, %w", err))
|
||||||
|
}
|
||||||
|
|
||||||
|
m.mutex.Lock()
|
||||||
|
m.state = state
|
||||||
|
m.mutex.Unlock()
|
||||||
|
case *websocket.CloseError:
|
||||||
|
return
|
||||||
|
default:
|
||||||
|
panic(fmt.Errorf("failed to read message, %w", err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewGame(base *Base, server *url.URL, roomID string) *Game {
|
||||||
|
m := &Game{
|
||||||
|
Base: base,
|
||||||
|
server: server,
|
||||||
|
roomID: roomID,
|
||||||
|
state: types.NewWordleState(),
|
||||||
|
input: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
go m.GetState()
|
||||||
|
return m
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Game) Init() tea.Cmd {
|
||||||
|
return tea.Batch(tea.ClearScreen)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Game) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
if _, cmd := m.Base.Update(msg); cmd != nil {
|
||||||
|
return m, cmd
|
||||||
|
}
|
||||||
|
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.Type {
|
||||||
|
case tea.KeyBackspace:
|
||||||
|
if len(m.input) > 0 {
|
||||||
|
m.input = m.input[:len(m.input)-1]
|
||||||
|
}
|
||||||
|
case tea.KeyEnter:
|
||||||
|
if len(m.input) == types.MaxLength {
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
case tea.KeyRunes:
|
||||||
|
if len(m.input) < types.MaxLength {
|
||||||
|
m.input = m.input + string(msg.Runes)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Game) View() string {
|
||||||
|
m.mutex.RLock()
|
||||||
|
ret := m.state.View()
|
||||||
|
m.mutex.RUnlock()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *Game) Next(queue *[]*tea.Program) error {
|
||||||
|
if len(m.input) == types.MaxLength {
|
||||||
|
b, err := msgpack.Marshal(wordle.Operation{
|
||||||
|
Type: wordle.OperationTypeGuess,
|
||||||
|
Username: m.username,
|
||||||
|
Guess: m.input,
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
_, err = m.Base.client.R().
|
||||||
|
SetBody(b).
|
||||||
|
Post(fmt.Sprintf("/api/rooms/%s/guess", m.roomID))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
*queue = append(*queue,
|
||||||
|
tea.NewProgram(NewGame(m.Base, m.server, m.roomID)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
89
plays/joinRoom.go
Normal file
89
plays/joinRoom.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
ea "github.com/charmbracelet/bubbletea"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JoinRoom struct {
|
||||||
|
*Base
|
||||||
|
rooms []types.Room
|
||||||
|
cursor int
|
||||||
|
choice *types.Room
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewJoinRoom(base *Base, rooms []types.Room) *JoinRoom {
|
||||||
|
return &JoinRoom{
|
||||||
|
Base: base,
|
||||||
|
rooms: rooms,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JoinRoom) Init() ea.Cmd {
|
||||||
|
return ea.ClearScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JoinRoom) Update(msg ea.Msg) (ea.Model, ea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case ea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "ctrl+c", "q":
|
||||||
|
return m, ea.Quit
|
||||||
|
case "enter":
|
||||||
|
if m.cursor < len(m.rooms) {
|
||||||
|
m.choice = &m.rooms[m.cursor]
|
||||||
|
}
|
||||||
|
return m, ea.Quit
|
||||||
|
case "up":
|
||||||
|
if m.cursor > 0 {
|
||||||
|
m.cursor--
|
||||||
|
}
|
||||||
|
case "down":
|
||||||
|
if m.cursor < len(m.rooms)-1 {
|
||||||
|
m.cursor++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JoinRoom) View() string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("Select a room to join:\n\n")
|
||||||
|
|
||||||
|
for i, room := range m.rooms {
|
||||||
|
if m.cursor == i {
|
||||||
|
b.WriteString("(•) ")
|
||||||
|
} else {
|
||||||
|
b.WriteString("( ) ")
|
||||||
|
}
|
||||||
|
b.WriteString(room.View() + "\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *JoinRoom) Next(queue *[]*ea.Program) error {
|
||||||
|
if m.choice == nil {
|
||||||
|
*queue = append(*queue, ea.NewProgram(NewLobby(m.Base)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := m.Base.client.R().
|
||||||
|
SetResult(&types.Room{}).
|
||||||
|
Post(fmt.Sprintf("/api/rooms/%s/join", m.choice.ID))
|
||||||
|
|
||||||
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
|
*queue = append(*queue, ea.NewProgram(NewRedirect("Failed to join room")))
|
||||||
|
*queue = append(*queue, ea.NewProgram(NewLobby(m.Base)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
room := resp.Result().(*types.Room)
|
||||||
|
*queue = append(*queue, ea.NewProgram(NewRoomWaiting(m.Base, room.ID)))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
104
plays/lobby.go
104
plays/lobby.go
@@ -4,7 +4,6 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/models"
|
"gitea.konchin.com/ytshih/inp2025/game/models"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
||||||
@@ -14,7 +13,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
lobbyChoices = []string{"No-op", "Logout"}
|
lobbyChoices = []string{"Create Room", "Join Room", "Refresh", "Logout"}
|
||||||
)
|
)
|
||||||
|
|
||||||
type Lobby struct {
|
type Lobby struct {
|
||||||
@@ -23,62 +22,44 @@ type Lobby struct {
|
|||||||
choice string
|
choice string
|
||||||
cursor int
|
cursor int
|
||||||
|
|
||||||
updateCh chan struct{}
|
users []models.UserStatus
|
||||||
users []models.UserStatus
|
rooms []types.Room
|
||||||
rooms []models.Room
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewLobby(base *Base) *Lobby {
|
func NewLobby(base *Base) *Lobby {
|
||||||
m := Lobby{
|
m := &Lobby{
|
||||||
Base: base,
|
Base: base,
|
||||||
choice: "",
|
choice: "",
|
||||||
cursor: 0,
|
cursor: 0,
|
||||||
updateCh: make(chan struct{}, 1),
|
|
||||||
}
|
}
|
||||||
|
return m
|
||||||
return &m
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateLobbyInfo(m *Lobby) error {
|
func (m *Lobby) fetchLobbyInfo() tea.Msg {
|
||||||
for {
|
var users []models.UserStatus
|
||||||
select {
|
resp, err := m.Base.client.R().
|
||||||
case <-m.updateCh:
|
SetResult(&users).
|
||||||
return nil
|
Get("/api/lobby/users")
|
||||||
default:
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
var users []models.UserStatus
|
tracing.Logger.Error("failed to get lobby users", zap.Error(err))
|
||||||
resp, err := m.Base.client.R().
|
return nil
|
||||||
SetResult(&users).
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Get("/api/lobby/users")
|
|
||||||
if resp.StatusCode() != http.StatusOK {
|
|
||||||
tracing.Logger.
|
|
||||||
Error("failed to get lobby users",
|
|
||||||
zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.users = users
|
|
||||||
|
|
||||||
var rooms []models.Room
|
|
||||||
_, err = m.Base.client.R().
|
|
||||||
SetResult(&rooms).
|
|
||||||
ForceContentType("application/json").
|
|
||||||
Get("/api/lobby/rooms")
|
|
||||||
if err != nil {
|
|
||||||
tracing.Logger.
|
|
||||||
Error("failed to get lobby rooms",
|
|
||||||
zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
m.rooms = rooms
|
|
||||||
|
|
||||||
time.Sleep(refetchTick)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
m.users = users
|
||||||
|
|
||||||
|
var rooms []types.Room
|
||||||
|
resp, err = m.Base.client.R().
|
||||||
|
SetResult(&rooms).
|
||||||
|
Get("/api/lobby/rooms")
|
||||||
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
|
tracing.Logger.Error("failed to get lobby rooms", zap.Error(err))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
m.rooms = rooms
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Lobby) Init() tea.Cmd {
|
func (m *Lobby) Init() tea.Cmd {
|
||||||
go updateLobbyInfo(m)
|
return tea.Batch(tea.ClearScreen, m.fetchLobbyInfo)
|
||||||
return tea.Sequence(tea.ClearScreen, Tick(refreshTick))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Lobby) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
func (m *Lobby) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
@@ -107,8 +88,6 @@ func (m *Lobby) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
m.cursor = len(lobbyChoices) - 1
|
m.cursor = len(lobbyChoices) - 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case types.TickMsg:
|
|
||||||
return m, Tick(refreshTick)
|
|
||||||
}
|
}
|
||||||
return m, nil
|
return m, nil
|
||||||
}
|
}
|
||||||
@@ -116,19 +95,20 @@ func (m *Lobby) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||||||
func (m *Lobby) View() string {
|
func (m *Lobby) View() string {
|
||||||
var b strings.Builder
|
var b strings.Builder
|
||||||
|
|
||||||
fmt.Fprintf(&b, "Game lobby\n")
|
fmt.Fprintf(&b, "Game lobby\n\n")
|
||||||
|
|
||||||
fmt.Fprintf(&b, "User Status (%d):\n", len(m.users))
|
fmt.Fprintf(&b, "Online Users (%d):\n", len(m.users))
|
||||||
for _, user := range m.users {
|
for _, user := range m.users {
|
||||||
b.WriteString("- " + user.View() + "\n")
|
b.WriteString("- " + user.View() + "\n")
|
||||||
}
|
}
|
||||||
|
b.WriteString("\n")
|
||||||
|
|
||||||
fmt.Fprintf(&b, "Room Status (%d):\n", len(m.rooms))
|
fmt.Fprintf(&b, "Game Rooms (%d):\n", len(m.rooms))
|
||||||
for _, room := range m.rooms {
|
for _, room := range m.rooms {
|
||||||
b.WriteString("- " + room.View() + "\n")
|
b.WriteString("- " + room.View() + "\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
b.WriteString("==========\n")
|
b.WriteString("\n==========\n")
|
||||||
|
|
||||||
for i := 0; i < len(lobbyChoices); i++ {
|
for i := 0; i < len(lobbyChoices); i++ {
|
||||||
if m.cursor == i {
|
if m.cursor == i {
|
||||||
@@ -142,11 +122,23 @@ func (m *Lobby) View() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (m *Lobby) Next(queue *[]*tea.Program) error {
|
func (m *Lobby) Next(queue *[]*tea.Program) error {
|
||||||
m.updateCh <- struct{}{}
|
|
||||||
switch m.choice {
|
switch m.choice {
|
||||||
case "No-op":
|
case "Refresh":
|
||||||
*queue = append(*queue,
|
*queue = append(*queue,
|
||||||
tea.NewProgram(NewLobby(m.Base)))
|
tea.NewProgram(NewLobby(m.Base)))
|
||||||
|
case "Create Room":
|
||||||
|
resp, err := m.Base.client.R().
|
||||||
|
SetResult(&types.Room{}).
|
||||||
|
Post("/api/rooms")
|
||||||
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
|
return fmt.Errorf("failed to create room")
|
||||||
|
}
|
||||||
|
room := resp.Result().(*types.Room)
|
||||||
|
*queue = append(*queue,
|
||||||
|
tea.NewProgram(NewRoomWaiting(m.Base, room.ID)))
|
||||||
|
case "Join Room":
|
||||||
|
*queue = append(*queue,
|
||||||
|
tea.NewProgram(NewJoinRoom(m.Base, m.rooms)))
|
||||||
case "Logout":
|
case "Logout":
|
||||||
*queue = append(*queue,
|
*queue = append(*queue,
|
||||||
tea.NewProgram(NewLogout(m.Base)))
|
tea.NewProgram(NewLogout(m.Base)))
|
||||||
|
|||||||
@@ -125,6 +125,7 @@ func (m *Login) Next(queue *[]*tea.Program) error {
|
|||||||
if resp.StatusCode() == http.StatusOK {
|
if resp.StatusCode() == http.StatusOK {
|
||||||
m.Base.client.
|
m.Base.client.
|
||||||
SetBasicAuth(username, password)
|
SetBasicAuth(username, password)
|
||||||
|
m.Base.username = username
|
||||||
*queue = append(*queue,
|
*queue = append(*queue,
|
||||||
tea.NewProgram(NewLobby(m.Base)))
|
tea.NewProgram(NewLobby(m.Base)))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -123,17 +123,19 @@ func (m *Register) Next(queue *[]*tea.Program) error {
|
|||||||
}).
|
}).
|
||||||
Post("/auth/register")
|
Post("/auth/register")
|
||||||
|
|
||||||
switch resp.StatusCode() {
|
if err == nil && resp.StatusCode() == http.StatusOK {
|
||||||
case http.StatusOK:
|
|
||||||
*queue = append(*queue,
|
*queue = append(*queue,
|
||||||
tea.NewProgram(NewLogin(m.Base)))
|
tea.NewProgram(NewLogin(m.Base)))
|
||||||
case http.StatusBadRequest:
|
} else {
|
||||||
*queue = append(*queue,
|
switch resp.StatusCode() {
|
||||||
tea.NewProgram(NewRedirect("Username already exist")))
|
case http.StatusBadRequest:
|
||||||
*queue = append(*queue,
|
*queue = append(*queue,
|
||||||
tea.NewProgram(NewRegister(m.Base)))
|
tea.NewProgram(NewRedirect("Username already exist")))
|
||||||
case http.StatusInternalServerError:
|
*queue = append(*queue,
|
||||||
return err
|
tea.NewProgram(NewRegister(m.Base)))
|
||||||
|
default:
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
90
plays/roomWaiting.go
Normal file
90
plays/roomWaiting.go
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
package plays
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
tea "github.com/charmbracelet/bubbletea"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoomWaiting struct {
|
||||||
|
*Base
|
||||||
|
roomID string
|
||||||
|
room *types.Room
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRoomWaiting(base *Base, roomID string) *RoomWaiting {
|
||||||
|
return &RoomWaiting{
|
||||||
|
Base: base,
|
||||||
|
roomID: roomID,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoomWaiting) checkRoomStatus() tea.Msg {
|
||||||
|
resp, err := m.Base.client.R().
|
||||||
|
SetResult(&types.Room{}).
|
||||||
|
Get(fmt.Sprintf("/api/rooms/%s", m.roomID))
|
||||||
|
|
||||||
|
if err != nil || resp.StatusCode() != http.StatusOK {
|
||||||
|
return types.TickMsg{} // Keep polling
|
||||||
|
}
|
||||||
|
|
||||||
|
room := resp.Result().(*types.Room)
|
||||||
|
if room.Status == types.RoomStatusPlaying {
|
||||||
|
m.room = room
|
||||||
|
return tea.Quit() // Stop polling and proceed
|
||||||
|
}
|
||||||
|
|
||||||
|
return types.TickMsg{} // Keep polling
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoomWaiting) Init() tea.Cmd {
|
||||||
|
return tea.Batch(tea.ClearScreen, func() tea.Msg {
|
||||||
|
return types.TickMsg(time.Now())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoomWaiting) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||||
|
switch msg := msg.(type) {
|
||||||
|
case types.TickMsg:
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
return m, m.checkRoomStatus
|
||||||
|
case tea.KeyMsg:
|
||||||
|
switch msg.String() {
|
||||||
|
case "ctrl+c", "q":
|
||||||
|
return m, tea.Quit
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return m, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoomWaiting) View() string {
|
||||||
|
var b strings.Builder
|
||||||
|
b.WriteString("Waiting for another player to join...\n")
|
||||||
|
b.WriteString(fmt.Sprintf("Room ID: %s\n", m.roomID))
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *RoomWaiting) Next(queue *[]*tea.Program) error {
|
||||||
|
if m.room != nil && m.room.Status == types.RoomStatusPlaying {
|
||||||
|
hostURL := m.Base.client.HostURL
|
||||||
|
wsHost := strings.Replace(hostURL, "http", "ws", 1)
|
||||||
|
wsURL := fmt.Sprintf("%s/api/rooms/%s/state", wsHost, m.roomID)
|
||||||
|
tracing.Logger.Info("parsing websocket url", zap.String("url", wsURL))
|
||||||
|
u, err := url.Parse(wsURL)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
*queue = append(*queue, tea.NewProgram(NewGame(m.Base, u, m.roomID)))
|
||||||
|
} else {
|
||||||
|
// If we quit without the room being ready, go back to the lobby.
|
||||||
|
*queue = append(*queue, tea.NewProgram(NewLobby(m.Base)))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@@ -3,7 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -4,7 +4,7 @@ import (
|
|||||||
"database/sql"
|
"database/sql"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
)
|
)
|
||||||
|
|
||||||
15
server/backend/api/handlers.go
Normal file
15
server/backend/api/handlers.go
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/interfaces"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/rooms"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Handlers struct {
|
||||||
|
db interfaces.Database
|
||||||
|
roomManager *rooms.RoomManager
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlers(db interfaces.Database, roomManager *rooms.RoomManager) *Handlers {
|
||||||
|
return &Handlers{db: db, roomManager: roomManager}
|
||||||
|
}
|
||||||
115
server/backend/api/rooms.go
Normal file
115
server/backend/api/rooms.go
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/models"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
"github.com/uptrace/bunrouter"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Handlers) CreateRoom(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
ctx := req.Context()
|
||||||
|
user, ok := ctx.Value(types.User("")).(models.User)
|
||||||
|
if !ok {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusUnauthorized,
|
||||||
|
Message: "user not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
room, err := self.roomManager.CreateRoom(user.Username)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "failed to create room",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bunrouter.JSON(w, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) GetLobbyRooms(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
rooms := self.roomManager.GetRooms()
|
||||||
|
return bunrouter.JSON(w, rooms)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) JoinRoom(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
ctx := req.Context()
|
||||||
|
user, ok := ctx.Value(types.User("")).(models.User)
|
||||||
|
if !ok {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusUnauthorized,
|
||||||
|
Message: "user not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
roomID := req.Param("id")
|
||||||
|
room, err := self.roomManager.JoinRoom(roomID, user.Username)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: err.Error(),
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bunrouter.JSON(w, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) GetRoom(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
roomID := req.Param("id")
|
||||||
|
room, ok := self.roomManager.GetRoom(roomID)
|
||||||
|
if !ok {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
Message: "room not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return bunrouter.JSON(w, room)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) WSGetRoomState(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
roomID := req.Param("id")
|
||||||
|
handler, ok := self.roomManager.GetGameHandler(roomID)
|
||||||
|
if !ok {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
Message: "game handler not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler.WSGetState(w, req)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) PostGuess(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
roomID := req.Param("id")
|
||||||
|
handler, ok := self.roomManager.GetGameHandler(roomID)
|
||||||
|
if !ok {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusNotFound,
|
||||||
|
Message: "game handler not found",
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return handler.WSPostGuess(w, req)
|
||||||
|
}
|
||||||
@@ -3,8 +3,8 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/models"
|
"gitea.konchin.com/ytshih/inp2025/game/models"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/types"
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
@@ -3,8 +3,8 @@ package auth
|
|||||||
import (
|
import (
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/models"
|
"gitea.konchin.com/ytshih/inp2025/game/models"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/types"
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
@@ -5,8 +5,8 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/backend/middlewares"
|
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/models"
|
"gitea.konchin.com/ytshih/inp2025/game/models"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/types"
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
"github.com/uptrace/bunrouter"
|
"github.com/uptrace/bunrouter"
|
||||||
@@ -18,11 +18,6 @@ const docTemplate = `{
|
|||||||
"host": "{{.Host}}",
|
"host": "{{.Host}}",
|
||||||
"basePath": "{{.BasePath}}",
|
"basePath": "{{.BasePath}}",
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/lobby/rooms": {
|
|
||||||
"get": {
|
|
||||||
"responses": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/lobby/users": {
|
"/api/lobby/users": {
|
||||||
"get": {
|
"get": {
|
||||||
"responses": {}
|
"responses": {}
|
||||||
@@ -10,11 +10,6 @@
|
|||||||
},
|
},
|
||||||
"basePath": "/",
|
"basePath": "/",
|
||||||
"paths": {
|
"paths": {
|
||||||
"/api/lobby/rooms": {
|
|
||||||
"get": {
|
|
||||||
"responses": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"/api/lobby/users": {
|
"/api/lobby/users": {
|
||||||
"get": {
|
"get": {
|
||||||
"responses": {}
|
"responses": {}
|
||||||
@@ -14,9 +14,6 @@ info:
|
|||||||
title: Intro. to Network Programming Game
|
title: Intro. to Network Programming Game
|
||||||
version: 0.0.1-alpha
|
version: 0.0.1-alpha
|
||||||
paths:
|
paths:
|
||||||
/api/lobby/rooms:
|
|
||||||
get:
|
|
||||||
responses: {}
|
|
||||||
/api/lobby/users:
|
/api/lobby/users:
|
||||||
get:
|
get:
|
||||||
responses: {}
|
responses: {}
|
||||||
@@ -11,7 +11,7 @@ func (self *Handlers) AccessLog(
|
|||||||
next bunrouter.HandlerFunc,
|
next bunrouter.HandlerFunc,
|
||||||
) bunrouter.HandlerFunc {
|
) bunrouter.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
return func(w http.ResponseWriter, req bunrouter.Request) error {
|
||||||
tracing.Logger.Ctx(req.Context()).
|
tracing.Logger.
|
||||||
Info(req.Method + " " + req.Params().Route())
|
Info(req.Method + " " + req.Params().Route())
|
||||||
return next(w, req)
|
return next(w, req)
|
||||||
}
|
}
|
||||||
@@ -35,7 +35,7 @@ func (self *Handlers) Auth(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if dbUser.Password != password {
|
if dbUser.Password != password {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Debug("password input",
|
Debug("password input",
|
||||||
zap.String("input.password", password),
|
zap.String("input.password", password),
|
||||||
zap.String("dbuser.password", dbUser.Password))
|
zap.String("dbuser.password", dbUser.Password))
|
||||||
@@ -45,7 +45,7 @@ func (self *Handlers) ErrorHandler(
|
|||||||
httpErr = err
|
httpErr = err
|
||||||
|
|
||||||
default:
|
default:
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Error("unhandled error",
|
Error("unhandled error",
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
httpErr = NewHTTPError(err)
|
httpErr = NewHTTPError(err)
|
||||||
@@ -60,20 +60,20 @@ func (self *Handlers) ErrorHandler(
|
|||||||
if 500 <= httpErr.StatusCode && httpErr.StatusCode <= 599 {
|
if 500 <= httpErr.StatusCode && httpErr.StatusCode <= 599 {
|
||||||
span.SetStatus(codes.Error, err.Error())
|
span.SetStatus(codes.Error, err.Error())
|
||||||
if httpErr.OriginError == nil {
|
if httpErr.OriginError == nil {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Error(httpErr.Message)
|
Error(httpErr.Message)
|
||||||
} else {
|
} else {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Error(httpErr.Message,
|
Error(httpErr.Message,
|
||||||
zap.Error(httpErr.OriginError))
|
zap.Error(httpErr.OriginError))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
span.SetStatus(codes.Ok, "")
|
span.SetStatus(codes.Ok, "")
|
||||||
if httpErr.OriginError == nil {
|
if httpErr.OriginError == nil {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Warn(httpErr.Message)
|
Warn(httpErr.Message)
|
||||||
} else {
|
} else {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Warn(httpErr.Message,
|
Warn(httpErr.Message,
|
||||||
zap.Error(httpErr.OriginError))
|
zap.Error(httpErr.OriginError))
|
||||||
}
|
}
|
||||||
93
server/rooms/manager.go
Normal file
93
server/rooms/manager.go
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
package rooms
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/wordle"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
"github.com/google/uuid"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RoomManager struct {
|
||||||
|
mu sync.RWMutex
|
||||||
|
rooms map[string]*types.Room
|
||||||
|
gameHandlers map[string]*wordle.Handlers
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewRoomManager() *RoomManager {
|
||||||
|
return &RoomManager{
|
||||||
|
rooms: make(map[string]*types.Room),
|
||||||
|
gameHandlers: make(map[string]*wordle.Handlers),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RoomManager) CreateRoom(creater string) (*types.Room, error) {
|
||||||
|
room := &types.Room{
|
||||||
|
ID: uuid.New().String(),
|
||||||
|
Creater: creater,
|
||||||
|
Status: types.RoomStatusWaiting,
|
||||||
|
Players: []string{creater},
|
||||||
|
}
|
||||||
|
|
||||||
|
gameHandler := wordle.NewHandlers()
|
||||||
|
|
||||||
|
rm.mu.Lock()
|
||||||
|
rm.rooms[room.ID] = room
|
||||||
|
rm.gameHandlers[room.ID] = gameHandler
|
||||||
|
rm.mu.Unlock()
|
||||||
|
|
||||||
|
return room, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RoomManager) GetRooms() []*types.Room {
|
||||||
|
rm.mu.RLock()
|
||||||
|
defer rm.mu.RUnlock()
|
||||||
|
|
||||||
|
rooms := make([]*types.Room, 0, len(rm.rooms))
|
||||||
|
for _, room := range rm.rooms {
|
||||||
|
rooms = append(rooms, room)
|
||||||
|
}
|
||||||
|
return rooms
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RoomManager) GetRoom(id string) (*types.Room, bool) {
|
||||||
|
rm.mu.RLock()
|
||||||
|
defer rm.mu.RUnlock()
|
||||||
|
room, ok := rm.rooms[id]
|
||||||
|
return room, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RoomManager) GetGameHandler(id string) (*wordle.Handlers, bool) {
|
||||||
|
rm.mu.RLock()
|
||||||
|
defer rm.mu.RUnlock()
|
||||||
|
handler, ok := rm.gameHandlers[id]
|
||||||
|
return handler, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rm *RoomManager) JoinRoom(id, username string) (*types.Room, error) {
|
||||||
|
rm.mu.Lock()
|
||||||
|
defer rm.mu.Unlock()
|
||||||
|
|
||||||
|
room, ok := rm.rooms[id]
|
||||||
|
if !ok {
|
||||||
|
return nil, fmt.Errorf("room not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(room.Players) >= 2 {
|
||||||
|
return nil, fmt.Errorf("room is full")
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, player := range room.Players {
|
||||||
|
if player == username {
|
||||||
|
return room, nil // already in room
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
room.Players = append(room.Players, username)
|
||||||
|
if len(room.Players) == 2 {
|
||||||
|
room.Status = types.RoomStatusPlaying
|
||||||
|
}
|
||||||
|
|
||||||
|
return room, nil
|
||||||
|
}
|
||||||
65
server/wordle/handlers.go
Normal file
65
server/wordle/handlers.go
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
package wordle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OperationType int
|
||||||
|
|
||||||
|
const (
|
||||||
|
OperationTypeGuess OperationType = iota
|
||||||
|
)
|
||||||
|
|
||||||
|
type Operation struct {
|
||||||
|
Type OperationType
|
||||||
|
Username string
|
||||||
|
Guess string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Handlers struct {
|
||||||
|
state types.WordleState
|
||||||
|
ans string
|
||||||
|
upgrader websocket.Upgrader
|
||||||
|
|
||||||
|
opCh chan Operation
|
||||||
|
subs []*chan types.WordleState
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewHandlers() *Handlers {
|
||||||
|
ret := &Handlers{
|
||||||
|
upgrader: websocket.Upgrader{
|
||||||
|
ReadBufferSize: 1024,
|
||||||
|
WriteBufferSize: 1024,
|
||||||
|
CheckOrigin: func(r *http.Request) bool {
|
||||||
|
return true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
subs: nil,
|
||||||
|
}
|
||||||
|
go ret.GameFlow()
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self *Handlers) GameFlow() {
|
||||||
|
for op := range self.opCh {
|
||||||
|
switch op.Type {
|
||||||
|
case OperationTypeGuess:
|
||||||
|
self.state.CurrentGuess++
|
||||||
|
if self.state.CurrentGuess >= len(self.state.Users) {
|
||||||
|
for username, user := range self.state.Users {
|
||||||
|
user.History = append(user.History,
|
||||||
|
types.NewWord(op.Guess, self.ans))
|
||||||
|
self.state.Users[username] = user
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, sub := range self.subs {
|
||||||
|
*sub <- self.state
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
44
server/wordle/wsGetState.go
Normal file
44
server/wordle/wsGetState.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package wordle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/types"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
|
"github.com/gorilla/websocket"
|
||||||
|
"github.com/uptrace/bunrouter"
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Handlers) WSGetState(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
c, err := self.upgrader.Upgrade(w, req.Request, nil)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "failed to upgrade websocket",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
dataCh := make(chan types.WordleState)
|
||||||
|
self.subs = append(self.subs, &dataCh)
|
||||||
|
|
||||||
|
for data := range dataCh {
|
||||||
|
b, err := msgpack.Marshal(data)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusInternalServerError,
|
||||||
|
Message: "failed to marshal data into msgpack",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
c.WriteMessage(websocket.BinaryMessage, b)
|
||||||
|
}
|
||||||
|
|
||||||
|
return utils.Success(w)
|
||||||
|
}
|
||||||
38
server/wordle/wsPostGuess.go
Normal file
38
server/wordle/wsPostGuess.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
package wordle
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/utils"
|
||||||
|
"github.com/uptrace/bunrouter"
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (self *Handlers) WSPostGuess(
|
||||||
|
w http.ResponseWriter,
|
||||||
|
req bunrouter.Request,
|
||||||
|
) error {
|
||||||
|
b, err := io.ReadAll(req.Body)
|
||||||
|
if err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to read body",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var op Operation
|
||||||
|
if err := msgpack.Unmarshal(b, &op); err != nil {
|
||||||
|
return middlewares.HTTPError{
|
||||||
|
StatusCode: http.StatusBadRequest,
|
||||||
|
Message: "failed to unmarshal msgpack",
|
||||||
|
OriginError: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.opCh <- op
|
||||||
|
|
||||||
|
return utils.Success(w)
|
||||||
|
}
|
||||||
@@ -1,33 +1,42 @@
|
|||||||
package tracing
|
package tracing
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
"github.com/uptrace/opentelemetry-go-extra/otelzap"
|
"go.opentelemetry.io/contrib/bridges/otelzap"
|
||||||
"go.opentelemetry.io/otel"
|
"go.opentelemetry.io/otel/exporters/otlp/otlplog/otlploggrpc"
|
||||||
|
"go.opentelemetry.io/otel/sdk/log"
|
||||||
"go.opentelemetry.io/otel/trace"
|
"go.opentelemetry.io/otel/trace"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
Tracer trace.Tracer
|
Provider *log.LoggerProvider
|
||||||
Logger *otelzap.Logger
|
Tracer trace.Tracer
|
||||||
|
Logger *zap.Logger
|
||||||
|
|
||||||
version string = "0.0.1-alpha"
|
version string = "0.0.1-alpha"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitTracer(appname string) {
|
func InitTracer(appname string) {
|
||||||
Tracer = otel.Tracer(appname)
|
ctx := context.Background()
|
||||||
|
exp, err := otlploggrpc.New(ctx,
|
||||||
var l *zap.Logger
|
otlploggrpc.WithInsecure(),
|
||||||
var err error
|
otlploggrpc.WithEndpoint(viper.GetString("otel-endpoint")))
|
||||||
if viper.GetBool("zap-production") {
|
|
||||||
l, err = zap.NewProduction()
|
|
||||||
} else {
|
|
||||||
l, err = zap.NewDevelopment()
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
Logger = otelzap.New(l)
|
Provider = log.NewLoggerProvider(
|
||||||
|
log.WithProcessor(log.NewBatchProcessor(exp)))
|
||||||
|
otelCore := otelzap.NewCore(appname,
|
||||||
|
otelzap.WithLoggerProvider(Provider))
|
||||||
|
|
||||||
|
Logger = zap.New(otelCore, zap.AddCaller())
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeferTracer() {
|
||||||
|
Logger.Sync()
|
||||||
|
Provider.Shutdown(context.Background())
|
||||||
}
|
}
|
||||||
|
|||||||
31
types/game.go
Normal file
31
types/game.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type RoomStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
RoomStatusWaiting RoomStatus = iota
|
||||||
|
RoomStatusPlaying
|
||||||
|
RoomStatusEnded
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
RoomStatusStr = []string{"Waiting", "Playing", "Ended"}
|
||||||
|
)
|
||||||
|
|
||||||
|
type Room struct {
|
||||||
|
ID string `json:"id"`
|
||||||
|
Creater string `json:"creater"`
|
||||||
|
Status RoomStatus `json:"status"`
|
||||||
|
Players []string `json:"players"`
|
||||||
|
Addr string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Room) View() string {
|
||||||
|
return fmt.Sprintf("%s's room [%s] (%d/2)",
|
||||||
|
self.Creater,
|
||||||
|
RoomStatusStr[self.Status],
|
||||||
|
len(self.Players),
|
||||||
|
)
|
||||||
|
}
|
||||||
89
types/wordle.go
Normal file
89
types/wordle.go
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/charmbracelet/lipgloss"
|
||||||
|
)
|
||||||
|
|
||||||
|
type CharStatus int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CharStatusA CharStatus = iota
|
||||||
|
CharStatusB
|
||||||
|
CharStatusC
|
||||||
|
)
|
||||||
|
|
||||||
|
type Char struct {
|
||||||
|
Char rune `msgpack:"char"`
|
||||||
|
Status CharStatus `msgpack:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewChar() Char {
|
||||||
|
return Char{
|
||||||
|
Char: 0x0,
|
||||||
|
Status: CharStatusC,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Char) View() string {
|
||||||
|
style := lipgloss.NewStyle()
|
||||||
|
|
||||||
|
switch self.Status {
|
||||||
|
case CharStatusA:
|
||||||
|
style = style.Background(lipgloss.Color("#00ff00"))
|
||||||
|
case CharStatusB:
|
||||||
|
style = style.Background(lipgloss.Color("#ffff00"))
|
||||||
|
case CharStatusC:
|
||||||
|
style = style.Background(lipgloss.Color("#808080"))
|
||||||
|
}
|
||||||
|
|
||||||
|
return style.Render(string(self.Char))
|
||||||
|
}
|
||||||
|
|
||||||
|
type Word struct {
|
||||||
|
Chars []Char `msgpack:"chars"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWord(word, ans string) Word {
|
||||||
|
return Word{
|
||||||
|
Chars: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self Word) View() string {
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
for _, char := range self.Chars {
|
||||||
|
b.WriteString(char.View())
|
||||||
|
b.WriteRune('\n')
|
||||||
|
}
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
MaxLength = 5
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserState struct {
|
||||||
|
History []Word `msgpack:"history"`
|
||||||
|
Input string
|
||||||
|
}
|
||||||
|
|
||||||
|
type WordleState struct {
|
||||||
|
Users map[string]UserState `msgpack:"users"`
|
||||||
|
CurrentGuess int
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewWordleState() WordleState {
|
||||||
|
return WordleState{
|
||||||
|
Users: nil,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (self WordleState) View() string {
|
||||||
|
var b strings.Builder
|
||||||
|
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
@@ -11,6 +11,5 @@ func InitDB(ctx context.Context, db *bun.DB) error {
|
|||||||
return db.ResetModel(ctx,
|
return db.ResetModel(ctx,
|
||||||
(*models.User)(nil),
|
(*models.User)(nil),
|
||||||
(*models.UserStatus)(nil),
|
(*models.UserStatus)(nil),
|
||||||
(*models.Room)(nil),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ func ReadConfig(ctx context.Context) {
|
|||||||
viper.AddConfigPath(".")
|
viper.AddConfigPath(".")
|
||||||
if err := viper.ReadInConfig(); err != nil {
|
if err := viper.ReadInConfig(); err != nil {
|
||||||
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
if _, ok := err.(viper.ConfigFileNotFoundError); !ok {
|
||||||
tracing.Logger.Ctx(ctx).
|
tracing.Logger.
|
||||||
Panic("failed to read config file",
|
Panic("failed to read config file",
|
||||||
zap.Error(err))
|
zap.Error(err))
|
||||||
panic(err)
|
panic(err)
|
||||||
|
|||||||
77
utils/udp.go
Normal file
77
utils/udp.go
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/vmihailenco/msgpack/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UDPPayload struct {
|
||||||
|
MagicNumber int `msgpack:"magicNumber"`
|
||||||
|
Data string `msgpack:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
BUFFER_SIZE int = 1024
|
||||||
|
MAGIC_NUMBER int = 114514
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListenUDP(
|
||||||
|
port string,
|
||||||
|
doneCh chan struct{},
|
||||||
|
) (string, error) {
|
||||||
|
addr, err := net.ResolveUDPAddr("udp", ":"+port)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to resolve address, %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := net.ListenUDP("udp", addr)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to listen on addr '%v', %w", addr, err)
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
dataCh := make(chan string)
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
buffer := make([]byte, BUFFER_SIZE)
|
||||||
|
|
||||||
|
n, _, err := conn.ReadFromUDP(buffer)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
var payload UDPPayload
|
||||||
|
err = msgpack.Unmarshal(buffer[:n], &payload)
|
||||||
|
if err == nil && payload.MagicNumber == MAGIC_NUMBER {
|
||||||
|
dataCh <- payload.Data
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
select {
|
||||||
|
case <-doneCh:
|
||||||
|
return "", nil
|
||||||
|
case data := <-dataCh:
|
||||||
|
return data, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return "", fmt.Errorf("failed to receive data")
|
||||||
|
}
|
||||||
|
|
||||||
|
func SendPayload(endpoint, data string) error {
|
||||||
|
conn, err := net.Dial("udp", endpoint)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to dial endpoint")
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
_, err = conn.Write([]byte(data))
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to send payload")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
47
workflows/wordleServer.go
Normal file
47
workflows/wordleServer.go
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
package workflows
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/implements"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/middlewares"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/server/wordle"
|
||||||
|
"gitea.konchin.com/ytshih/inp2025/game/tracing"
|
||||||
|
"github.com/uptrace/bunrouter"
|
||||||
|
"go.uber.org/zap"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ShutdownFunc = func()
|
||||||
|
|
||||||
|
func WordleServer(addr string) ShutdownFunc {
|
||||||
|
handlers := wordle.NewHandlers()
|
||||||
|
middlewareHandlers := middlewares.NewHandlers(
|
||||||
|
implements.NewBunDatabase(nil))
|
||||||
|
|
||||||
|
router := bunrouter.New()
|
||||||
|
|
||||||
|
apiGroup := router.NewGroup("/api").
|
||||||
|
Use(middlewareHandlers.ErrorHandler)
|
||||||
|
apiGroup.GET("/state",
|
||||||
|
handlers.WSGetState)
|
||||||
|
apiGroup.POST("/guess",
|
||||||
|
handlers.WSPostGuess)
|
||||||
|
|
||||||
|
server := &http.Server{
|
||||||
|
Addr: addr,
|
||||||
|
Handler: http.Handler(router),
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
if err := server.ListenAndServe(); err != http.ErrServerClosed {
|
||||||
|
tracing.Logger.Error("wordle server failed", zap.Error(err))
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
return func() {
|
||||||
|
if err := server.Shutdown(context.Background()); err != nil {
|
||||||
|
tracing.Logger.Error("failed to shutdown wordle server", zap.Error(err))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user