diff --git a/.gitignore b/.gitignore index 723ef36..822f71d 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ -.idea \ No newline at end of file +.idea + +# Configuration files +config.toml +*.db + +# Keep example configs +!*.example \ No newline at end of file diff --git a/app/app.go b/app/app.go index 6ef31ce..fb758ce 100644 --- a/app/app.go +++ b/app/app.go @@ -3,22 +3,30 @@ package app import ( "context" "fmt" + "go_oxspeak_server/config" + "go_oxspeak_server/network/http" + "go_oxspeak_server/network/udp" "os" "os/signal" "syscall" ) type App struct { + cfg *config.Config + + // Serveurs + udpServer *udp.Server + httpServer *http.Server + + // Context (to be used for graceful shutdown) + ctx context.Context + cancel context.CancelFunc + sigChan chan os.Signal + errChan chan error } -func New() *App { - return &App{} -} - -func (app *App) Run() error { - // Context pour gérer l'arrêt gracieux +func NewApp(cfg *config.Config) *App { ctx, cancel := context.WithCancel(context.Background()) - defer cancel() // Channel pour capturer les signaux d'arrêt (Ctrl+c, etc...) sigChan := make(chan os.Signal, 1) @@ -27,29 +35,55 @@ func (app *App) Run() error { // Channel pour les erreurs errChan := make(chan error, 1) + // Servers + udpServer := udp.NewServer(cfg.Server.BindAddr) + httpServer := http.NewServer(cfg.Server.BindAddr) + + return &App{ + cfg: cfg, + udpServer: udpServer, + httpServer: httpServer, + ctx: ctx, + cancel: cancel, + sigChan: sigChan, + errChan: errChan, + } +} + +func (app *App) Run() error { + // Context pour gérer l'arrêt gracieux + defer app.cancel() + // Lancer les app ici - go app.runWorker(ctx, errChan) + go app.runWorkers() fmt.Println("App started, press CTRL+C to stop...") select { - case err := <-errChan: + case err := <-app.errChan: return fmt.Errorf("error in the app: %w", err) - case sig := <-sigChan: + case sig := <-app.sigChan: fmt.Printf("\nSIGTERM received: %v, stopping app...\n", sig) - cancel() // Annule le context pour arrêter les goroutines + app.cancel() // Annule le context pour arrêter les goroutines return nil } } -func (app *App) runWorker(ctx context.Context, err_chan chan<- error) { - for { - select { - case <-ctx.Done(): - fmt.Println("Worker stopped") - return - default: - // Logique métier ... +func (app *App) runWorkers() { + // lancer le serveur udp + go func() { + if err := app.udpServer.Run(); err != nil { + app.errChan <- err } - } + }() + + // lancer le serveur http + go func() { + if err := app.httpServer.Run(); err != nil { + app.errChan <- err + } + }() + + <-app.ctx.Done() + fmt.Println("App stopped") } diff --git a/config.example.toml b/config.example.toml new file mode 100644 index 0000000..3dae990 --- /dev/null +++ b/config.example.toml @@ -0,0 +1,41 @@ +# OxSpeak Server Configuration +# Copy this file to config.toml and modify the values according to your needs + +[server] +# Server listening address + port (TCP+UDP) +bind_addr = "0.0.0.0:7000" +# Mode: "debug" or "release" +mode = "release" + +[database] +# Database type: "sqlite", "postgres" or "mysql" +type = "sqlite" + +# SQLite configuration (default) +# Path to the database file +path = "./oxspeak.db" + +# PostgreSQL configuration (uncomment and configure if type = "postgres") +# host = "localhost" +# port = 5432 +# user = "postgres" +# password = "your_password" +# dbname = "oxspeak" +# sslmode = "disable" + +# MySQL configuration (uncomment and configure if type = "mysql") +# host = "localhost" +# port = 3306 +# user = "root" +# password = "your_password" +# dbname = "oxspeak" + +[jwt] +# Secret key for JWT token generation (CHANGE THIS VALUE!) +secret = "your_very_secure_jwt_secret_to_change" +# Token validity duration in hours +expiration = 24 + +[udp] +# UDP server port (voice) +port = 9000 \ No newline at end of file diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000..7970007 --- /dev/null +++ b/config/config.go @@ -0,0 +1,152 @@ +package config + +import ( + "fmt" + "os" + "path/filepath" + "sync" + + "github.com/BurntSushi/toml" +) + +type Config struct { + Server ServerConfig `toml:"server"` + Database DatabaseConfig `toml:"database"` + JWT JWTConfig `toml:"jwt"` +} + +type ServerConfig struct { + BindAddr string `toml:"bind_addr"` + Mode string `toml:"mode"` // "debug" or "release" +} + +type DatabaseConfig struct { + Type string `toml:"type"` // "sqlite", "postgres", "mysql" + Path string `toml:"path"` // For SQLite + Host string `toml:"host"` // For PostgreSQL/MySQL + Port int `toml:"port"` // For PostgreSQL/MySQL + User string `toml:"user"` // For PostgreSQL/MySQL + Password string `toml:"password"` // For PostgreSQL/MySQL + DBName string `toml:"dbname"` // For PostgreSQL/MySQL + SSLMode string `toml:"sslmode"` // For PostgreSQL +} + +type JWTConfig struct { + Secret string `toml:"secret"` + Expiration int `toml:"expiration"` // in hours +} + +var ( + instance *Config + once sync.Once +) + +// Load loads the configuration from a TOML file +func Load(configPath string) (*Config, error) { + var err error + once.Do(func() { + instance = &Config{} + + if _, loadErr := toml.DecodeFile(configPath, instance); loadErr != nil { + err = fmt.Errorf("failed to load configuration: %w", loadErr) + return + } + + // Default values if not specified + if instance.Server.BindAddr == "" { + instance.Server.BindAddr = "0.0.0.0:7000" + } + if instance.Server.Mode == "" { + instance.Server.Mode = "release" + } + if instance.Database.Type == "" { + instance.Database.Type = "sqlite" + } + if instance.Database.Path == "" { + instance.Database.Path = "./oxspeak.db" + } + if instance.JWT.Expiration == 0 { + instance.JWT.Expiration = 24 + } + + // Validation + if instance.JWT.Secret == "" { + err = fmt.Errorf("JWT secret is required") + return + } + + if instance.Database.Type != "sqlite" && instance.Database.Type != "postgres" && instance.Database.Type != "mysql" { + err = fmt.Errorf("invalid database type: %s (accepted values: sqlite, postgres, mysql)", instance.Database.Type) + return + } + }) + + return instance, err +} + +// Get returns the configuration instance (must be loaded first) +func Get() *Config { + if instance == nil { + panic("configuration not loaded. Call Load() first") + } + return instance +} + +// GetDSN returns the connection string based on the database type +func (c *Config) GetDSN() string { + switch c.Database.Type { + case "sqlite": + return c.Database.Path + case "postgres": + return fmt.Sprintf( + "host=%s port=%d user=%s password=%s dbname=%s sslmode=%s", + c.Database.Host, + c.Database.Port, + c.Database.User, + c.Database.Password, + c.Database.DBName, + c.Database.SSLMode, + ) + case "mysql": + return fmt.Sprintf( + "%s:%s@tcp(%s:%d)/%s?charset=utf8mb4&parseTime=True&loc=Local", + c.Database.User, + c.Database.Password, + c.Database.Host, + c.Database.Port, + c.Database.DBName, + ) + default: + return "" + } +} + +// GetDialector returns the GORM dialect name +func (c *Config) GetDialector() string { + return c.Database.Type +} + +// FindConfigFile searches for the config file in multiple locations +func FindConfigFile() (string, error) { + locations := []string{ + "./config.toml", + "./config/config.toml", + "/etc/oxspeak/config.toml", + } + + // Add executable directory + if exePath, err := os.Executable(); err == nil { + exeDir := filepath.Dir(exePath) + locations = append([]string{ + filepath.Join(exeDir, "config.toml"), + }, locations...) + } + + for _, path := range locations { + if _, err := os.Stat(path); err == nil { + return path, nil + } + } + + return "", fmt.Errorf("configuration file not found in locations: %v", locations) +} diff --git a/go.mod b/go.mod index fd66d18..ba4dd9f 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,10 @@ go 1.25.3 require ( github.com/deckarep/golang-set/v2 v2.8.0 github.com/gin-gonic/gin v1.11.0 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/uuid v1.6.0 + github.com/gorilla/websocket v1.5.3 + github.com/puzpuzpuz/xsync/v4 v4.2.0 gorm.io/driver/mysql v1.6.0 gorm.io/driver/postgres v1.6.0 gorm.io/driver/sqlite v1.6.0 @@ -14,6 +17,7 @@ require ( require ( filippo.io/edwards25519 v1.1.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/bytedance/gopkg v0.1.3 // indirect github.com/bytedance/sonic v1.14.2 // indirect github.com/bytedance/sonic/loader v0.4.0 // indirect @@ -26,8 +30,6 @@ require ( github.com/go-sql-driver/mysql v1.9.3 // indirect github.com/goccy/go-json v0.10.5 // indirect github.com/goccy/go-yaml v1.18.0 // indirect - github.com/golang-jwt/jwt/v5 v5.3.0 // indirect - github.com/gorilla/websocket v1.5.3 // indirect github.com/jackc/pgpassfile v1.0.0 // indirect github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect github.com/jackc/pgx/v5 v5.7.6 // indirect @@ -42,19 +44,16 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.4 // indirect - github.com/puzpuzpuz/xsync/v4 v4.2.0 // indirect github.com/quic-go/qpack v0.5.1 // indirect - github.com/quic-go/quic-go v0.55.0 // indirect + github.com/quic-go/quic-go v0.56.0 // indirect github.com/twitchyliquid64/golang-asm v0.15.1 // indirect github.com/ugorji/go/codec v1.3.1 // indirect go.uber.org/mock v0.6.0 // indirect - golang.org/x/arch v0.22.0 // indirect + golang.org/x/arch v0.23.0 // indirect golang.org/x/crypto v0.43.0 // indirect - golang.org/x/mod v0.29.0 // indirect golang.org/x/net v0.46.0 // indirect - golang.org/x/sync v0.17.0 // indirect - golang.org/x/sys v0.37.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect golang.org/x/text v0.30.0 // indirect - golang.org/x/tools v0.38.0 // indirect google.golang.org/protobuf v1.36.10 // indirect ) diff --git a/go.sum b/go.sum index c87894e..eb9ff21 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,7 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M= github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM= github.com/bytedance/sonic v1.14.2 h1:k1twIoe97C1DtYUo+fZQy865IuHia4PR5RPiuGPPIIE= @@ -9,6 +11,7 @@ github.com/bytedance/sonic/loader v0.4.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCc 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 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/deckarep/golang-set/v2 v2.8.0 h1:swm0rlPCmdWn9mESxKOjWk8hXSqoxOp+ZlfuyaAdFlQ= github.com/deckarep/golang-set/v2 v2.8.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= @@ -18,6 +21,8 @@ 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/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s= +github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= 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= @@ -32,6 +37,8 @@ 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/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +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/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= 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= @@ -66,17 +73,14 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G 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 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/puzpuzpuz/xsync v1.5.2 h1:yRAP4wqSOZG+/4pxJ08fPTwrfL0IzE/LKQ/cw509qGY= -github.com/puzpuzpuz/xsync v1.5.2/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg= -github.com/puzpuzpuz/xsync/v3 v3.5.1 h1:GJYJZwO6IdxN/IKbneznS6yPkVC+c3zyY/j19c++5Fg= -github.com/puzpuzpuz/xsync/v3 v3.5.1/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA= github.com/puzpuzpuz/xsync/v4 v4.2.0 h1:dlxm77dZj2c3rxq0/XNvvUKISAmovoXF4a4qM6Wvkr0= github.com/puzpuzpuz/xsync/v4 v4.2.0/go.mod h1:VJDmTCJMBt8igNxnkQd86r+8KUeN1quSfNKu5bLYFQo= 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.55.0 h1:zccPQIqYCXDt5NmcEabyYvOnomjs8Tlwl7tISjJh9Mk= -github.com/quic-go/quic-go v0.55.0/go.mod h1:DR51ilwU1uE164KuWXhinFcKWGlEjzys2l8zUl5Ss1U= +github.com/quic-go/quic-go v0.56.0 h1:q/TW+OLismmXAehgFLczhCDTYB3bFmua4D9lsNBWxvY= +github.com/quic-go/quic-go v0.56.0/go.mod h1:9gx5KsFQtw2oZ6GZTyh+7YEvOxWCL9WZAepnHxgAo6c= 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= @@ -87,33 +91,34 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= 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.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY= github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI= -golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= +golang.org/x/arch v0.23.0 h1:lKF64A2jF6Zd8L0knGltUnegD62JMFBiCPBmQpToHhg= +golang.org/x/arch v0.23.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A= golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= -golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.46.0 h1:giFlY12I07fugqwPuWJi68oOnpfqFnJIJzaIIm2JVV4= golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= -golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= -golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= -golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= -golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= +golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg= gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo= @@ -121,7 +126,5 @@ gorm.io/driver/postgres v1.6.0 h1:2dxzU8xJ+ivvqTRph34QX+WrRaJlmfyPqXmoGVjMBa4= gorm.io/driver/postgres v1.6.0/go.mod h1:vUw0mrGgrTK+uPHEhAdV4sfFELrByKVGnaVRkXDhtWo= gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ= gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8= -gorm.io/gorm v1.31.0 h1:0VlycGreVhK7RF/Bwt51Fk8v0xLiiiFdbGDPIZQ7mJY= -gorm.io/gorm v1.31.0/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg= gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs= diff --git a/main.go b/main.go index 8da3c94..92c01ab 100644 --- a/main.go +++ b/main.go @@ -1,15 +1,44 @@ package main -import app "go_oxspeak_server/app" +import ( + "flag" + "log" -//TIP

