// Odoo Go Server — Main entrypoint // Mirrors: odoo-bin // // Usage: // // go run ./cmd/odoo-server // ODOO_DB_HOST=localhost ODOO_DB_PORT=5432 go run ./cmd/odoo-server package main import ( "context" "log" "os" "os/signal" "path/filepath" "syscall" "github.com/jackc/pgx/v5/pgxpool" // Import all modules (register models via init()) _ "odoo-go/addons/base" _ "odoo-go/addons/account" _ "odoo-go/addons/product" _ "odoo-go/addons/sale" _ "odoo-go/addons/stock" _ "odoo-go/addons/purchase" _ "odoo-go/addons/hr" _ "odoo-go/addons/project" _ "odoo-go/addons/crm" _ "odoo-go/addons/fleet" _ "odoo-go/addons/l10n_de" // Google integrations (opt-in, only active with API keys) _ "odoo-go/addons/google_address" _ "odoo-go/addons/google_translate" _ "odoo-go/addons/google_calendar" "odoo-go/pkg/modules" "odoo-go/pkg/server" "odoo-go/pkg/service" "odoo-go/pkg/tools" ) func main() { log.SetFlags(log.Ltime | log.Lshortfile) // Load configuration cfg := tools.DefaultConfig() cfg.LoadFromEnv() // Auto-detect frontend/ directory relative to the binary if not set if cfg.FrontendDir == "" { exe, _ := os.Executable() candidate := filepath.Join(filepath.Dir(exe), "frontend") if _, err := os.Stat(candidate); err != nil { // Try relative to working directory candidate = "frontend" } cfg.FrontendDir = candidate } // Auto-detect build/ directory if cfg.BuildDir == "" { exe, _ := os.Executable() candidate := filepath.Join(filepath.Dir(exe), "build") if _, err := os.Stat(candidate); err != nil { candidate = "build" } cfg.BuildDir = candidate } log.Printf("odoo: Odoo Go Server 19.0") log.Printf("odoo: database: %s@%s:%d/%s", cfg.DBUser, cfg.DBHost, cfg.DBPort, cfg.DBName) ctx, cancel := context.WithCancel(context.Background()) defer cancel() // Handle shutdown signals sigCh := make(chan os.Signal, 1) signal.Notify(sigCh, syscall.SIGINT, syscall.SIGTERM) go func() { sig := <-sigCh log.Printf("odoo: received signal %s, shutting down...", sig) cancel() }() // Connect to database pool, err := pgxpool.New(ctx, cfg.DSN()) if err != nil { log.Fatalf("odoo: database connection failed: %v", err) } defer pool.Close() if err := pool.Ping(ctx); err != nil { log.Fatalf("odoo: database ping failed: %v", err) } log.Println("odoo: database connected") // Load modules (base is auto-registered via init()) log.Println("odoo: loading modules...") if err := modules.LoadModules(modules.All()); err != nil { log.Fatalf("odoo: module loading failed: %v", err) } log.Printf("odoo: %d modules loaded", len(modules.All())) // Initialize database schema log.Println("odoo: initializing database schema...") if err := service.InitDatabase(ctx, pool); err != nil { log.Fatalf("odoo: schema init failed: %v", err) } // Migrate schema: add any missing columns for newly registered fields log.Println("odoo: running schema migration...") if err := service.MigrateSchema(ctx, pool); err != nil { log.Printf("odoo: schema migration warning: %v", err) } // Check if database needs setup if service.NeedsSetup(ctx, pool) { log.Println("odoo: database is empty — database manager will be shown at /web/database/manager") } else { log.Println("odoo: database already initialized") } // Start HTTP server srv := server.New(cfg, pool) log.Printf("odoo: starting HTTP service on %s:%d", cfg.HTTPInterface, cfg.HTTPPort) if err := srv.Start(); err != nil { log.Fatalf("odoo: server error: %v", err) } }