package main import ( "context" "entgo.io/ent/dialect" "entgo.io/ent/dialect/sql" "flag" "fmt" "github.com/coreos/go-systemd/v22/activation" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" "github.com/go-chi/cors" "github.com/go-chi/render" _ "github.com/jackc/pgx/v4/stdlib" log "github.com/sirupsen/logrus" "github.com/wercker/journalhook" "gopkg.in/yaml.v3" "net" "net/http" "os" "os/signal" "somegit.dev/ALHP/ALHP.GO/ent" "syscall" "time" ) var ( conf *Conf repos []string db *ent.Client journalLog = flag.Bool("journal", false, "Log to systemd journal instead of stdout") sqlDebug = flag.Bool("sqldebug", false, "Enable SQL debug log") configFile = flag.String("config", "config.yaml", "set config file name/path") ) func main() { killSignals := make(chan os.Signal, 1) signal.Notify(killSignals, syscall.SIGINT, syscall.SIGTERM) reloadSignals := make(chan os.Signal, 1) signal.Notify(reloadSignals, syscall.SIGUSR1) flag.Parse() confStr, err := os.ReadFile(*configFile) if err != nil { log.Fatalf("error reading config file: %v", err) } err = yaml.Unmarshal(confStr, &conf) if err != nil { log.Fatalf("error parsing config file: %v", err) } lvl, err := log.ParseLevel(conf.Logging.Level) if err != nil { log.Fatalf("error parsing log level from config: %v", err) } log.SetLevel(lvl) if *journalLog { journalhook.Enable() } if conf.DB.Driver == "pgx" { pdb, err := sql.Open("pgx", conf.DB.ConnectTo) if err != nil { log.Fatalf("failed to open database %s: %v", conf.DB.ConnectTo, err) } drv := sql.OpenDB(dialect.Postgres, pdb.DB()) db = ent.NewClient(ent.Driver(drv)) } else { db, err = ent.Open(conf.DB.Driver, conf.DB.ConnectTo) if err != nil { log.Panicf("failed to open database %s: %v", conf.DB.ConnectTo, err) } defer func(Client *ent.Client) { _ = Client.Close() }(db) } if *sqlDebug { db = db.Debug() } if err := db.Schema.Create(context.Background()); err != nil { log.Panicf("automigrate failed: %v", err) } r := chi.NewRouter() r.Use(middleware.RequestID) r.Use(middleware.RealIP) r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(middleware.Timeout(60 * time.Second)) r.Use(render.SetContentType(render.ContentTypeJSON)) r.Use(cors.AllowAll().Handler) // routes r.Get("/stats", GetStats) r.Get("/packages", GetPackages) log.Info("Start listening...") sockets := make([]net.Listener, 0) // handle systemd socket activation listeners, err := activation.Listeners() if err != nil { panic(err) } if len(listeners) > 0 { for _, l := range listeners { log.Infof("listening on %s", l.Addr()) if err != nil { log.Panicf("failure listing on socket %s: %v", l, err) } sockets = append(sockets, l) l := l go func() { srv := &http.Server{ ReadTimeout: time.Duration(conf.Httpd.Timeout.Read) * time.Second, WriteTimeout: time.Duration(conf.Httpd.Timeout.Write) * time.Second, IdleTimeout: time.Duration(conf.Httpd.Timeout.Idle) * time.Second, Handler: r, } _ = srv.Serve(l) }() } } else { // handle config if not systemd-socket activated for _, l := range conf.Httpd.Listen { if l.Socket != "" { sL, err := net.Listen("unix", l.Socket) log.Infof("Listening on %s", l.Socket) if err != nil { log.Panicf("Failure listing on socket %s: %v", l.Socket, err) } sockets = append(sockets, sL) go func() { srv := &http.Server{ ReadTimeout: time.Duration(conf.Httpd.Timeout.Read) * time.Second, WriteTimeout: time.Duration(conf.Httpd.Timeout.Write) * time.Second, IdleTimeout: time.Duration(conf.Httpd.Timeout.Idle) * time.Second, Handler: r, } _ = srv.Serve(sL) }() } else { log.Infof("Listening on %s:%d", l.Host, l.Port) tL, err := net.Listen("tcp", fmt.Sprintf("%s:%d", l.Host, l.Port)) if err != nil { log.Panicf("Failure listing on %s:%d: %v", l.Host, l.Port, err) } go func(l struct { Socket string Host string Port int }) { srv := &http.Server{ ReadTimeout: time.Duration(conf.Httpd.Timeout.Read) * time.Second, WriteTimeout: time.Duration(conf.Httpd.Timeout.Write) * time.Second, IdleTimeout: time.Duration(conf.Httpd.Timeout.Idle) * time.Second, Handler: r, } err = srv.Serve(tL) if err != nil { log.Fatalf("Failure serving on %s:%d: %v", l.Host, l.Port, err) } }(l) } } } killLoop: for { select { case <-killSignals: break killLoop case <-reloadSignals: confStr, err := os.ReadFile(*configFile) if err != nil { log.Panicf("unable to open config: %v", err) } err = yaml.Unmarshal(confStr, &conf) if err != nil { log.Panicf("unable to parse config: %v", err) } lvl, err := log.ParseLevel(conf.Logging.Level) if err != nil { log.Panicf("failure setting logging level: %v", err) } log.SetLevel(lvl) log.Infof("config reloaded") } } for _, s := range sockets { _ = s.Close() } }