From 8355ca374b6125253097b8526c695fb43606fcfd Mon Sep 17 00:00:00 2001 From: zomo Date: Fri, 31 Oct 2025 10:32:50 -0500 Subject: [PATCH] rough skeleton and basic user authentication --- .gitignore | 2 + api/endpoints.go | 82 ++++++++++++++++++++++++++++ api/main.go | 33 ++++++++++++ api/ws/main.go | 17 ++++++ db/db_cold/main.go | 10 ++++ db/db_hot/main.go | 10 ++++ db/main.go | 24 +++++++++ go.mod | 53 ++++++++++++++++++ go.sum | 107 +++++++++++++++++++++++++++++++++++++ main.go | 42 +++++++++++++++ ttv/auth.go | 78 +++++++++++++++++++++++++++ ttv/events.go | 6 +++ ttv/main.go | 19 +++++++ util/conf.go | 54 +++++++++++++++++++ util/main.go | 1 + web/oauth-login/auth.html | 1 + web/oauth-login/login.html | 3 ++ 17 files changed, 542 insertions(+) create mode 100644 .gitignore create mode 100644 api/endpoints.go create mode 100644 api/main.go create mode 100644 api/ws/main.go create mode 100644 db/db_cold/main.go create mode 100644 db/db_hot/main.go create mode 100644 db/main.go create mode 100644 go.mod create mode 100644 go.sum create mode 100644 main.go create mode 100644 ttv/auth.go create mode 100644 ttv/events.go create mode 100644 ttv/main.go create mode 100644 util/conf.go create mode 100644 util/main.go create mode 100644 web/oauth-login/auth.html create mode 100644 web/oauth-login/login.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9b70a9f --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +.env +todo diff --git a/api/endpoints.go b/api/endpoints.go new file mode 100644 index 0000000..bd36f22 --- /dev/null +++ b/api/endpoints.go @@ -0,0 +1,82 @@ +package api + +import ( + "net/http" + + "github.com/gin-gonic/gin" +) + +func (server *ApiServer) loadEndpoints() { + server.engine.GET("/ping", func(c *gin.Context) { + c.JSON(http.StatusOK, gin.H{ + "message": "pong", + }) + }) + + // info for user-end html pages + serverInfo := ServerInfo{ + ClientID: server.conf.ClientID, + AuthParams: TwitchAuthParams{ + ClientID: server.conf.ClientID, + ForceVerify: false, + RedirectURI: server.conf.RedirectURI, + ResponseType: "code", + Scope: []string{ + "bits:read", + "channel:bot", + "channel:read:goals", + "channel:read:hype_train", + "channel:read:polls", + "channel:manage:polls", + "channel:read:predictions", + "channel:manage:predictions", + "channel:read:redemptions", + "channel:manage:redemptions", + "channel:read:vips", + "channel:moderate", + "user:bot", + "user:read:broadcast", + "user:read:chat", + "user:read:emotes", + "user:write:chat", + }, + State: "", // TODO make this unique per request + }, + } + + server.engine.GET("/info", func(c *gin.Context) { + c.JSON(http.StatusOK, serverInfo) + }) + + server.engine.GET("/auth", func(c *gin.Context) { + // TODO auth response from twitch + // parse args as TwitchAuthRespOk or TwitchAuthRespErr + c.JSON(http.StatusOK, serverInfo) + }) +} + +type ServerInfo struct { + ClientID string `json:"client_id"` + AuthParams TwitchAuthParams `json:"auth_params"` +} + +type TwitchAuthParams struct { + ClientID string `json:"client_id"` + ForceVerify bool `json:"force_verify"` + RedirectURI string `json:"redirect_uri"` + ResponseType string `json:"response_type"` + Scope []string `json:"scope"` + State string `json:"state"` +} + +type TwitchAuthRespOk struct { + Code string `json:"code"` + Scope string `json:"scope"` + State string `json:"state"` +} + +type TwitchAuthRespErr struct { + Err string `json:"error"` + ErrDesc string `json:"error_description"` + State string `json:"state"` +} diff --git a/api/main.go b/api/main.go new file mode 100644 index 0000000..60659d3 --- /dev/null +++ b/api/main.go @@ -0,0 +1,33 @@ +package api + +import ( + "github.com/gin-gonic/gin" + "zomo.dev/largehadroncollider/db" + "zomo.dev/largehadroncollider/ttv" + "zomo.dev/largehadroncollider/util" +) + +func InitApiServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*ApiServer, error) { + engine := gin.Default() + + apiServer := &ApiServer{ engine, conf, dbConn, twitchConn } + + apiServer.loadEndpoints() + + return apiServer, nil +} + +type ApiServer struct { + engine *gin.Engine + conf *util.Config + db *db.DBConn + twitch *ttv.TwitchConn +} + +func (server *ApiServer) Listen() { + server.engine.Run() +} + +func initHTMLFiles() { + +} diff --git a/api/ws/main.go b/api/ws/main.go new file mode 100644 index 0000000..b0e58af --- /dev/null +++ b/api/ws/main.go @@ -0,0 +1,17 @@ +package ws + +import ( + "zomo.dev/largehadroncollider/db" + "zomo.dev/largehadroncollider/ttv" +) + +func InitWSServer(dbConn db.DBConn, twitchConn ttv.TwitchConn) (WSServer, error) { + return WSServer{}, nil +} + +type WSServer struct { +} + +func (wsServer *WSServer) Listen() { + // start web server itself +} diff --git a/db/db_cold/main.go b/db/db_cold/main.go new file mode 100644 index 0000000..1410a5c --- /dev/null +++ b/db/db_cold/main.go @@ -0,0 +1,10 @@ +package db_cold + +// sqlite file + +func InitDBColdConn() (DBColdConn, error) { + return DBColdConn{}, nil +} + +type DBColdConn struct { +} diff --git a/db/db_hot/main.go b/db/db_hot/main.go new file mode 100644 index 0000000..dff5ab8 --- /dev/null +++ b/db/db_hot/main.go @@ -0,0 +1,10 @@ +package db_hot + +// redis connection + +func InitDBHotConn() (DBHotConn, error) { + return DBHotConn{}, nil +} + +type DBHotConn struct { +} diff --git a/db/main.go b/db/main.go new file mode 100644 index 0000000..c257bc9 --- /dev/null +++ b/db/main.go @@ -0,0 +1,24 @@ +package db + +import ( + "zomo.dev/largehadroncollider/db/db_cold" + "zomo.dev/largehadroncollider/db/db_hot" + "zomo.dev/largehadroncollider/util" +) + +func InitDBConn(conf *util.Config) (*DBConn, error) { + hot, err := db_hot.InitDBHotConn() + if err != nil { + return nil, err + } + cold, err := db_cold.InitDBColdConn() + if err != nil { + return nil, err + } + return &DBConn{ hot, cold }, nil +} + +type DBConn struct { + hot db_hot.DBHotConn + cold db_cold.DBColdConn +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..0d8ca22 --- /dev/null +++ b/go.mod @@ -0,0 +1,53 @@ +module zomo.dev/largehadroncollider + +go 1.25.0 + +require github.com/gin-gonic/gin v1.11.0 + +require ( + github.com/adeithe/go-twitch v0.4.1 // indirect + github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef // indirect + github.com/bytedance/sonic v1.14.0 // indirect + github.com/bytedance/sonic/loader v0.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/cloudwego/base64x v0.1.6 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/gabriel-vasile/mimetype v1.4.8 // indirect + github.com/gin-contrib/sse v1.1.0 // indirect + github.com/go-playground/locales v0.14.1 // indirect + github.com/go-playground/universal-translator v0.18.1 // indirect + github.com/go-playground/validator/v10 v10.27.0 // indirect + github.com/gobwas/httphead v0.1.0 // indirect + github.com/gobwas/pool v0.2.1 // indirect + github.com/gobwas/ws v1.4.0 // indirect + github.com/goccy/go-json v0.10.2 // indirect + github.com/goccy/go-yaml v1.18.0 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/joho/godotenv v1.5.1 // indirect + github.com/json-iterator/go v1.1.12 // indirect + github.com/klauspost/cpuid/v2 v2.3.0 // indirect + github.com/leodido/go-urn v1.4.0 // indirect + github.com/mattn/go-isatty v0.0.20 // indirect + github.com/mattn/go-sqlite3 v1.14.32 // indirect + github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect + github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/pelletier/go-toml/v2 v2.2.4 // indirect + github.com/quic-go/qpack v0.5.1 // indirect + github.com/quic-go/quic-go v0.54.0 // indirect + github.com/redis/go-redis/v9 v9.13.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + github.com/ugorji/go/codec v1.3.0 // indirect + go.uber.org/mock v0.5.0 // indirect + golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect + golang.org/x/mod v0.26.0 // indirect + golang.org/x/net v0.42.0 // indirect + golang.org/x/sync v0.16.0 // indirect + golang.org/x/sys v0.35.0 // indirect + golang.org/x/text v0.28.0 // indirect + golang.org/x/tools v0.35.0 // indirect + google.golang.org/protobuf v1.36.9 // indirect + gorm.io/driver/sqlite v1.6.0 // indirect + gorm.io/gorm v1.30.2 // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..08444d1 --- /dev/null +++ b/go.sum @@ -0,0 +1,107 @@ +github.com/adeithe/go-twitch v0.4.1 h1:y45IXnzPFfDZfStl42LAZw/tOoIfUShVfR91XvXBt2U= +github.com/adeithe/go-twitch v0.4.1/go.mod h1:UPJJgFZo3FjecmLENm4GaNwF33fVbTwAwjElPgpmz4s= +github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef h1:2JGTg6JapxP9/R33ZaagQtAM4EkkSYnIAlOG5EI8gkM= +github.com/asaskevich/EventBus v0.0.0-20200907212545-49d423059eef/go.mod h1:JS7hed4L1fj0hXcyEejnW57/7LCetXggd+vwrRnYeII= +github.com/bytedance/sonic v1.14.0 h1:/OfKt8HFw0kh2rj8N0F6C/qPGRESq0BbaNZgcNXXzQQ= +github.com/bytedance/sonic v1.14.0/go.mod h1:WoEbx8WTcFJfzCe0hbmyTGrfjt8PzNEBdxlNUO24NhA= +github.com/bytedance/sonic/loader v0.3.0 h1:dskwH8edlzNMctoruo8FPTJDF3vLtDT0sXZwvZJyqeA= +github.com/bytedance/sonic/loader v0.3.0/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M= +github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/gabriel-vasile/mimetype v1.4.8 h1:FfZ3gj38NjllZIeJAmMhr+qKL8Wu+nOoI3GqacKw1NM= +github.com/gabriel-vasile/mimetype v1.4.8/go.mod h1:ByKUIKGjh1ODkGM1asKUbQZOLGrPjydw3hYPU2YU9t8= +github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w= +github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM= +github.com/gin-gonic/gin v1.11.0 h1:OW/6PLjyusp2PPXtyxKHU0RbX6I/l28FTdDlae5ueWk= +github.com/gin-gonic/gin v1.11.0/go.mod h1:+iq/FyxlGzII0KHiBGjuNn4UNENUlKbGlNmc+W50Dls= +github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA= +github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY= +github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY= +github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY= +github.com/go-playground/validator/v10 v10.27.0 h1:w8+XrWVMhGkxOaaowyKH35gFydVHOvC0/uWoy2Fzwn4= +github.com/go-playground/validator/v10 v10.27.0/go.mod h1:I5QpIEbmr8On7W0TktmJAumgzX4CA1XNl4ZmDuVHKKo= +github.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU= +github.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM= +github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= +github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= +github.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs= +github.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc= +github.com/goccy/go-json v0.10.2 h1:CrxCmQqYDkv1z7lO7Wbh2HN93uovUHgrECaO5ZrCXAU= +github.com/goccy/go-json v0.10.2/go.mod h1:6MelG93GURQebXPDq3khkgXZkazVtN9CRI+MGFi0w8I= +github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= +github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= +github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= +github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ= +github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= +github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= +github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= +github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= +github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y= +github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0= +github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ= +github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI= +github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= +github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.32 h1:JD12Ag3oLy1zQA+BNn74xRgaBbdhbNIDYvQUEuuErjs= +github.com/mattn/go-sqlite3 v1.14.32/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc= +github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= +github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= +github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/quic-go/qpack v0.5.1 h1:giqksBPnT/HDtZ6VhtFKgoLOWmlyo9Ei6u9PqzIMbhI= +github.com/quic-go/qpack v0.5.1/go.mod h1:+PC4XFrEskIVkcLzpEkbLqq1uCoxPhQuvK5rH1ZgaEg= +github.com/quic-go/quic-go v0.54.0 h1:6s1YB9QotYI6Ospeiguknbp2Znb/jZYjZLRXn9kMQBg= +github.com/quic-go/quic-go v0.54.0/go.mod h1:e68ZEaCdyviluZmy44P6Iey98v/Wfz6HCjQEm+l8zTY= +github.com/redis/go-redis/v9 v9.13.0 h1:PpmlVykE0ODh8P43U0HqC+2NXHXwG+GUtQyz+MPKGRg= +github.com/redis/go-redis/v9 v9.13.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/ugorji/go/codec v1.3.0 h1:Qd2W2sQawAfG8XSvzwhBeoGq71zXOC/Q1E9y/wUcsUA= +github.com/ugorji/go/codec v1.3.0/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= +go.uber.org/mock v0.5.0 h1:KAMbZvZPyBPWgD14IrIQ38QCyjwpvVVV6K/bHl1IwQU= +go.uber.org/mock v0.5.0/go.mod h1:ge71pBPLYDk7QIi1LupWxdAykm7KIEFchiOqd6z7qMM= +golang.org/x/arch v0.20.0 h1:dx1zTU0MAE98U+TQ8BLl7XsJbgze2WnNKF/8tGp/Q6c= +golang.org/x/arch v0.20.0/go.mod h1:bdwinDaKcfZUGpH09BB7ZmOfhalA8lQdzl62l8gGWsk= +golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= +golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= +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/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= +golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= +golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= +golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +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/text v0.28.0 h1:rhazDwis8INMIwQ4tpjLDzUhx6RlXqZNPEM0huQojng= +golang.org/x/text v0.28.0/go.mod h1:U8nCwOR8jO/marOQ0QbDiOngZVEBB7MAiitBuMjXiNU= +golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= +golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= +google.golang.org/protobuf v1.36.9 h1:w2gp2mA27hUeUzj9Ex9FBjsBm40zfaDtEWow293U7Iw= +google.golang.org/protobuf v1.36.9/go.mod h1:fuxRtAxBytpl4zzqUh6/eyUujkJdNiuEkXntxiD/uRU= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= +gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= +gorm.io/gorm v1.30.2 h1:f7bevlVoVe4Byu3pmbWPVHnPsLoWaMjEb7/clyr9Ivs= +gorm.io/gorm v1.30.2/go.mod h1:8Z33v652h4//uMA76KjeDH8mJXPm1QNCYrMeatR0DOE= diff --git a/main.go b/main.go new file mode 100644 index 0000000..e575e0b --- /dev/null +++ b/main.go @@ -0,0 +1,42 @@ +package main + +import ( + "zomo.dev/largehadroncollider/api" + "zomo.dev/largehadroncollider/db" + "zomo.dev/largehadroncollider/ttv" + "zomo.dev/largehadroncollider/util" +) + +func main() { + if err := mainErr(); err != nil { + panic(err) + } +} + +func mainErr() error { + conf, err := util.LoadConfig() + if err != nil { + return err + } + + // init databases + dbConn, err := db.InitDBConn(conf) + if err != nil { + return err + } + + // init twitch connections + twitchConn, err := ttv.InitTwitchConn(conf, dbConn) + if err != nil { + return err + } + + // init http api server + apiServer, err := api.InitApiServer(conf, dbConn, twitchConn) + if err != nil { + return err + } + apiServer.Listen() + + return nil +} diff --git a/ttv/auth.go b/ttv/auth.go new file mode 100644 index 0000000..d5f3195 --- /dev/null +++ b/ttv/auth.go @@ -0,0 +1,78 @@ +package ttv + +import ( + "context" + "errors" + + "github.com/adeithe/go-twitch/api" + "zomo.dev/largehadroncollider/db" + "zomo.dev/largehadroncollider/util" +) + +// sign in to twitch with each saved tokens + +func initAuth(conf *util.Config, dbConn *db.DBConn) (*TwitchAuth, error) { + ctx := context.Background() + + tokens, err := getTokensFromDB(dbConn) + if err != nil { + return nil, err + } + + client := api.New(conf.ClientID) + + accounts, err := testTokens(ctx, client, tokens) + if err != nil { + return nil, err + } + + return &TwitchAuth{ ctx, client, accounts }, nil +} + +type TwitchAuth struct { + Ctx context.Context + Client *api.Client + Accounts []TwitchAuthAccount +} + +type TwitchAuthAccount struct { + api.User + Token string +} + +func getTokensFromDB(dbConn *db.DBConn) ([]string, error) { + // TODO db cold + return []string{}, nil +} + +func testTokens(ctx context.Context, client *api.Client, tokens []string) ([]TwitchAuthAccount, error) { + accounts := make([]TwitchAuthAccount, 0) + for _, token := range tokens { + account, err := testToken(ctx, client, token) + if err != nil { + return nil, err + } + accounts = append(accounts, account) + } + return accounts, nil +} + +func testToken(ctx context.Context, client *api.Client, token string) (TwitchAuthAccount, error) { + users, err := client.Users.List().Do(ctx, api.WithBearerToken(token)) + if err != nil { + return TwitchAuthAccount{}, err + } + + usersData := users.Data + + if len(usersData) <= 0 { + return TwitchAuthAccount{}, errors.New("user data returned an empty array") + } + + mainUser := usersData[0] + + return TwitchAuthAccount{ + mainUser, + token, + }, nil +} diff --git a/ttv/events.go b/ttv/events.go new file mode 100644 index 0000000..49b4e0f --- /dev/null +++ b/ttv/events.go @@ -0,0 +1,6 @@ +package ttv + +// hook into twitch events with a given user auth + +// the websocket server (or anything else) will have a line to send a function here, i.e. ttv.AddEventHook(func(eventInfo) { ... }) +// this ttv/events class will act as an Event Emitter, where these functions will all be called diff --git a/ttv/main.go b/ttv/main.go new file mode 100644 index 0000000..99a13ef --- /dev/null +++ b/ttv/main.go @@ -0,0 +1,19 @@ +package ttv + +import ( + "zomo.dev/largehadroncollider/db" + "zomo.dev/largehadroncollider/util" +) + +func InitTwitchConn(conf *util.Config, dbConn *db.DBConn) (*TwitchConn, error) { + auth, err := initAuth(conf, dbConn) + if err != nil { + return nil, err + } + + return &TwitchConn{ auth }, nil +} + +type TwitchConn struct { + Auth *TwitchAuth +} diff --git a/util/conf.go b/util/conf.go new file mode 100644 index 0000000..d114c0b --- /dev/null +++ b/util/conf.go @@ -0,0 +1,54 @@ +package util + +import ( + "errors" + "os" + "strings" + + "github.com/joho/godotenv" +) + +func LoadConfig() (*Config, error) { + config := Config{} + + err := config.loadEnv() + if err != nil { + return nil, err + } + // other sources? + + config.verify() + return &config, nil +} + +type Config struct { + ClientID string + RedirectURI string +} + +func (c *Config) loadEnv() error { + err := godotenv.Load() + if err != nil { + return err + } + + if str, found := os.LookupEnv("CLIENT_ID"); found { + c.ClientID = strings.TrimSpace(str) + } + if str, found := os.LookupEnv("REDIR_URI"); found { + c.RedirectURI = strings.TrimSpace(str) + } + + return nil +} + +func (c *Config) verify() error { + if c.ClientID == "" { + return errors.New("unable to load a configured Client ID") + } + if c.RedirectURI == "" { + return errors.New("unable to load a configured Redirect URI") + } + + return nil +} diff --git a/util/main.go b/util/main.go new file mode 100644 index 0000000..c7d8682 --- /dev/null +++ b/util/main.go @@ -0,0 +1 @@ +package util diff --git a/web/oauth-login/auth.html b/web/oauth-login/auth.html new file mode 100644 index 0000000..ce04c37 --- /dev/null +++ b/web/oauth-login/auth.html @@ -0,0 +1 @@ + diff --git a/web/oauth-login/login.html b/web/oauth-login/login.html new file mode 100644 index 0000000..ccd0271 --- /dev/null +++ b/web/oauth-login/login.html @@ -0,0 +1,3 @@ + + +Connect with Twitch