This commit is contained in:
yusing
2026-02-16 08:59:01 +08:00
parent 15b9635ee1
commit e4e6f6b3e8
242 changed files with 3953 additions and 3502 deletions

View File

@@ -9,7 +9,6 @@ import (
"fmt"
"io/fs"
"iter"
"net/http"
"os"
"strconv"
"strings"
@@ -18,14 +17,21 @@ import (
"github.com/goccy/go-yaml"
"github.com/puzpuzpuz/xsync/v4"
"github.com/rs/zerolog"
"github.com/yusing/godoxy/internal/acl"
"github.com/rs/zerolog/log"
acl "github.com/yusing/godoxy/internal/acl/types"
"github.com/yusing/godoxy/internal/agentpool"
"github.com/yusing/godoxy/internal/api"
"github.com/yusing/godoxy/internal/autocert"
autocertctx "github.com/yusing/godoxy/internal/autocert/types"
"github.com/yusing/godoxy/internal/common"
config "github.com/yusing/godoxy/internal/config/types"
"github.com/yusing/godoxy/internal/entrypoint"
entrypointctx "github.com/yusing/godoxy/internal/entrypoint/types"
homepage "github.com/yusing/godoxy/internal/homepage/types"
"github.com/yusing/godoxy/internal/logging"
"github.com/yusing/godoxy/internal/maxmind"
"github.com/yusing/godoxy/internal/metrics/systeminfo"
"github.com/yusing/godoxy/internal/metrics/uptime"
"github.com/yusing/godoxy/internal/notif"
route "github.com/yusing/godoxy/internal/route/provider"
"github.com/yusing/godoxy/internal/serialization"
@@ -40,7 +46,7 @@ type state struct {
providers *xsync.Map[string, types.RouteProvider]
autocertProvider *autocert.Provider
entrypoint entrypoint.Entrypoint
entrypoint *entrypoint.Entrypoint
task *task.Task
@@ -50,14 +56,25 @@ type state struct {
tmpLog zerolog.Logger
}
type CriticalError struct {
err error
}
func (e CriticalError) Error() string {
return e.err.Error()
}
func (e CriticalError) Unwrap() error {
return e.err
}
func NewState() config.State {
tmpLogBuf := bytes.NewBuffer(make([]byte, 0, 4096))
return &state{
providers: xsync.NewMap[string, types.RouteProvider](),
entrypoint: entrypoint.NewEntrypoint(),
task: task.RootTask("config", false),
tmpLogBuf: tmpLogBuf,
tmpLog: logging.NewLoggerWithFixedLevel(zerolog.InfoLevel, tmpLogBuf),
providers: xsync.NewMap[string, types.RouteProvider](),
task: task.RootTask("config", false),
tmpLogBuf: tmpLogBuf,
tmpLog: logging.NewLoggerWithFixedLevel(zerolog.InfoLevel, tmpLogBuf),
}
}
@@ -73,13 +90,7 @@ func SetState(state config.State) {
cfg := state.Value()
config.ActiveState.Store(state)
entrypoint.ActiveConfig.Store(&cfg.Entrypoint)
homepage.ActiveConfig.Store(&cfg.Homepage)
if autocertProvider := state.AutoCertProvider(); autocertProvider != nil {
autocert.ActiveProvider.Store(autocertProvider.(*autocert.Provider))
} else {
autocert.ActiveProvider.Store(nil)
}
}
func HasState() bool {
@@ -96,7 +107,7 @@ func (state *state) InitFromFile(filename string) error {
if errors.Is(err, fs.ErrNotExist) {
state.Config = config.DefaultConfig()
} else {
return err
return CriticalError{err}
}
}
return state.Init(data)
@@ -105,7 +116,7 @@ func (state *state) InitFromFile(filename string) error {
func (state *state) Init(data []byte) error {
err := serialization.UnmarshalValidate(data, &state.Config, yaml.Unmarshal)
if err != nil {
return err
return CriticalError{err}
}
g := gperr.NewGroup("config load error")
@@ -117,7 +128,9 @@ func (state *state) Init(data []byte) error {
// these won't benefit from running on goroutines
errs.Add(state.initNotification())
errs.Add(state.initACL())
errs.Add(state.initEntrypoint())
if err := state.initEntrypoint(); err != nil {
errs.Add(CriticalError{err})
}
errs.Add(state.loadRouteProviders())
return errs.Error()
}
@@ -134,8 +147,8 @@ func (state *state) Value() *config.Config {
return &state.Config
}
func (state *state) EntrypointHandler() http.Handler {
return &state.entrypoint
func (state *state) Entrypoint() entrypointctx.Entrypoint {
return state.entrypoint
}
func (state *state) ShortLinkMatcher() config.ShortLinkMatcher {
@@ -186,10 +199,39 @@ func (state *state) NumProviders() int {
}
func (state *state) FlushTmpLog() {
state.tmpLogBuf.WriteTo(os.Stdout)
_, _ = state.tmpLogBuf.WriteTo(os.Stdout)
state.tmpLogBuf.Reset()
}
func (state *state) StartAPIServers() {
// API Handler needs to start after auth is initialized.
_, err := server.StartServer(state.task.Subtask("api_server", false), server.Options{
Name: "api",
HTTPAddr: common.APIHTTPAddr,
Handler: api.NewHandler(true),
})
if err != nil {
log.Err(err).Msg("failed to start API server")
}
// Local API Handler is used for unauthenticated access.
if common.LocalAPIHTTPAddr != "" {
_, err := server.StartServer(state.task.Subtask("local_api_server", false), server.Options{
Name: "local_api",
HTTPAddr: common.LocalAPIHTTPAddr,
Handler: api.NewHandler(false),
})
if err != nil {
log.Err(err).Msg("failed to start local API server")
}
}
}
func (state *state) StartMetrics() {
systeminfo.Poller.Start(state.task)
uptime.Poller.Start(state.task)
}
// initACL initializes the ACL.
func (state *state) initACL() error {
if !state.ACL.Valid() {
@@ -199,7 +241,7 @@ func (state *state) initACL() error {
if err != nil {
return err
}
state.task.SetValue(acl.ContextKey{}, state.ACL)
acl.SetCtx(state.task, state.ACL)
return nil
}
@@ -207,6 +249,7 @@ func (state *state) initEntrypoint() error {
epCfg := state.Config.Entrypoint
matchDomains := state.MatchDomains
state.entrypoint = entrypoint.NewEntrypoint(state.task, &epCfg)
state.entrypoint.SetFindRouteDomains(matchDomains)
state.entrypoint.SetNotFoundRules(epCfg.Rules.NotFound)
@@ -220,6 +263,8 @@ func (state *state) initEntrypoint() error {
}
}
entrypointctx.SetCtx(state.task, state.entrypoint)
errs := gperr.NewBuilder("entrypoint error")
errs.Add(state.entrypoint.SetMiddlewares(epCfg.Middlewares))
errs.Add(state.entrypoint.SetAccessLogger(state.task, epCfg.AccessLog))
@@ -296,6 +341,7 @@ func (state *state) initAutoCert() error {
p.PrintCertExpiriesAll()
state.autocertProvider = p
autocertctx.SetCtx(state.task, p)
return nil
}
@@ -309,7 +355,7 @@ func (state *state) initProxmox() error {
for _, cfg := range proxmoxCfg {
errs.Go(func() error {
if err := cfg.Init(state.task.Context()); err != nil {
return err.Subject(cfg.URL)
return gperr.PrependSubject(err, cfg.URL)
}
return nil
})
@@ -333,7 +379,7 @@ func (state *state) loadRouteProviders() error {
for _, a := range providers.Agents {
agentErrs.Go(func() error {
if err := a.Init(state.task.Context()); err != nil {
return gperr.PrependSubject(a.String(), err)
return gperr.PrependSubject(err, a.String())
}
agentpool.Add(a)
return nil
@@ -351,7 +397,7 @@ func (state *state) loadRouteProviders() error {
for _, filename := range providers.Files {
p, err := route.NewFileProvider(filename)
if err != nil {
errs.Add(gperr.PrependSubject(filename, err))
errs.Add(gperr.PrependSubject(err, filename))
return err
}
registerProvider(p)
@@ -376,7 +422,7 @@ func (state *state) loadRouteProviders() error {
for _, p := range state.providers.Range {
loadErrs.Go(func() error {
if err := p.LoadRoutes(); err != nil {
return err.Subject(p.String())
return gperr.PrependSubject(err, p.String())
}
resultsMu.Lock()
results.Addf("%-"+strconv.Itoa(lenLongestName)+"s %d routes", p.String(), p.NumRoutes())