added basic ws commands
This commit is contained in:
@@ -5,5 +5,9 @@ CLIENT_SECRET= # Twitch Client Secret
|
|||||||
REDIR_URI= # Twitch OAuth Redirect URI
|
REDIR_URI= # Twitch OAuth Redirect URI
|
||||||
# Required
|
# Required
|
||||||
|
|
||||||
|
WS_AUTHORIZATION= # the authorization code all websocket clients will need to connect
|
||||||
|
# Required
|
||||||
|
# TODO this is not the final version, this will not be a permanent config option
|
||||||
|
|
||||||
SQLITE_DB= # SQlite DB location
|
SQLITE_DB= # SQlite DB location
|
||||||
# Default: ./db.sqlite
|
# Default: ./db.sqlite
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import (
|
|||||||
|
|
||||||
func InitApiServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*ApiServer, error) {
|
func InitApiServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*ApiServer, error) {
|
||||||
engine := gin.Default()
|
engine := gin.Default()
|
||||||
wsServer, err := ws.InitWSServer(dbConn, twitchConn)
|
wsServer, err := ws.InitWSServer(conf, dbConn, twitchConn)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|||||||
130
api/ws/main.go
130
api/ws/main.go
@@ -1,24 +1,35 @@
|
|||||||
package ws
|
package ws
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
"log"
|
"log"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/gobwas/ws"
|
"github.com/gobwas/ws"
|
||||||
"github.com/gobwas/ws/wsutil"
|
"github.com/gobwas/ws/wsutil"
|
||||||
"zomo.dev/largehadroncollider/db"
|
"zomo.dev/largehadroncollider/db"
|
||||||
"zomo.dev/largehadroncollider/ttv"
|
"zomo.dev/largehadroncollider/ttv"
|
||||||
|
"zomo.dev/largehadroncollider/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func InitWSServer(dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*WSServer, error) {
|
func InitWSServer(conf *util.Config, dbConn *db.DBConn, twitchConn *ttv.TwitchConn) (*WSServer, error) {
|
||||||
return &WSServer{}, nil
|
return &WSServer{conf, dbConn, twitchConn}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type WSServer struct {
|
type WSServer struct {
|
||||||
|
conf *util.Config
|
||||||
|
db *db.DBConn
|
||||||
|
twitch *ttv.TwitchConn
|
||||||
}
|
}
|
||||||
|
|
||||||
type WSConn struct {
|
type WSConn struct {
|
||||||
|
conf *util.Config
|
||||||
|
c net.Conn
|
||||||
events []string
|
events []string
|
||||||
|
authrorized bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
|
func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
|
||||||
@@ -27,42 +38,91 @@ func (wsServer *WSServer) Handle(w http.ResponseWriter, r *http.Request) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
authrorization := ""
|
wsconn := &WSConn{}
|
||||||
|
go wsconn.handleThread(wsServer.conf, conn)
|
||||||
go func() {
|
|
||||||
defer conn.Close()
|
|
||||||
|
|
||||||
for {
|
|
||||||
msg, _, err := wsutil.ReadClientData(conn)
|
|
||||||
if err != nil {
|
|
||||||
// TODO handle error better, print more conn info
|
|
||||||
log.Printf("Error reading data from ws connection %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cmd, err := parseCommand(string(msg))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error parsing command data from ws connection %v", err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
log.Printf("%+v", cmd)
|
|
||||||
|
|
||||||
if authrorization == "" {
|
|
||||||
// TODO authorize connection
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO run with cmd
|
|
||||||
|
|
||||||
// TODO errors will be responded to the client
|
|
||||||
// if the response errors, log and exit the loop
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func runCommand(cmd *Command) {
|
func (conn *WSConn) handleThread(conf *util.Config, c net.Conn) {
|
||||||
|
conn.conf = conf
|
||||||
|
conn.c = c
|
||||||
|
defer c.Close()
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg, _, err := wsutil.ReadClientData(conn.c)
|
||||||
|
if err != nil {
|
||||||
|
// TODO handle error better, print more conn info
|
||||||
|
log.Printf("Error reading data from ws connection %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
cmd, err := parseCommand(string(msg))
|
||||||
|
if err != nil {
|
||||||
|
log.Printf("Error parsing command data from ws connection %v", err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("%+v", cmd)
|
||||||
|
|
||||||
|
err = conn.runCommand(cmd)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
_, err := fmt.Fprintf(conn.c, "error running command: %s: %v", cmd.Command, err)
|
||||||
|
if err != nil {
|
||||||
|
// TODO better print
|
||||||
|
log.Printf("ERROR: unable to send error to client: %v", err)
|
||||||
|
log.Printf("ERROR: client details: %+v", conn)
|
||||||
|
log.Printf("ERROR: command details: %+v", cmd)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *WSConn) runCommand(cmd *Command) error {
|
||||||
|
switch strings.ToLower(cmd.Command) {
|
||||||
|
case "ping":
|
||||||
|
return conn.runCommandPing(cmd.Args)
|
||||||
|
case "authorization":
|
||||||
|
return conn.runCommandAuthorization(cmd.Args)
|
||||||
|
|
||||||
|
case "events-add":
|
||||||
|
return conn.runCommandEventsAdd(cmd.Args)
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("invalid command: %s", cmd.Command)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *WSConn) runCommandPing(_ []string) error {
|
||||||
|
_, err := fmt.Fprint(conn.c, "pong")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *WSConn) runCommandAuthorization(args []string) error {
|
||||||
|
if conn.authrorized {
|
||||||
|
return errors.New("connection already authorized")
|
||||||
|
}
|
||||||
|
if len(args) < 1 {
|
||||||
|
return errors.New("no authorization key given")
|
||||||
|
}
|
||||||
|
|
||||||
|
key := args[0]
|
||||||
|
if key != conn.conf.WSAuthorization {
|
||||||
|
return errors.New("invalid authorization key")
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.authrorized = true
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (conn *WSConn) runCommandEventsAdd(args []string) error {
|
||||||
|
if !conn.authrorized {
|
||||||
|
return errors.New("unauthorized")
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO subscribe
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ type Config struct {
|
|||||||
ClientSecret string
|
ClientSecret string
|
||||||
RedirectURI string
|
RedirectURI string
|
||||||
SQliteDB string
|
SQliteDB string
|
||||||
|
WSAuthorization string
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Config) def() {
|
func (c *Config) def() {
|
||||||
@@ -51,6 +52,9 @@ func (c *Config) loadEnv() error {
|
|||||||
if str, found := os.LookupEnv("SQLITE_DB"); found {
|
if str, found := os.LookupEnv("SQLITE_DB"); found {
|
||||||
c.SQliteDB = strings.TrimSpace(str)
|
c.SQliteDB = strings.TrimSpace(str)
|
||||||
}
|
}
|
||||||
|
if str, found := os.LookupEnv("WS_AUTHORIZATION"); found {
|
||||||
|
c.WSAuthorization = strings.TrimSpace(str)
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
@@ -65,6 +69,9 @@ func (c *Config) verify() error {
|
|||||||
if c.RedirectURI == "" {
|
if c.RedirectURI == "" {
|
||||||
return errors.New("unable to load a configured Redirect URI")
|
return errors.New("unable to load a configured Redirect URI")
|
||||||
}
|
}
|
||||||
|
if c.WSAuthorization == "" {
|
||||||
|
return errors.New("unable to load a configured WS Authorization code")
|
||||||
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user