feat(desktop): 支持桌面端余额账单配置
This commit is contained in:
parent
e8df26da9b
commit
bffd4ecb98
@ -22,6 +22,7 @@ type Config struct {
|
|||||||
ServerMainBaseURL string
|
ServerMainBaseURL string
|
||||||
ServerMainInternalToken string
|
ServerMainInternalToken string
|
||||||
PublicBaseURL string
|
PublicBaseURL string
|
||||||
|
WebBaseURL string
|
||||||
LocalGeneratedStorageDir string
|
LocalGeneratedStorageDir string
|
||||||
LocalUploadedStorageDir string
|
LocalUploadedStorageDir string
|
||||||
LocalTempAssetTTLHours int
|
LocalTempAssetTTLHours int
|
||||||
@ -49,6 +50,7 @@ func Load() Config {
|
|||||||
),
|
),
|
||||||
ServerMainInternalToken: env("SERVER_MAIN_INTERNAL_TOKEN", ""),
|
ServerMainInternalToken: env("SERVER_MAIN_INTERNAL_TOKEN", ""),
|
||||||
PublicBaseURL: strings.TrimRight(env("AI_GATEWAY_PUBLIC_BASE_URL", env("PUBLIC_BASE_URL", "")), "/"),
|
PublicBaseURL: strings.TrimRight(env("AI_GATEWAY_PUBLIC_BASE_URL", env("PUBLIC_BASE_URL", "")), "/"),
|
||||||
|
WebBaseURL: strings.TrimRight(env("AI_GATEWAY_WEB_BASE_URL", env("GATEWAY_WEB_BASE_URL", env("PUBLIC_WEB_BASE_URL", ""))), "/"),
|
||||||
LocalGeneratedStorageDir: env("AI_GATEWAY_GENERATED_STORAGE_DIR", env("LOCAL_GENERATED_STORAGE_DIR", env("AI_GATEWAY_STATIC_STORAGE_DIR", DefaultLocalGeneratedStorageDir))),
|
LocalGeneratedStorageDir: env("AI_GATEWAY_GENERATED_STORAGE_DIR", env("LOCAL_GENERATED_STORAGE_DIR", env("AI_GATEWAY_STATIC_STORAGE_DIR", DefaultLocalGeneratedStorageDir))),
|
||||||
LocalUploadedStorageDir: env("AI_GATEWAY_UPLOADED_STORAGE_DIR", env("LOCAL_UPLOADED_STORAGE_DIR", DefaultLocalUploadedStorageDir)),
|
LocalUploadedStorageDir: env("AI_GATEWAY_UPLOADED_STORAGE_DIR", env("LOCAL_UPLOADED_STORAGE_DIR", DefaultLocalUploadedStorageDir)),
|
||||||
LocalTempAssetTTLHours: envInt("AI_GATEWAY_LOCAL_TEMP_ASSET_TTL_HOURS", 24),
|
LocalTempAssetTTLHours: envInt("AI_GATEWAY_LOCAL_TEMP_ASSET_TTL_HOURS", 24),
|
||||||
|
|||||||
@ -773,10 +773,20 @@ WHERE reference_type = 'gateway_task'
|
|||||||
Balance float64 `json:"balance"`
|
Balance float64 `json:"balance"`
|
||||||
} `json:"primaryAccount"`
|
} `json:"primaryAccount"`
|
||||||
}
|
}
|
||||||
doJSON(t, server.URL, http.MethodGet, "/api/workspace/wallet", loginResponse.AccessToken, nil, http.StatusOK, &walletSummary)
|
doJSON(t, server.URL, http.MethodGet, "/api/workspace/wallet?currency=resource", loginResponse.AccessToken, nil, http.StatusOK, &walletSummary)
|
||||||
if walletSummary.PrimaryAccount.Currency != "resource" || !floatNear(walletSummary.PrimaryAccount.Balance, walletBalanceAfter) || len(walletSummary.Accounts) == 0 {
|
if walletSummary.PrimaryAccount.Currency != "resource" || !floatNear(walletSummary.PrimaryAccount.Balance, walletBalanceAfter) || len(walletSummary.Accounts) == 0 {
|
||||||
t.Fatalf("workspace wallet should expose current resource balance, got %+v want balance=%f", walletSummary, walletBalanceAfter)
|
t.Fatalf("workspace wallet should expose current resource balance, got %+v want balance=%f", walletSummary, walletBalanceAfter)
|
||||||
}
|
}
|
||||||
|
var desktopConfig struct {
|
||||||
|
Billing struct {
|
||||||
|
GatewayBaseURL string `json:"gatewayBaseUrl"`
|
||||||
|
GatewayBillingPath string `json:"gatewayBillingPath"`
|
||||||
|
} `json:"billing"`
|
||||||
|
}
|
||||||
|
doJSON(t, server.URL, http.MethodGet, "/api/workspace/desktop-config", loginResponse.AccessToken, nil, http.StatusOK, &desktopConfig)
|
||||||
|
if desktopConfig.Billing.GatewayBillingPath != "/workspace/billing" || desktopConfig.Billing.GatewayBaseURL == "" {
|
||||||
|
t.Fatalf("desktop config should expose gateway billing route, got %+v", desktopConfig)
|
||||||
|
}
|
||||||
var walletTransactions struct {
|
var walletTransactions struct {
|
||||||
Items []struct {
|
Items []struct {
|
||||||
TransactionType string `json:"transactionType"`
|
TransactionType string `json:"transactionType"`
|
||||||
|
|||||||
55
apps/api/internal/httpapi/desktop_config_handlers.go
Normal file
55
apps/api/internal/httpapi/desktop_config_handlers.go
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
package httpapi
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type desktopBillingConfigResponse struct {
|
||||||
|
GatewayBaseURL string `json:"gatewayBaseUrl,omitempty"`
|
||||||
|
GatewayBillingPath string `json:"gatewayBillingPath"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type desktopConfigResponse struct {
|
||||||
|
Billing desktopBillingConfigResponse `json:"billing"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// getDesktopConfig godoc
|
||||||
|
// @Summary 获取桌面端配置
|
||||||
|
// @Description 返回桌面端需要的 Gateway Web 账单入口配置。
|
||||||
|
// @Tags workspace
|
||||||
|
// @Produce json
|
||||||
|
// @Security BearerAuth
|
||||||
|
// @Success 200 {object} desktopConfigResponse
|
||||||
|
// @Failure 401 {object} ErrorEnvelope
|
||||||
|
// @Router /api/workspace/desktop-config [get]
|
||||||
|
func (s *Server) getDesktopConfig(w http.ResponseWriter, r *http.Request) {
|
||||||
|
writeJSON(w, http.StatusOK, desktopConfigResponse{
|
||||||
|
Billing: desktopBillingConfigResponse{
|
||||||
|
GatewayBaseURL: firstNonEmpty(
|
||||||
|
strings.TrimRight(strings.TrimSpace(s.cfg.WebBaseURL), "/"),
|
||||||
|
strings.TrimRight(strings.TrimSpace(s.cfg.PublicBaseURL), "/"),
|
||||||
|
requestOrigin(r),
|
||||||
|
),
|
||||||
|
GatewayBillingPath: "/workspace/billing",
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func requestOrigin(r *http.Request) string {
|
||||||
|
host := strings.TrimSpace(r.Host)
|
||||||
|
if host == "" {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
proto := strings.TrimSpace(r.Header.Get("X-Forwarded-Proto"))
|
||||||
|
if proto == "" {
|
||||||
|
proto = "http"
|
||||||
|
}
|
||||||
|
if comma := strings.Index(proto, ","); comma >= 0 {
|
||||||
|
proto = strings.TrimSpace(proto[:comma])
|
||||||
|
}
|
||||||
|
if proto == "" {
|
||||||
|
proto = "http"
|
||||||
|
}
|
||||||
|
return proto + "://" + host
|
||||||
|
}
|
||||||
@ -88,6 +88,7 @@ func NewServerWithContext(ctx context.Context, cfg config.Config, db *store.Stor
|
|||||||
mux.Handle("PATCH /api/v1/api-keys/{apiKeyID}/disable", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.disableAPIKey)))
|
mux.Handle("PATCH /api/v1/api-keys/{apiKeyID}/disable", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.disableAPIKey)))
|
||||||
mux.Handle("DELETE /api/v1/api-keys/{apiKeyID}", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.deleteAPIKey)))
|
mux.Handle("DELETE /api/v1/api-keys/{apiKeyID}", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.deleteAPIKey)))
|
||||||
mux.Handle("GET /api/playground/api-keys", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listPlayableAPIKeys)))
|
mux.Handle("GET /api/playground/api-keys", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listPlayableAPIKeys)))
|
||||||
|
mux.Handle("GET /api/workspace/desktop-config", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.getDesktopConfig)))
|
||||||
mux.Handle("GET /api/workspace/user-groups", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listCurrentUserGroups)))
|
mux.Handle("GET /api/workspace/user-groups", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listCurrentUserGroups)))
|
||||||
mux.Handle("GET /api/workspace/wallet", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.getWallet)))
|
mux.Handle("GET /api/workspace/wallet", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.getWallet)))
|
||||||
mux.Handle("GET /api/workspace/wallet/transactions", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listWalletTransactions)))
|
mux.Handle("GET /api/workspace/wallet/transactions", server.auth.Require(auth.PermissionBasic, http.HandlerFunc(server.listWalletTransactions)))
|
||||||
|
|||||||
@ -592,6 +592,13 @@ export interface WalletSummaryResponse {
|
|||||||
primaryAccount: GatewayWalletAccount;
|
primaryAccount: GatewayWalletAccount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface GatewayDesktopConfigResponse {
|
||||||
|
billing: {
|
||||||
|
gatewayBaseUrl?: string;
|
||||||
|
gatewayBillingPath: string;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface WalletAdjustmentResponse {
|
export interface WalletAdjustmentResponse {
|
||||||
account: GatewayWalletAccount;
|
account: GatewayWalletAccount;
|
||||||
before: GatewayWalletAccount;
|
before: GatewayWalletAccount;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user