241 lines
7.2 KiB
Go
241 lines
7.2 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"crypto/tls"
|
|
"embed"
|
|
"io/fs"
|
|
"net/http"
|
|
"time"
|
|
slog "go.unistack.org/micro/v4/logger/slog"
|
|
appconfig "git.unistack.org/unistack-org/pkgdash/internal/config"
|
|
"git.unistack.org/unistack-org/pkgdash/internal/database"
|
|
"git.unistack.org/unistack-org/pkgdash/internal/handler"
|
|
"git.unistack.org/unistack-org/pkgdash/internal/storage"
|
|
_ "git.unistack.org/unistack-org/pkgdash/internal/storage/sqlite"
|
|
"git.unistack.org/unistack-org/pkgdash/internal/worker"
|
|
pb "git.unistack.org/unistack-org/pkgdash/proto"
|
|
jsoncodec "go.unistack.org/micro-codec-json/v4"
|
|
jsonpbcodec "go.unistack.org/micro-codec-jsonpb/v4"
|
|
yamlcodec "go.unistack.org/micro-codec-yaml/v4"
|
|
envconfig "go.unistack.org/micro-config-env/v4"
|
|
fileconfig "go.unistack.org/micro-config-file/v4"
|
|
vaultconfig "go.unistack.org/micro-config-vault/v4"
|
|
victoriameter "go.unistack.org/micro-meter-victoriametrics/v4"
|
|
httpsrv "go.unistack.org/micro-server-http/v4"
|
|
healthhandler "go.unistack.org/micro-server-http/v4/handler/health"
|
|
meterhandler "go.unistack.org/micro-server-http/v4/handler/meter"
|
|
spahandler "go.unistack.org/micro-server-http/v4/handler/spa"
|
|
swaggerui "go.unistack.org/micro-server-http/v4/handler/swagger-ui"
|
|
"go.unistack.org/micro/v4"
|
|
"go.unistack.org/micro/v4/config"
|
|
"go.unistack.org/micro/v4/logger"
|
|
"go.unistack.org/micro/v4/meter"
|
|
"go.unistack.org/micro/v4/options"
|
|
"go.unistack.org/micro/v4/server"
|
|
rutil "go.unistack.org/micro/v4/util/reflect"
|
|
)
|
|
|
|
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()
|
|
|
|
logger.DefaultLogger = slog.NewLogger(logger.WithLevel(logger.DebugLevel))
|
|
if err := logger.DefaultLogger.Init(); err != nil {
|
|
logger.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
|
|
options.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
|
|
options.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 {
|
|
logger.Fatal(ctx, "failed to load config: %v", err)
|
|
}
|
|
|
|
if err := config.Validate(ctx, cfg); err != nil {
|
|
logger.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),
|
|
options.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 {
|
|
logger.Fatal(ctx, "failed to init service: %v", err)
|
|
}
|
|
|
|
assetsUI, err := fs.Sub(assets, "assets/ui")
|
|
if err != nil {
|
|
logger.Fatal(ctx, "failed to get assets: %v", err)
|
|
}
|
|
|
|
if err := svc.Server("http").Init(
|
|
options.Address(cfg.Server.Addr),
|
|
options.Name(cfg.Server.Name),
|
|
server.Version(cfg.Server.Version),
|
|
options.Codecs("application/json", jsonpbcodec.NewCodec()),
|
|
options.Address(cfg.Server.Addr),
|
|
options.Context(ctx),
|
|
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 {
|
|
logger.Fatal(ctx, "failed to init service: %v", err)
|
|
}
|
|
|
|
if err := database.ParseDSN(cfg.Database); err != nil {
|
|
logger.Fatal(ctx, "failed to init database: %v", err)
|
|
}
|
|
|
|
db, err := database.Connect(ctx, cfg.Database, logger.DefaultLogger)
|
|
if err != nil {
|
|
logger.Fatal(ctx, "failed to connect database: %v", err)
|
|
}
|
|
|
|
store, err := storage.NewStorage(cfg.Database.Type, db)
|
|
if err != nil {
|
|
logger.Fatal(ctx, "failed to init storage: %v", err)
|
|
}
|
|
|
|
h, err := handler.NewHandler(store)
|
|
if err != nil {
|
|
logger.Fatal(ctx, "failed to create handler: %v", err)
|
|
}
|
|
|
|
log := logger.NewLogger(
|
|
logger.WithLevel(logger.ParseLevel(cfg.Server.LoggerLevel)),
|
|
logger.WithCallerSkipCount(3),
|
|
)
|
|
if err := svc.Init(micro.Logger(log)); err != nil {
|
|
logger.Fatal(ctx, "failed to init service: %v", err)
|
|
}
|
|
|
|
if err := pb.RegisterPkgdashServiceServer(svc.Server("http"), h); err != nil {
|
|
logger.Fatal(ctx, "failed to register handler: %v", err)
|
|
}
|
|
|
|
intsvc := httpsrv.NewServer(
|
|
options.Codecs("application/json", jsoncodec.NewCodec()),
|
|
options.Address(cfg.Meter.Addr),
|
|
options.Context(ctx),
|
|
)
|
|
|
|
if err := intsvc.Init(); err != nil {
|
|
logger.Fatal(ctx, "failed to init http srv: %v", err)
|
|
}
|
|
|
|
if err := healthhandler.RegisterHealthServiceServer(intsvc, healthhandler.NewHandler()); err != nil {
|
|
logger.Fatal(ctx, "failed to set http handler: %v", err)
|
|
}
|
|
|
|
if err := meterhandler.RegisterMeterServiceServer(intsvc, meterhandler.NewHandler()); err != nil {
|
|
logger.Fatal(ctx, "failed to set http handler: %v", err)
|
|
}
|
|
|
|
if err := intsvc.Start(); err != nil {
|
|
logger.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 {
|
|
logger.Fatal(ctx, "failed to watch config: %v", err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := cw.Stop(); err != nil {
|
|
logger.Error(ctx, err.Error())
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
for {
|
|
changes, err := cw.Next()
|
|
if err != nil {
|
|
logger.Error(ctx, "failed to get config update: %v", err)
|
|
}
|
|
for k, v := range changes {
|
|
if err = rutil.SetFieldByPath(cfg, v, k); err != nil {
|
|
logger.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 {
|
|
logger.Info(ctx, "logger level changed to %s", lvl)
|
|
logger.DefaultLogger.Level(logger.ParseLevel(lvl))
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}()
|
|
|
|
go func() {
|
|
worker.Run(ctx, store, time.Duration(cfg.App.CheckInterval))
|
|
}()
|
|
|
|
if err = svc.Run(); err != nil {
|
|
logger.Fatal(ctx, "failed to run svc: %v", err)
|
|
}
|
|
}
|