package di import ( "errors" "fmt" "log/slog" "os" "strings" "git.0xdad.com/tblyler/goatcounter-systemd/config" "git.0xdad.com/tblyler/goatcounter-systemd/goatcounter" "git.0xdad.com/tblyler/goatcounter-systemd/journald" ) type Container struct { config config.Config journald *journald.Journald goatcounterMultiSiteClient *goatcounter.MultiSiteClient closers []func() error } func NewContainer() (*Container, error) { container := &Container{} sets := []func() error{ container.setSlog, container.setConfig, container.setJournald, container.setGoatcounterMultiSiteClient, } for _, set := range sets { err := set() if err != nil { return container, err } } return container, nil } func (c *Container) Close() error { errs := make([]error, 0, len(c.closers)) for _, closer := range c.closers { errs = append(errs, closer()) } return errors.Join(errs...) } func (c *Container) setSlog() error { var logLevel slog.Level switch strings.ToLower(strings.TrimSpace(os.Getenv("LOG_LEVEL"))) { case "debug": logLevel = slog.LevelDebug case "info", "": logLevel = slog.LevelInfo case "warn": logLevel = slog.LevelWarn case "error": logLevel = slog.LevelError default: return fmt.Errorf("invalid log level: %s", os.Getenv("LOG_LEVEL")) } logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ AddSource: logLevel <= slog.LevelDebug, Level: logLevel, })) slog.SetDefault(logger) slog.SetLogLoggerLevel(logLevel) return nil } func (c *Container) Config() config.Config { return c.config } func (c *Container) setConfig() error { configPath := os.Getenv("CONFIG_FILE") if configPath == "" { configPath = "/etc/goatcounter-systemd/config.json" } config, err := config.FromFilePath(configPath) if err != nil { return fmt.Errorf("failed to set config: %w", err) } c.config = config return nil } func (c *Container) Journald() *journald.Journald { return c.journald } func (c *Container) setJournald() error { config := c.Config() journald, err := journald.NewJournald(config.LogIdentifier) if err != nil { return fmt.Errorf("failed to set journald: %w", err) } c.closers = append(c.closers, journald.Close) c.journald = journald return nil } func (c *Container) GoatcounterMultiSiteClient() *goatcounter.MultiSiteClient { return c.goatcounterMultiSiteClient } func (c *Container) setGoatcounterMultiSiteClient() error { config := c.Config() siteToClient := map[string]goatcounter.SiteClient{} for _, site := range config.Sites { client, err := goatcounter.NewClient(site.URL, site.APIKey) if err != nil { return fmt.Errorf("failed to create client for site %s: %w", site.URL, err) } for _, host := range site.Hosts { siteToClient[host] = client } } c.goatcounterMultiSiteClient = goatcounter.NewMultiSiteClient(siteToClient, true) return nil }