Files
pkgdash/cmd/pkgdash/main.go
Gorbunov Kirill Andreevich 8e633fe83f #19 reset HEAD
2024-12-18 21:16:11 +03:00

238 lines
6.8 KiB
Go

package main
import (
"context"
"crypto/tls"
"embed"
"io/fs"
"net/http"
"time"
jsoncodec "go.unistack.org/micro-codec-json/v3"
jsonpbcodec "go.unistack.org/micro-codec-jsonpb/v3"
yamlcodec "go.unistack.org/micro-codec-yaml/v3"
envconfig "go.unistack.org/micro-config-env/v3"
fileconfig "go.unistack.org/micro-config-file/v3"
vaultconfig "go.unistack.org/micro-config-vault/v3"
victoriameter "go.unistack.org/micro-meter-victoriametrics/v3"
httpsrv "go.unistack.org/micro-server-http/v3"
healthhandler "go.unistack.org/micro-server-http/v3/handler/health"
meterhandler "go.unistack.org/micro-server-http/v3/handler/meter"
spahandler "go.unistack.org/micro-server-http/v3/handler/spa"
swaggerui "go.unistack.org/micro-server-http/v3/handler/swagger-ui"
"go.unistack.org/micro/v3"
"go.unistack.org/micro/v3/config"
"go.unistack.org/micro/v3/logger"
slog "go.unistack.org/micro/v3/logger/slog"
"go.unistack.org/micro/v3/meter"
"go.unistack.org/micro/v3/server"
rutil "go.unistack.org/micro/v3/util/reflect"
appconfig "go.unistack.org/pkgdash/internal/config"
"go.unistack.org/pkgdash/internal/database"
"go.unistack.org/pkgdash/internal/handler"
"go.unistack.org/pkgdash/internal/storage"
_ "go.unistack.org/pkgdash/internal/storage/sqlite"
"go.unistack.org/pkgdash/internal/worker"
pb "go.unistack.org/pkgdash/proto"
)
const appName = "pkgdash"
var (
BuildDate string = "now" // filled when build
AppVersion string = "latest" // filled when build
)
//go:generate rm -rf assets
//go:generate mkdir assets
//go:generate cp -vr ../../ui/dist/ui assets/
//go:embed assets/*
var assets embed.FS
func main() {
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
log := slog.NewLogger(logger.WithLevel(logger.DebugLevel))
if err := log.Init(); err != nil {
log.Fatal(ctx, "failed to init logger")
}
cfg := appconfig.NewConfig(appName, AppVersion) // create new empty config
vc := vaultconfig.NewConfig(
config.AllowFail(true), // that may be not exists
config.Struct(cfg), // load from vault
config.Codec(jsoncodec.NewCodec()), // vault config in json
config.BeforeLoad(func(ctx context.Context, c config.Config) error {
return c.Init(
vaultconfig.HTTPClient(&http.Client{
Transport: &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
},
}),
vaultconfig.Address(cfg.Vault.Addr),
vaultconfig.Timeout(5*time.Second),
vaultconfig.Token(cfg.Vault.Token),
vaultconfig.Path(cfg.Vault.Path),
)
}),
)
if err := config.Load(ctx,
[]config.Config{
config.NewConfig( // load from defaults
config.Struct(cfg), // pass config struct
),
fileconfig.NewConfig( // load from file
config.AllowFail(true), // that may be not exists
config.Struct(cfg), // pass config struct
config.Codec(yamlcodec.NewCodec()), // file config in json
fileconfig.Path("./local.yaml"), // nearby file
),
envconfig.NewConfig( // load from environment
config.Struct(cfg), // pass config struct
),
vc,
}, config.LoadOverride(true),
); err != nil {
log.Fatal(ctx, "failed to load config: %v", err)
}
if err := config.Validate(ctx, cfg); err != nil {
log.Fatal(ctx, "failed to validate config: %v", err)
}
swaggerui.Config["url"] = "../service.swagger.yaml"
meter.DefaultMeter = victoriameter.NewMeter(
meter.Path(cfg.Meter.Path),
meter.WriteFDMetrics(true),
meter.WriteProcessMetrics(true),
meter.Address(cfg.Meter.Addr),
)
svc := micro.NewService()
if err := svc.Init(
micro.Server(httpsrv.NewServer()),
micro.Name(cfg.Server.Name),
micro.Version(cfg.Server.Version),
); err != nil {
log.Fatal(ctx, "failed to init service: %v", err)
}
assetsUI, err := fs.Sub(assets, "assets/ui")
if err != nil {
log.Fatal(ctx, "failed to get assets: %v", err)
}
if err := svc.Server("http").Init(
server.Address(cfg.Server.Addr),
server.Name(cfg.Server.Name),
server.Version(cfg.Server.Version),
server.Codec("application/json", jsonpbcodec.NewCodec()),
httpsrv.PathHandler(http.MethodGet, "/ui/*", spahandler.Handler("/ui/", assetsUI)),
httpsrv.PathHandler(http.MethodHead, "/ui/*", spahandler.Handler("/ui/", assetsUI)),
httpsrv.PathHandler(http.MethodGet, "/swagger-ui/*", swaggerui.Handler("/swagger-ui")),
); err != nil {
log.Fatal(ctx, "failed to init service: %v", err)
}
if err := database.ParseDSN(cfg.Database); err != nil {
log.Fatal(ctx, "failed to init database: %v", err)
}
db, err := database.Connect(ctx, cfg.Database, log)
if err != nil {
log.Fatal(ctx, "failed to connect database: %v", err)
}
store, err := storage.NewStorage(cfg.Database.Type, log, db)
if err != nil {
log.Fatal(ctx, "failed to init storage: %v", err)
}
h, err := handler.NewHandler(log, store)
if err != nil {
log.Fatal(ctx, "failed to create handler: %v", err)
}
if err := svc.Init(
micro.Logger(
log.Clone(logger.WithLevel(logger.ParseLevel(cfg.Server.LoggerLevel))),
),
); err != nil {
log.Fatal(ctx, "failed to init service", err)
}
if err := pb.RegisterPkgdashServer(svc.Server("http"), h); err != nil {
log.Fatal(ctx, "failed to register handler", err)
}
intsvc := httpsrv.NewServer(
server.Codec("application/json", jsoncodec.NewCodec()),
server.Address(cfg.Meter.Addr),
)
if err := intsvc.Init(); err != nil {
log.Fatal(ctx, "failed to init http srv: %v", err)
}
if err := healthhandler.RegisterHealthServiceServer(intsvc, healthhandler.NewHandler()); err != nil {
log.Fatal(ctx, "failed to set http handler: %v", err)
}
if err := meterhandler.RegisterMeterServiceServer(intsvc, meterhandler.NewHandler()); err != nil {
log.Fatal(ctx, "failed to set http handler: %v", err)
}
if err := intsvc.Start(); err != nil {
log.Fatal(ctx, "failed to run http srv: %v", err)
}
cw, err := vc.Watch(ctx, config.WatchCoalesce(true), config.WatchInterval(1*time.Second, 5*time.Second))
if err != nil {
log.Fatal(ctx, "failed to watch config: %v", err)
}
defer func() {
if err := cw.Stop(); err != nil {
log.Error(ctx, err.Error())
}
}()
go func() {
for {
changes, err := cw.Next()
if err != nil {
log.Error(ctx, "failed to get config update: %v", err)
}
for k, v := range changes {
if err = rutil.SetFieldByPath(cfg, v, k); err != nil {
log.Error(ctx, "failed to set config update: %v", err)
break
}
}
if err == nil {
for k := range changes {
switch k {
case "Server.LoggerLevel":
if lvl, ok := changes[k].(string); ok {
log.Info(ctx, "logger level changed to %s", lvl)
log.Level(logger.ParseLevel(lvl))
}
}
}
}
}
}()
go func() {
worker.Run(ctx, log, store, time.Duration(cfg.App.CheckInterval))
}()
if err = svc.Run(); err != nil {
log.Fatal(ctx, "failed to run svc: %v", err)
}
}