Go 实现 /api/v1/analysis 契约、Demo/Live Worker、DeerFlow SSE;含设计文档与冒烟脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
package main
|
||
|
||
import (
|
||
"database/sql"
|
||
"log"
|
||
"net/http"
|
||
"os"
|
||
"strconv"
|
||
"time"
|
||
|
||
_ "github.com/go-sql-driver/mysql"
|
||
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/api"
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/config"
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/deerflow"
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/repo"
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/service"
|
||
"git.gozeros.com/wsk/eightbf-analysis-go/internal/worker"
|
||
)
|
||
|
||
func main() {
|
||
cfg := config.Load()
|
||
log.Printf("eightbf-analysis-go starting port=%d mode=%s auth=%v demo=%v",
|
||
cfg.Port, cfg.AnalysisMode, cfg.AuthEnabled, cfg.DemoEnabled)
|
||
|
||
db, err := sql.Open("mysql", cfg.DBDSN)
|
||
if err != nil {
|
||
log.Fatalf("open db: %v", err)
|
||
}
|
||
db.SetMaxOpenConns(20)
|
||
db.SetMaxIdleConns(5)
|
||
db.SetConnMaxLifetime(time.Hour)
|
||
if err := db.Ping(); err != nil {
|
||
log.Fatalf("ping db: %v(请检查 EBA_DB_DSN / EBA_DB_*)", err)
|
||
}
|
||
|
||
jobRepo := repo.NewJobRepo(db)
|
||
if schema, err := os.ReadFile("schema.sql"); err != nil {
|
||
log.Printf("未找到 schema.sql,跳过自动建表: %v", err)
|
||
} else {
|
||
jobRepo.ApplySchema(string(schema))
|
||
}
|
||
|
||
access := &service.AccessService{AuthEnabled: cfg.AuthEnabled}
|
||
jobs := &service.JobService{Repo: jobRepo, Access: access}
|
||
h := &api.Handler{Jobs: jobs}
|
||
|
||
mux := http.NewServeMux()
|
||
h.Routes(mux)
|
||
|
||
if cfg.AnalysisMode == "live" && !cfg.DemoEnabled {
|
||
auth := deerflow.NewAuth(cfg)
|
||
df := deerflow.NewClient(cfg, auth)
|
||
runner := &worker.LiveChainRunner{DF: df, Repo: jobRepo, Coord: cfg.DeerFlowCoordinator}
|
||
lw := &worker.LiveWorker{
|
||
Repo: jobRepo,
|
||
Runner: runner,
|
||
PollEvery: time.Duration(cfg.LivePollMS) * time.Millisecond,
|
||
}
|
||
lw.Start()
|
||
log.Printf("LiveWorker 已启动 poll=%dms", cfg.LivePollMS)
|
||
} else if cfg.DemoEnabled || cfg.AnalysisMode == "demo" {
|
||
dw := &service.DemoWorker{
|
||
Repo: jobRepo,
|
||
Every: time.Duration(cfg.DemoTickMS) * time.Millisecond,
|
||
}
|
||
dw.Start()
|
||
log.Printf("DemoWorker 已启动 tick=%dms", cfg.DemoTickMS)
|
||
} else {
|
||
log.Printf("警告:未启动任何 Worker(mode=%s demo=%v)", cfg.AnalysisMode, cfg.DemoEnabled)
|
||
}
|
||
|
||
addr := ":" + strconv.Itoa(cfg.Port)
|
||
log.Printf("监听 %s (健康检查 GET /api/v1/analysis/health)", addr)
|
||
if err := http.ListenAndServe(addr, api.WithCORS(mux)); err != nil {
|
||
log.Fatal(err)
|
||
}
|
||
}
|