2021-05-02 12:29:43 +00:00
package main
import (
2021-11-19 18:37:42 +00:00
"context"
2021-05-02 12:29:43 +00:00
"embed"
2021-11-19 18:37:42 +00:00
"errors"
2021-05-02 12:29:43 +00:00
"flag"
"fmt"
"io/fs"
2021-09-18 20:06:22 +00:00
"math/rand"
2021-05-02 19:33:37 +00:00
"runtime"
2021-05-02 12:29:43 +00:00
"time"
2021-11-19 18:37:42 +00:00
"github.com/strimertul/strimertul/modules/http"
2021-11-15 12:33:40 +00:00
kv "github.com/strimertul/kilovolt/v5"
2021-05-02 19:33:37 +00:00
2021-05-10 21:09:15 +00:00
"github.com/strimertul/strimertul/database"
2021-05-02 12:29:43 +00:00
"github.com/strimertul/strimertul/modules"
"github.com/strimertul/strimertul/modules/loyalty"
2021-05-14 14:37:54 +00:00
"github.com/strimertul/strimertul/modules/stulbe"
2021-05-14 11:15:38 +00:00
"github.com/strimertul/strimertul/modules/twitch"
2021-05-02 12:29:43 +00:00
"github.com/dgraph-io/badger/v3"
"github.com/pkg/browser"
2021-05-02 19:33:37 +00:00
"github.com/mattn/go-colorable"
"github.com/sirupsen/logrus"
2021-06-05 23:18:31 +00:00
_ "net/http/pprof"
2021-05-02 12:29:43 +00:00
)
const AppTitle = "strimertül"
const AppHeader = `
_ _ _ O O _
__ | | _ _ _ ( _ ) _ __ ___ _ _ | | _ _ _ | |
( _ - < _ | ' _ | | ' \ / - _ ) ' _ | _ | || | |
/ __ / \ __ | _ | | _ | _ | _ | _ \ ___ | _ | \ __ | \ _ , _ | _ | `
const DefaultBind = "localhost:4337"
//go:embed frontend/dist/*
var frontend embed . FS
2021-05-02 19:33:37 +00:00
var log = logrus . New ( )
func wrapLogger ( module string ) logrus . FieldLogger {
return log . WithField ( "module" , module )
}
func parseLogLevel ( level string ) logrus . Level {
switch level {
case "error" :
return logrus . ErrorLevel
case "warn" , "warning" :
return logrus . WarnLevel
case "info" , "notice" :
return logrus . InfoLevel
case "debug" :
return logrus . DebugLevel
case "trace" :
return logrus . TraceLevel
default :
return logrus . InfoLevel
2021-05-02 12:29:43 +00:00
}
}
func main ( ) {
2021-05-02 19:33:37 +00:00
// Get cmd line parameters
2021-05-10 21:09:15 +00:00
dbdir := flag . String ( "dbdir" , "data" , "Path to strimertul database dir" )
2021-05-02 19:33:37 +00:00
loglevel := flag . String ( "loglevel" , "info" , "Logging level (debug, info, warn, error)" )
2021-05-02 12:29:43 +00:00
flag . Parse ( )
2021-09-18 20:06:22 +00:00
rand . Seed ( time . Now ( ) . UnixNano ( ) )
2021-05-02 19:33:37 +00:00
log . SetLevel ( parseLogLevel ( * loglevel ) )
// Ok this is dumb but listen, I like colors.
if runtime . GOOS == "windows" {
log . SetFormatter ( & logrus . TextFormatter { ForceColors : true } )
log . SetOutput ( colorable . NewColorableStdout ( ) )
}
// Print the app header :D
2021-05-02 12:29:43 +00:00
fmt . Println ( AppHeader )
// Loading routine
2021-05-16 16:10:51 +00:00
dblogger := wrapLogger ( "db" )
db , err := database . Open ( badger . DefaultOptions ( * dbdir ) . WithLogger ( dblogger ) , dblogger )
2021-05-02 12:29:43 +00:00
failOnError ( err , "Could not open DB" )
2021-11-19 18:37:42 +00:00
defer func ( ) { logOnError ( db . Close ( ) , "Could not close DB" ) } ( )
2021-05-02 12:29:43 +00:00
// Check if onboarding was completed
var moduleConfig modules . ModuleConfig
2021-05-10 21:09:15 +00:00
err = db . GetJSON ( modules . ModuleConfigKey , & moduleConfig )
2021-05-02 12:29:43 +00:00
if err != nil {
2021-11-19 18:37:42 +00:00
if errors . Is ( err , badger . ErrKeyNotFound ) {
2021-05-02 12:29:43 +00:00
moduleConfig = modules . ModuleConfig { CompletedOnboarding : false }
} else {
fatalError ( err , "Could not read from DB" )
}
}
if ! moduleConfig . CompletedOnboarding {
// Initialize DB as empty and default endpoint
2021-11-19 18:37:42 +00:00
failOnError ( db . PutJSON ( http . ServerConfigKey , http . ServerConfig {
2021-05-10 21:09:15 +00:00
Bind : DefaultBind ,
} ) , "could not save http config" )
failOnError ( db . PutJSON ( modules . ModuleConfigKey , modules . ModuleConfig {
EnableKV : true ,
2021-05-14 11:15:38 +00:00
EnableTwitch : false ,
2021-05-10 21:09:15 +00:00
EnableStulbe : false ,
CompletedOnboarding : true ,
} ) , "could not save onboarding config" )
2021-05-02 12:29:43 +00:00
fmt . Printf ( "It appears this is your first time running %s! Please go to http://%s and make sure to configure anything you want!\n\n" , AppTitle , DefaultBind )
}
// Initialize KV (required)
2021-05-16 15:55:40 +00:00
hub , err := kv . NewHub ( db . Client ( ) , wrapLogger ( "kv" ) )
failOnError ( err , "Could not initialize kilovolt hub" )
2021-05-02 12:29:43 +00:00
go hub . Run ( )
// Get Stulbe config, if enabled
2021-05-11 11:12:00 +00:00
var stulbeManager * stulbe . Manager = nil
2021-05-02 12:29:43 +00:00
if moduleConfig . EnableStulbe {
2021-05-18 11:30:27 +00:00
stulbeLogger := wrapLogger ( "stulbe" )
stulbeManager , err = stulbe . Initialize ( db , stulbeLogger )
2021-05-07 16:36:23 +00:00
if err != nil {
log . WithError ( err ) . Error ( "Stulbe initialization failed! Module was temporarely disabled" )
moduleConfig . EnableStulbe = false
2021-05-18 13:53:17 +00:00
} else {
defer stulbeManager . Close ( )
go func ( ) {
err := stulbeManager . ReceiveEvents ( )
stulbeLogger . WithError ( err ) . Error ( "Stulbe subscription died unexpectedly!" )
} ( )
2021-05-07 16:36:23 +00:00
}
2021-05-02 12:29:43 +00:00
}
var loyaltyManager * loyalty . Manager
loyaltyLogger := wrapLogger ( "loyalty" )
if moduleConfig . EnableLoyalty {
2021-05-16 15:55:40 +00:00
loyaltyManager , err = loyalty . NewManager ( db , loyaltyLogger )
2021-05-02 12:29:43 +00:00
if err != nil {
2021-05-14 11:15:38 +00:00
log . WithError ( err ) . Error ( "Loyalty initialization failed! Module was temporarily disabled" )
2021-05-07 16:36:23 +00:00
moduleConfig . EnableLoyalty = false
2021-05-02 12:29:43 +00:00
}
2021-05-14 14:37:54 +00:00
if stulbeManager != nil {
2021-11-19 18:37:42 +00:00
go func ( ) {
logOnError ( stulbeManager . ReplicateKeys ( [ ] string {
loyalty . ConfigKey ,
loyalty . RewardsKey ,
loyalty . GoalsKey ,
loyalty . PointsPrefix ,
} ) , "Could not replicate loyalty keys" )
} ( )
2021-05-14 14:37:54 +00:00
}
2021-05-02 12:29:43 +00:00
}
//TODO Refactor this to something sane
2021-05-14 11:15:38 +00:00
if moduleConfig . EnableTwitch {
2021-05-02 12:29:43 +00:00
// Create logger
2021-05-14 11:15:38 +00:00
twitchLogger := wrapLogger ( "twitch" )
// Create Twitch client
2021-11-19 18:37:42 +00:00
twitchClient , err := twitch . NewClient ( db , twitchLogger )
if err != nil {
2021-05-14 11:15:38 +00:00
log . WithError ( err ) . Error ( "Twitch initialization failed! Module was temporarily disabled" )
moduleConfig . EnableTwitch = false
2021-11-19 18:37:42 +00:00
} else {
if twitchClient . Bot != nil && moduleConfig . EnableLoyalty {
twitchClient . Bot . SetupLoyalty ( loyaltyManager )
}
2021-05-02 12:29:43 +00:00
}
}
// Create logger and endpoints
httpLogger := wrapLogger ( "http" )
2021-11-19 18:37:42 +00:00
httpServer , err := http . NewServer ( db , httpLogger )
failOnError ( err , "Could not initialize http server" )
2021-05-02 12:29:43 +00:00
fedir , _ := fs . Sub ( frontend , "frontend/dist" )
2021-11-19 18:37:42 +00:00
httpServer . SetFrontend ( fedir )
httpServer . SetHub ( hub )
2021-05-02 12:29:43 +00:00
go func ( ) {
time . Sleep ( time . Second ) // THIS IS STUPID
2021-11-19 18:37:42 +00:00
dashboardURL := fmt . Sprintf ( "http://%s/ui" , httpServer . Config . Bind )
2021-10-28 09:01:52 +00:00
err := browser . OpenURL ( dashboardURL )
if err != nil {
log . WithError ( err ) . Warnf ( "could not open browser, dashboard URL available at: %s" , dashboardURL )
}
2021-05-02 12:29:43 +00:00
} ( )
2021-11-19 18:37:42 +00:00
go func ( ) {
err := db . Subscribe ( context . Background ( ) , func ( changed [ ] database . ModifiedKV ) error {
for _ , pair := range changed {
if pair . Key == modules . ModuleConfigKey {
//TODO Enable/disable modules
}
}
return nil
} , modules . ModuleConfigKey )
log . WithError ( err ) . Error ( "Error while listening to module config changes" )
} ( )
2021-05-02 12:29:43 +00:00
// Start HTTP server
2021-11-19 18:37:42 +00:00
failOnError ( httpServer . Listen ( ) , "HTTP server stopped" )
2021-05-02 12:29:43 +00:00
}
func failOnError ( err error , text string ) {
if err != nil {
fatalError ( err , text )
}
}
2021-11-19 18:37:42 +00:00
func logOnError ( err error , text string ) {
if err != nil {
log . WithError ( err ) . Error ( text )
}
}
2021-05-02 12:29:43 +00:00
func fatalError ( err error , text string ) {
log . Fatalf ( "FATAL ERROR OCCURRED: %s\n\n%s" , text , err . Error ( ) )
}