mirror of
https://github.com/CJackHwang/ds2api.git
synced 2026-05-17 06:35:14 +08:00
refactor backend API structure
This commit is contained in:
@@ -15,14 +15,18 @@ import (
|
||||
"github.com/go-chi/chi/v5/middleware"
|
||||
|
||||
"ds2api/internal/account"
|
||||
"ds2api/internal/adapter/claude"
|
||||
"ds2api/internal/adapter/gemini"
|
||||
"ds2api/internal/adapter/openai"
|
||||
"ds2api/internal/admin"
|
||||
"ds2api/internal/auth"
|
||||
"ds2api/internal/chathistory"
|
||||
"ds2api/internal/config"
|
||||
"ds2api/internal/deepseek"
|
||||
dsclient "ds2api/internal/deepseek/client"
|
||||
"ds2api/internal/httpapi/admin"
|
||||
"ds2api/internal/httpapi/claude"
|
||||
"ds2api/internal/httpapi/gemini"
|
||||
"ds2api/internal/httpapi/openai/chat"
|
||||
"ds2api/internal/httpapi/openai/embeddings"
|
||||
"ds2api/internal/httpapi/openai/files"
|
||||
"ds2api/internal/httpapi/openai/responses"
|
||||
"ds2api/internal/httpapi/openai/shared"
|
||||
"ds2api/internal/webui"
|
||||
)
|
||||
|
||||
@@ -30,7 +34,7 @@ type App struct {
|
||||
Store *config.Store
|
||||
Pool *account.Pool
|
||||
Resolver *auth.Resolver
|
||||
DS *deepseek.Client
|
||||
DS *dsclient.Client
|
||||
Router http.Handler
|
||||
}
|
||||
|
||||
@@ -40,11 +44,11 @@ func NewApp() (*App, error) {
|
||||
return nil, fmt.Errorf("load config: %w", err)
|
||||
}
|
||||
pool := account.NewPool(store)
|
||||
var dsClient *deepseek.Client
|
||||
var dsClient *dsclient.Client
|
||||
resolver := auth.NewResolver(store, pool, func(ctx context.Context, acc config.Account) (string, error) {
|
||||
return dsClient.Login(ctx, acc)
|
||||
})
|
||||
dsClient = deepseek.NewClient(store, resolver)
|
||||
dsClient = dsclient.NewClient(store, resolver)
|
||||
if err := dsClient.PreloadPow(context.Background()); err != nil {
|
||||
config.Logger.Warn("[PoW] init failed", "error", err)
|
||||
} else {
|
||||
@@ -55,10 +59,14 @@ func NewApp() (*App, error) {
|
||||
config.Logger.Warn("[chat_history] unavailable", "path", chatHistoryStore.Path(), "error", err)
|
||||
}
|
||||
|
||||
openaiHandler := &openai.Handler{Store: store, Auth: resolver, DS: dsClient, ChatHistory: chatHistoryStore}
|
||||
claudeHandler := &claude.Handler{Store: store, Auth: resolver, DS: dsClient, OpenAI: openaiHandler}
|
||||
geminiHandler := &gemini.Handler{Store: store, Auth: resolver, DS: dsClient, OpenAI: openaiHandler}
|
||||
adminHandler := &admin.Handler{Store: store, Pool: pool, DS: dsClient, OpenAI: openaiHandler, ChatHistory: chatHistoryStore}
|
||||
modelsHandler := &shared.ModelsHandler{Store: store}
|
||||
chatHandler := &chat.Handler{Store: store, Auth: resolver, DS: dsClient, ChatHistory: chatHistoryStore}
|
||||
responsesHandler := &responses.Handler{Store: store, Auth: resolver, DS: dsClient, ChatHistory: chatHistoryStore}
|
||||
filesHandler := &files.Handler{Store: store, Auth: resolver, DS: dsClient, ChatHistory: chatHistoryStore}
|
||||
embeddingsHandler := &embeddings.Handler{Store: store, Auth: resolver, DS: dsClient, ChatHistory: chatHistoryStore}
|
||||
claudeHandler := &claude.Handler{Store: store, Auth: resolver, DS: dsClient, OpenAI: chatHandler}
|
||||
geminiHandler := &gemini.Handler{Store: store, Auth: resolver, DS: dsClient, OpenAI: chatHandler}
|
||||
adminHandler := &admin.Handler{Store: store, Pool: pool, DS: dsClient, OpenAI: chatHandler, ChatHistory: chatHistoryStore}
|
||||
webuiHandler := webui.NewHandler()
|
||||
|
||||
r := chi.NewRouter()
|
||||
@@ -83,7 +91,13 @@ func NewApp() (*App, error) {
|
||||
r.Head("/healthz", healthzHandler)
|
||||
r.Get("/readyz", readyzHandler)
|
||||
r.Head("/readyz", readyzHandler)
|
||||
openai.RegisterRoutes(r, openaiHandler)
|
||||
r.Get("/v1/models", modelsHandler.ListModels)
|
||||
r.Get("/v1/models/{model_id}", modelsHandler.GetModel)
|
||||
r.Post("/v1/chat/completions", chatHandler.ChatCompletions)
|
||||
r.Post("/v1/responses", responsesHandler.Responses)
|
||||
r.Get("/v1/responses/{response_id}", responsesHandler.GetResponseByID)
|
||||
r.Post("/v1/files", filesHandler.UploadFile)
|
||||
r.Post("/v1/embeddings", embeddingsHandler.Embeddings)
|
||||
claude.RegisterRoutes(r, claudeHandler)
|
||||
gemini.RegisterRoutes(r, geminiHandler)
|
||||
r.Route("/admin", func(ar chi.Router) {
|
||||
|
||||
99
internal/server/router_routes_test.go
Normal file
99
internal/server/router_routes_test.go
Normal file
@@ -0,0 +1,99 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/go-chi/chi/v5"
|
||||
)
|
||||
|
||||
func TestAPIRoutesRemainRegistered(t *testing.T) {
|
||||
t.Setenv("DS2API_CONFIG_JSON", `{"keys":["k1"],"accounts":[{"email":"u@example.com","password":"p"}]}`)
|
||||
t.Setenv("DS2API_ENV_WRITEBACK", "0")
|
||||
|
||||
app, err := NewApp()
|
||||
if err != nil {
|
||||
t.Fatalf("NewApp() error: %v", err)
|
||||
}
|
||||
routes, ok := app.Router.(chi.Routes)
|
||||
if !ok {
|
||||
t.Fatalf("app router does not expose chi routes: %T", app.Router)
|
||||
}
|
||||
|
||||
got := map[string]bool{}
|
||||
if err := chi.Walk(routes, func(method string, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
|
||||
got[fmt.Sprintf("%s %s", method, route)] = true
|
||||
return nil
|
||||
}); err != nil {
|
||||
t.Fatalf("walk routes: %v", err)
|
||||
}
|
||||
|
||||
for _, want := range []string{
|
||||
"GET /v1/models",
|
||||
"GET /v1/models/{model_id}",
|
||||
"POST /v1/chat/completions",
|
||||
"POST /v1/responses",
|
||||
"GET /v1/responses/{response_id}",
|
||||
"POST /v1/files",
|
||||
"POST /v1/embeddings",
|
||||
"GET /anthropic/v1/models",
|
||||
"POST /anthropic/v1/messages",
|
||||
"POST /anthropic/v1/messages/count_tokens",
|
||||
"POST /v1/messages",
|
||||
"POST /messages",
|
||||
"POST /v1/messages/count_tokens",
|
||||
"POST /messages/count_tokens",
|
||||
"POST /v1beta/models/{model}:generateContent",
|
||||
"POST /v1beta/models/{model}:streamGenerateContent",
|
||||
"POST /v1/models/{model}:generateContent",
|
||||
"POST /v1/models/{model}:streamGenerateContent",
|
||||
"POST /admin/login",
|
||||
"GET /admin/verify",
|
||||
"GET /admin/config",
|
||||
"POST /admin/config",
|
||||
"GET /admin/settings",
|
||||
"PUT /admin/settings",
|
||||
"POST /admin/settings/password",
|
||||
"POST /admin/config/import",
|
||||
"GET /admin/config/export",
|
||||
"POST /admin/keys",
|
||||
"PUT /admin/keys/{key}",
|
||||
"DELETE /admin/keys/{key}",
|
||||
"GET /admin/proxies",
|
||||
"POST /admin/proxies",
|
||||
"PUT /admin/proxies/{proxyID}",
|
||||
"DELETE /admin/proxies/{proxyID}",
|
||||
"POST /admin/proxies/test",
|
||||
"GET /admin/accounts",
|
||||
"POST /admin/accounts",
|
||||
"PUT /admin/accounts/{identifier}",
|
||||
"DELETE /admin/accounts/{identifier}",
|
||||
"PUT /admin/accounts/{identifier}/proxy",
|
||||
"GET /admin/queue/status",
|
||||
"POST /admin/accounts/test",
|
||||
"POST /admin/accounts/test-all",
|
||||
"POST /admin/accounts/sessions/delete-all",
|
||||
"POST /admin/import",
|
||||
"POST /admin/test",
|
||||
"POST /admin/dev/raw-samples/capture",
|
||||
"GET /admin/dev/raw-samples/query",
|
||||
"POST /admin/dev/raw-samples/save",
|
||||
"POST /admin/vercel/sync",
|
||||
"GET /admin/vercel/status",
|
||||
"POST /admin/vercel/status",
|
||||
"GET /admin/export",
|
||||
"GET /admin/dev/captures",
|
||||
"DELETE /admin/dev/captures",
|
||||
"GET /admin/chat-history",
|
||||
"GET /admin/chat-history/{id}",
|
||||
"DELETE /admin/chat-history",
|
||||
"DELETE /admin/chat-history/{id}",
|
||||
"PUT /admin/chat-history/settings",
|
||||
"GET /admin/version",
|
||||
} {
|
||||
if !got[want] {
|
||||
t.Fatalf("expected route %s to be registered", want)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user