To run your code, right-click the code and select Run.

Alternatively, click -// the icon in the gutter and select the Run menu item from here.

+ "go_oxspeak_server/app" + "go_oxspeak_server/config" +) func main() { + // Flag to specify the config file path + configPath := flag.String("config", "", "Path to configuration file (default: automatic search)") + flag.Parse() + + // Load configuration + var cfg *config.Config + var err error + + if *configPath != "" { + cfg, err = config.Load(*configPath) + } else { + // Automatic search + path, findErr := config.FindConfigFile() + if findErr != nil { + log.Fatalf("Error: %v", findErr) + } + log.Printf("Configuration file found: %s", path) + cfg, err = config.Load(path) + } - process := app.App{} - err := process.Run() if err != nil { + log.Fatalf("Failed to load configuration: %v", err) + } + + log.Printf("Configuration loaded - Database: %s", cfg.Database.Type) + + // Start application + process := app.NewApp(cfg) + if err = process.Run(); err != nil { + log.Fatalf("Failed to start application: %v", err) return } } diff --git a/network/http/server.go b/network/http/server.go index 61ec494..d8f79a3 100644 --- a/network/http/server.go +++ b/network/http/server.go @@ -22,6 +22,6 @@ func NewServer(addr string) *Server { return s } -func (s *Server) Start() error { +func (s *Server) Run() error { return s.router.Run(s.addr) } diff --git a/network/udp/server.go b/network/udp/server.go index cb9cf2b..9399c65 100644 --- a/network/udp/server.go +++ b/network/udp/server.go @@ -18,17 +18,17 @@ type Server struct { cancel context.CancelFunc } -func NewServer(bindAddr string) (*Server, error) { +func NewServer(bindAddr string) *Server { ctx, cancel := context.WithCancel(context.Background()) return &Server{ bindAddr: bindAddr, ctx: ctx, cancel: cancel, - }, nil + } } -func (s *Server) run() error { +func (s *Server) Run() error { add, err := net.ResolveUDPAddr("udp", s.bindAddr) if err != nil { return fmt.Errorf("cannot resolve address: %w", err)