mirror of
https://github.com/yusing/godoxy.git
synced 2025-05-20 12:42:34 +02:00

* feat: idle sleep for proxmox LXCs * refactor: replace deprecated docker api types * chore(api): remove debug task list endpoint * refactor: move servemux to gphttp/servemux; favicon.go to v1/favicon * refactor: introduce Pool interface, move agent_pool to agent module * refactor: simplify api code * feat: introduce debug api * refactor: remove net.URL and net.CIDR types, improved unmarshal handling * chore: update Makefile for debug build tag, update README * chore: add gperr.Unwrap method * feat: relative time and duration formatting * chore: add ROOT_DIR environment variable, refactor * migration: move homepage override and icon cache to $BASE_DIR/data, add migration code * fix: nil dereference on marshalling service health * fix: wait for route deletion * chore: enhance tasks debuggability * feat: stdout access logger and MultiWriter * fix(agent): remove agent properly on verify error * fix(metrics): disk exclusion logic and added corresponding tests * chore: update schema and prettify, fix package.json and Makefile * fix: I/O buffer not being shrunk before putting back to pool * feat: enhanced error handling module * chore: deps upgrade * feat: better value formatting and handling --------- Co-authored-by: yusing <yusing@6uo.me>
173 lines
4.1 KiB
Go
Executable file
173 lines
4.1 KiB
Go
Executable file
package main
|
|
|
|
import (
|
|
"encoding/json"
|
|
"log"
|
|
"os"
|
|
"sync"
|
|
|
|
"github.com/yusing/go-proxy/internal/api/v1/auth"
|
|
debugapi "github.com/yusing/go-proxy/internal/api/v1/debug"
|
|
"github.com/yusing/go-proxy/internal/api/v1/query"
|
|
"github.com/yusing/go-proxy/internal/common"
|
|
"github.com/yusing/go-proxy/internal/config"
|
|
"github.com/yusing/go-proxy/internal/gperr"
|
|
"github.com/yusing/go-proxy/internal/homepage"
|
|
"github.com/yusing/go-proxy/internal/logging"
|
|
"github.com/yusing/go-proxy/internal/logging/memlogger"
|
|
"github.com/yusing/go-proxy/internal/metrics/systeminfo"
|
|
"github.com/yusing/go-proxy/internal/metrics/uptime"
|
|
"github.com/yusing/go-proxy/internal/net/gphttp/middleware"
|
|
"github.com/yusing/go-proxy/internal/route/routes/routequery"
|
|
"github.com/yusing/go-proxy/internal/task"
|
|
"github.com/yusing/go-proxy/migrations"
|
|
"github.com/yusing/go-proxy/pkg"
|
|
)
|
|
|
|
var rawLogger = log.New(os.Stdout, "", 0)
|
|
|
|
func parallel(fns ...func()) {
|
|
var wg sync.WaitGroup
|
|
for _, fn := range fns {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
fn()
|
|
}()
|
|
}
|
|
wg.Wait()
|
|
}
|
|
|
|
func main() {
|
|
initProfiling()
|
|
if err := migrations.RunMigrations(); err != nil {
|
|
gperr.LogFatal("migration error", err)
|
|
}
|
|
args := pkg.GetArgs(common.MainServerCommandValidator{})
|
|
|
|
switch args.Command {
|
|
case common.CommandReload:
|
|
if err := query.ReloadServer(); err != nil {
|
|
gperr.LogFatal("server reload error", err)
|
|
}
|
|
rawLogger.Println("ok")
|
|
return
|
|
case common.CommandListIcons:
|
|
icons, err := homepage.ListAvailableIcons()
|
|
if err != nil {
|
|
rawLogger.Fatal(err)
|
|
}
|
|
printJSON(icons)
|
|
return
|
|
case common.CommandListRoutes:
|
|
routes, err := query.ListRoutes()
|
|
if err != nil {
|
|
log.Printf("failed to connect to api server: %s", err)
|
|
log.Printf("falling back to config file")
|
|
} else {
|
|
printJSON(routes)
|
|
return
|
|
}
|
|
case common.CommandDebugListMTrace:
|
|
trace, err := query.ListMiddlewareTraces()
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
printJSON(trace)
|
|
return
|
|
}
|
|
|
|
if args.Command == common.CommandStart {
|
|
logging.InitLogger(os.Stderr, memlogger.GetMemLogger())
|
|
logging.Info().Msgf("GoDoxy version %s", pkg.GetVersion())
|
|
logging.Trace().Msg("trace enabled")
|
|
parallel(
|
|
homepage.InitIconListCache,
|
|
homepage.InitIconCache,
|
|
homepage.InitOverridesConfig,
|
|
systeminfo.Poller.Start,
|
|
)
|
|
|
|
if common.APIJWTSecret == nil {
|
|
logging.Warn().Msg("API_JWT_SECRET is not set, using random key")
|
|
common.APIJWTSecret = common.RandomJWTKey()
|
|
}
|
|
} else {
|
|
logging.DiscardLogger()
|
|
}
|
|
|
|
if args.Command == common.CommandValidate {
|
|
data, err := os.ReadFile(common.ConfigPath)
|
|
if err == nil {
|
|
err = config.Validate(data)
|
|
}
|
|
if err != nil {
|
|
log.Fatal("config error: ", err)
|
|
}
|
|
log.Print("config OK")
|
|
return
|
|
}
|
|
|
|
for _, dir := range common.RequiredDirectories {
|
|
prepareDirectory(dir)
|
|
}
|
|
|
|
middleware.LoadComposeFiles()
|
|
|
|
var cfg *config.Config
|
|
var err gperr.Error
|
|
if cfg, err = config.Load(); err != nil {
|
|
gperr.LogWarn("errors in config", err)
|
|
err = nil
|
|
}
|
|
|
|
switch args.Command {
|
|
case common.CommandListRoutes:
|
|
cfg.StartProxyProviders()
|
|
printJSON(routequery.RoutesByAlias())
|
|
return
|
|
case common.CommandListConfigs:
|
|
printJSON(cfg.Value())
|
|
return
|
|
case common.CommandDebugListEntries:
|
|
printJSON(cfg.DumpRoutes())
|
|
return
|
|
case common.CommandDebugListProviders:
|
|
printJSON(cfg.DumpRouteProviders())
|
|
return
|
|
}
|
|
|
|
cfg.Start(&config.StartServersOptions{
|
|
Proxy: true,
|
|
})
|
|
if err := auth.Initialize(); err != nil {
|
|
logging.Fatal().Err(err).Msg("failed to initialize authentication")
|
|
}
|
|
// API Handler needs to start after auth is initialized.
|
|
cfg.StartServers(&config.StartServersOptions{
|
|
API: true,
|
|
})
|
|
|
|
uptime.Poller.Start()
|
|
config.WatchChanges()
|
|
|
|
debugapi.StartServer(cfg)
|
|
|
|
task.WaitExit(cfg.Value().TimeoutShutdown)
|
|
}
|
|
|
|
func prepareDirectory(dir string) {
|
|
if _, err := os.Stat(dir); os.IsNotExist(err) {
|
|
if err = os.MkdirAll(dir, 0o755); err != nil {
|
|
logging.Fatal().Msgf("failed to create directory %s: %v", dir, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
func printJSON(obj any) {
|
|
j, err := json.MarshalIndent(obj, "", " ")
|
|
if err != nil {
|
|
logging.Fatal().Err(err).Send()
|
|
}
|
|
rawLogger.Print(string(j)) // raw output for convenience using "jq"
|
|
}
|