Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,10 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/prism/proxy/internal/geodata"
|
||||
)
|
||||
|
||||
func applyGeoSettingsOnStart(dataDir string, settings map[string]string) {
|
||||
geodata.InitHomeDir(dataDir)
|
||||
geodata.ApplyMihomoURLs(geodata.ConfigFromSettings(settings))
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
"github.com/robfig/cron/v3"
|
||||
|
||||
"github.com/prism/proxy/internal/api"
|
||||
"github.com/prism/proxy/internal/cache"
|
||||
"github.com/prism/proxy/internal/configbuilder"
|
||||
"github.com/prism/proxy/internal/core"
|
||||
"github.com/prism/proxy/internal/geodata"
|
||||
"github.com/prism/proxy/internal/health"
|
||||
applog "github.com/prism/proxy/internal/log"
|
||||
"github.com/prism/proxy/internal/metrics"
|
||||
"github.com/prism/proxy/internal/mihomoapi"
|
||||
"github.com/prism/proxy/internal/service"
|
||||
"github.com/prism/proxy/internal/store"
|
||||
"github.com/prism/proxy/internal/subscription"
|
||||
"github.com/prism/proxy/internal/trafficlog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
dataDir := flag.String("data", "data", "data directory")
|
||||
flag.Parse()
|
||||
|
||||
log.Printf("Prism starting, data dir: %s", *dataDir)
|
||||
|
||||
if err := os.MkdirAll(*dataDir, 0o755); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
st, err := store.Open(*dataDir)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer st.Close()
|
||||
log.Printf("database ready")
|
||||
|
||||
if n, err := st.BackfillNodeCountries(context.Background()); err != nil {
|
||||
log.Printf("country backfill: %v", err)
|
||||
} else if n > 0 {
|
||||
log.Printf("country tags backfilled for %d nodes", n)
|
||||
}
|
||||
|
||||
core.InitDataDir(*dataDir)
|
||||
|
||||
c := cache.New()
|
||||
engine := core.NewEngine()
|
||||
builder := configbuilder.New(st, *dataDir)
|
||||
logCollector := applog.NewCollector(st, 5000, true)
|
||||
subSvc := subscription.NewService(st)
|
||||
|
||||
app := &service.App{
|
||||
Store: st,
|
||||
Cache: c,
|
||||
Engine: engine,
|
||||
Builder: builder,
|
||||
Subscription: subSvc,
|
||||
Logs: logCollector,
|
||||
DataDir: *dataDir,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
apiBase, secret, _ := app.MihomoAPIBase(ctx)
|
||||
hc := health.NewChecker(st, c, apiBase, secret)
|
||||
mc := metrics.NewCollector(st, c, apiBase, secret)
|
||||
trafficCollector := trafficlog.NewCollector(st, 5000)
|
||||
trafficCollector.Start(ctx, mihomoapi.New(apiBase, secret))
|
||||
|
||||
settings, _ := st.GetSettings(ctx)
|
||||
applyGeoSettingsOnStart(*dataDir, settings)
|
||||
if settings["geo_auto_download"] != "false" {
|
||||
go func() {
|
||||
log.Printf("checking geo data in background...")
|
||||
cfg := geodata.ConfigFromSettings(settings)
|
||||
if updated, err := geodata.Ensure(*dataDir, cfg, false); err != nil {
|
||||
log.Printf("geo data: %v", err)
|
||||
logCollector.Add("warn", "prism", "geo data: "+err.Error())
|
||||
} else if len(updated) > 0 {
|
||||
log.Printf("geo data downloaded: %v", updated)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
logCollector.Add("info", "prism", "Prism started")
|
||||
log.Printf("loading proxy engine...")
|
||||
|
||||
if err := app.ReloadEngine(ctx); err != nil {
|
||||
log.Printf("initial engine reload: %v (will retry after config)", err)
|
||||
} else {
|
||||
log.Printf("proxy engine ready")
|
||||
}
|
||||
go func() {
|
||||
warmCtx, cancel := context.WithTimeout(context.Background(), 8*time.Second)
|
||||
defer cancel()
|
||||
_, _ = mc.Refresh(warmCtx)
|
||||
app.RefreshEngineStatusAsync()
|
||||
}()
|
||||
|
||||
srv := api.NewServer(app, hc, mc, subSvc, logCollector, trafficCollector)
|
||||
router := srv.Router()
|
||||
|
||||
apiPort := settings["api_port"]
|
||||
if apiPort == "" {
|
||||
apiPort = "8080"
|
||||
}
|
||||
|
||||
httpServer := &http.Server{
|
||||
Addr: ":" + apiPort,
|
||||
Handler: router,
|
||||
}
|
||||
|
||||
cr := cron.New()
|
||||
_, _ = cr.AddFunc("@every 1m", func() {
|
||||
_, _ = mc.Refresh(context.Background())
|
||||
})
|
||||
_, _ = cr.AddFunc("@every 5m", func() {
|
||||
settings, _ := st.GetSettings(context.Background())
|
||||
sec, _ := strconv.Atoi(settings["health_interval_sec"])
|
||||
if sec <= 0 {
|
||||
sec = 300
|
||||
}
|
||||
// health checker runs on its own schedule approximation
|
||||
_ = hc.TestAll(context.Background(), nil)
|
||||
})
|
||||
_, _ = cr.AddFunc("@every 10m", func() {
|
||||
syncSubscriptions(app, subSvc)
|
||||
})
|
||||
cr.Start()
|
||||
|
||||
go func() {
|
||||
log.Printf("Prism API listening on :%s", apiPort)
|
||||
if err := httpServer.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}()
|
||||
|
||||
sig := make(chan os.Signal, 1)
|
||||
signal.Notify(sig, syscall.SIGINT, syscall.SIGTERM)
|
||||
<-sig
|
||||
|
||||
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
|
||||
defer cancel()
|
||||
_ = httpServer.Shutdown(shutdownCtx)
|
||||
cr.Stop()
|
||||
engine.Shutdown()
|
||||
}
|
||||
|
||||
func syncSubscriptions(app *service.App, subSvc *subscription.Service) {
|
||||
ctx := context.Background()
|
||||
settings, err := app.Store.GetSettings(ctx)
|
||||
if err != nil || settings["sub_sync_enabled"] != "true" {
|
||||
return
|
||||
}
|
||||
subs, err := app.Store.ListSubscriptions(ctx)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
for _, sub := range subs {
|
||||
if !sub.Enabled {
|
||||
continue
|
||||
}
|
||||
if sub.LastFetchAt != nil && now.Sub(*sub.LastFetchAt) < time.Duration(sub.IntervalSec)*time.Second {
|
||||
continue
|
||||
}
|
||||
if err := subSvc.FetchAndStore(ctx, sub.ID); err != nil {
|
||||
app.Logs.Add("error", "subscription", fmt.Sprintf("sync %s failed: %v", sub.Name, err))
|
||||
continue
|
||||
}
|
||||
}
|
||||
_ = app.ReloadEngine(ctx)
|
||||
}
|
||||
Reference in New Issue
Block a user