203 lines
5.0 KiB
Go
203 lines
5.0 KiB
Go
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()
|
|
}
|
|
}
|