fix: node-tls-proxy not receiving traffic due to viper BindEnv bug
- Add explicit viper.BindEnv() for all gateway.node_tls_proxy.* keys to fix viper's AutomaticEnv+Unmarshal nested struct bug where env vars are silently ignored when config.yaml lacks the corresponding section - Sync proxy.js CLI_VERSION 2.1.84→2.1.87 and BUILD_TIME to match constants.go, eliminating API/telemetry version mismatch
This commit is contained in:
parent
53eaae61a3
commit
d3d885cf75
@ -1513,6 +1513,21 @@ func setDefaults() {
|
|||||||
viper.SetDefault("gateway.user_message_queue.cleanup_interval_seconds", 60)
|
viper.SetDefault("gateway.user_message_queue.cleanup_interval_seconds", 60)
|
||||||
|
|
||||||
viper.SetDefault("gateway.tls_fingerprint.enabled", true)
|
viper.SetDefault("gateway.tls_fingerprint.enabled", true)
|
||||||
|
|
||||||
|
// Node.js TLS Proxy 默认值
|
||||||
|
// 注意:必须显式 BindEnv,因为 viper.Unmarshal 对嵌套 struct 的 AutomaticEnv
|
||||||
|
// 支持有缺陷——仅 SetDefault 注册的 key 在 config.yaml 缺少对应 section 时,
|
||||||
|
// 环境变量不会被合并到 Unmarshal 结果中。
|
||||||
|
viper.SetDefault("gateway.node_tls_proxy.enabled", false)
|
||||||
|
viper.SetDefault("gateway.node_tls_proxy.listen_port", 3456)
|
||||||
|
viper.SetDefault("gateway.node_tls_proxy.listen_host", "127.0.0.1")
|
||||||
|
viper.SetDefault("gateway.node_tls_proxy.health_path", "/__health")
|
||||||
|
viper.SetDefault("gateway.node_tls_proxy.upstream_host", "api.anthropic.com")
|
||||||
|
_ = viper.BindEnv("gateway.node_tls_proxy.enabled", "GATEWAY_NODE_TLS_PROXY_ENABLED")
|
||||||
|
_ = viper.BindEnv("gateway.node_tls_proxy.listen_port", "GATEWAY_NODE_TLS_PROXY_LISTEN_PORT")
|
||||||
|
_ = viper.BindEnv("gateway.node_tls_proxy.listen_host", "GATEWAY_NODE_TLS_PROXY_LISTEN_HOST")
|
||||||
|
_ = viper.BindEnv("gateway.node_tls_proxy.health_path", "GATEWAY_NODE_TLS_PROXY_HEALTH_PATH")
|
||||||
|
_ = viper.BindEnv("gateway.node_tls_proxy.upstream_host", "GATEWAY_NODE_TLS_PROXY_UPSTREAM_HOST")
|
||||||
viper.SetDefault("concurrency.ping_interval", 10)
|
viper.SetDefault("concurrency.ping_interval", 10)
|
||||||
|
|
||||||
// Sora 直连配置
|
// Sora 直连配置
|
||||||
|
|||||||
138
backend/internal/handler/admin/debug_log_handler.go
Normal file
138
backend/internal/handler/admin/debug_log_handler.go
Normal file
@ -0,0 +1,138 @@
|
|||||||
|
package admin
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/Wei-Shaw/sub2api/internal/service"
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
// DebugLogHandler provides admin endpoints to control gateway debug logging.
|
||||||
|
type DebugLogHandler struct {
|
||||||
|
debugLogger *service.GatewayDebugLogger
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewDebugLogHandler(debugLogger *service.GatewayDebugLogger) *DebugLogHandler {
|
||||||
|
return &DebugLogHandler{debugLogger: debugLogger}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStatus returns whether debug logging is enabled.
|
||||||
|
func (h *DebugLogHandler) GetStatus(c *gin.Context) {
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"enabled": h.debugLogger.IsEnabled(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Enable turns on debug logging.
|
||||||
|
func (h *DebugLogHandler) Enable(c *gin.Context) {
|
||||||
|
h.debugLogger.Enable()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"enabled": true,
|
||||||
|
"message": "gateway debug logging enabled",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disable turns off debug logging.
|
||||||
|
func (h *DebugLogHandler) Disable(c *gin.Context) {
|
||||||
|
h.debugLogger.Disable()
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"enabled": false,
|
||||||
|
"message": "gateway debug logging disabled",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// ListLogs returns recent debug logs with pagination.
|
||||||
|
func (h *DebugLogHandler) ListLogs(c *gin.Context) {
|
||||||
|
db := h.debugLogger.DB()
|
||||||
|
if db == nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": "database not available"})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
limit := 50
|
||||||
|
if v := c.Query("limit"); v != "" {
|
||||||
|
if n, err := strconv.Atoi(v); err == nil && n > 0 && n <= 200 {
|
||||||
|
limit = n
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
accountID := c.Query("account_id")
|
||||||
|
eventType := c.Query("event_type")
|
||||||
|
|
||||||
|
query := `SELECT id, upstream_request_id, account_id, account_email, account_platform,
|
||||||
|
event_type, method, full_url, request_headers, request_body, request_size,
|
||||||
|
response_status, response_headers, response_body_preview, response_size,
|
||||||
|
model_requested, model_upstream, is_stream, duration_ms, tls_profile,
|
||||||
|
error_message, created_at
|
||||||
|
FROM gateway_debug_logs WHERE 1=1`
|
||||||
|
args := []interface{}{}
|
||||||
|
argIdx := 1
|
||||||
|
|
||||||
|
if accountID != "" {
|
||||||
|
query += " AND account_id = $" + strconv.Itoa(argIdx)
|
||||||
|
args = append(args, accountID)
|
||||||
|
argIdx++
|
||||||
|
}
|
||||||
|
if eventType != "" {
|
||||||
|
query += " AND event_type = $" + strconv.Itoa(argIdx)
|
||||||
|
args = append(args, eventType)
|
||||||
|
argIdx++
|
||||||
|
}
|
||||||
|
|
||||||
|
query += " ORDER BY created_at DESC LIMIT $" + strconv.Itoa(argIdx)
|
||||||
|
args = append(args, limit)
|
||||||
|
|
||||||
|
rows, err := db.QueryContext(c.Request.Context(), query, args...)
|
||||||
|
if err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer rows.Close()
|
||||||
|
|
||||||
|
type logRow struct {
|
||||||
|
ID int64 `json:"id"`
|
||||||
|
UpstreamRequestID *string `json:"upstream_request_id"`
|
||||||
|
AccountID int64 `json:"account_id"`
|
||||||
|
AccountEmail *string `json:"account_email"`
|
||||||
|
AccountPlatform *string `json:"account_platform"`
|
||||||
|
EventType string `json:"event_type"`
|
||||||
|
Method *string `json:"method"`
|
||||||
|
FullURL *string `json:"full_url"`
|
||||||
|
RequestHeaders *string `json:"request_headers"`
|
||||||
|
RequestBody *string `json:"request_body"`
|
||||||
|
RequestSize *int `json:"request_size"`
|
||||||
|
ResponseStatus *int `json:"response_status"`
|
||||||
|
ResponseHeaders *string `json:"response_headers"`
|
||||||
|
ResponseBodyPreview *string `json:"response_body_preview"`
|
||||||
|
ResponseSize *int `json:"response_size"`
|
||||||
|
ModelRequested *string `json:"model_requested"`
|
||||||
|
ModelUpstream *string `json:"model_upstream"`
|
||||||
|
IsStream bool `json:"is_stream"`
|
||||||
|
DurationMs *int `json:"duration_ms"`
|
||||||
|
TLSProfile *string `json:"tls_profile"`
|
||||||
|
ErrorMessage *string `json:"error_message"`
|
||||||
|
CreatedAt string `json:"created_at"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var results []logRow
|
||||||
|
for rows.Next() {
|
||||||
|
var r logRow
|
||||||
|
if err := rows.Scan(
|
||||||
|
&r.ID, &r.UpstreamRequestID, &r.AccountID, &r.AccountEmail, &r.AccountPlatform,
|
||||||
|
&r.EventType, &r.Method, &r.FullURL, &r.RequestHeaders, &r.RequestBody, &r.RequestSize,
|
||||||
|
&r.ResponseStatus, &r.ResponseHeaders, &r.ResponseBodyPreview, &r.ResponseSize,
|
||||||
|
&r.ModelRequested, &r.ModelUpstream, &r.IsStream, &r.DurationMs, &r.TLSProfile,
|
||||||
|
&r.ErrorMessage, &r.CreatedAt,
|
||||||
|
); err != nil {
|
||||||
|
c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
results = append(results, r)
|
||||||
|
}
|
||||||
|
|
||||||
|
c.JSON(http.StatusOK, gin.H{
|
||||||
|
"items": results,
|
||||||
|
"count": len(results),
|
||||||
|
})
|
||||||
|
}
|
||||||
255
backend/internal/service/gateway_debug_logger.go
Normal file
255
backend/internal/service/gateway_debug_logger.go
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package service
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"encoding/json"
|
||||||
|
"log/slog"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GatewayDebugLogEntry holds all fields for a single debug log row.
|
||||||
|
type GatewayDebugLogEntry struct {
|
||||||
|
UpstreamRequestID string
|
||||||
|
AccountID int64
|
||||||
|
AccountEmail string
|
||||||
|
AccountPlatform string
|
||||||
|
|
||||||
|
EventType string // "api_call", "oauth_refresh", "error"
|
||||||
|
|
||||||
|
Method string
|
||||||
|
FullURL string
|
||||||
|
RequestHeaders map[string]string
|
||||||
|
RequestBody []byte // raw bytes, stored as TEXT
|
||||||
|
RequestSize int
|
||||||
|
|
||||||
|
ResponseStatus int
|
||||||
|
ResponseHeaders map[string]string
|
||||||
|
ResponseBodyPreview string
|
||||||
|
ResponseSize int
|
||||||
|
|
||||||
|
ModelRequested string
|
||||||
|
ModelUpstream string
|
||||||
|
IsStream bool
|
||||||
|
DurationMs int
|
||||||
|
|
||||||
|
TLSProfile string
|
||||||
|
|
||||||
|
ErrorMessage string
|
||||||
|
}
|
||||||
|
|
||||||
|
// GatewayDebugLogger writes debug log entries to gateway_debug_logs.
|
||||||
|
type GatewayDebugLogger struct {
|
||||||
|
db *sql.DB
|
||||||
|
enabled atomic.Bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewGatewayDebugLogger creates a new debug logger (enabled by default).
|
||||||
|
func NewGatewayDebugLogger(db *sql.DB) *GatewayDebugLogger {
|
||||||
|
l := &GatewayDebugLogger{db: db}
|
||||||
|
l.enabled.Store(true)
|
||||||
|
return l
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GatewayDebugLogger) IsEnabled() bool {
|
||||||
|
return l != nil && l.enabled.Load()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DB returns the underlying database handle (for admin queries).
|
||||||
|
func (l *GatewayDebugLogger) DB() *sql.DB {
|
||||||
|
if l == nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return l.db
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GatewayDebugLogger) Enable() {
|
||||||
|
if l != nil {
|
||||||
|
l.enabled.Store(true)
|
||||||
|
slog.Info("gateway debug logging ENABLED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (l *GatewayDebugLogger) Disable() {
|
||||||
|
if l != nil {
|
||||||
|
l.enabled.Store(false)
|
||||||
|
slog.Info("gateway debug logging DISABLED")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const insertDebugLogSQL = `
|
||||||
|
INSERT INTO gateway_debug_logs (
|
||||||
|
upstream_request_id, account_id, account_email, account_platform,
|
||||||
|
event_type,
|
||||||
|
method, full_url, request_headers, request_body, request_size,
|
||||||
|
response_status, response_headers, response_body_preview, response_size,
|
||||||
|
model_requested, model_upstream, is_stream, duration_ms,
|
||||||
|
tls_profile, error_message
|
||||||
|
) VALUES (
|
||||||
|
$1, $2, $3, $4,
|
||||||
|
$5,
|
||||||
|
$6, $7, $8, $9, $10,
|
||||||
|
$11, $12, $13, $14,
|
||||||
|
$15, $16, $17, $18,
|
||||||
|
$19, $20
|
||||||
|
)`
|
||||||
|
|
||||||
|
// Log writes a debug log entry asynchronously (fire-and-forget).
|
||||||
|
func (l *GatewayDebugLogger) Log(entry GatewayDebugLogEntry) {
|
||||||
|
if !l.IsEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
_, err := l.db.ExecContext(ctx, insertDebugLogSQL,
|
||||||
|
nullStr(entry.UpstreamRequestID),
|
||||||
|
entry.AccountID,
|
||||||
|
nullStr(entry.AccountEmail),
|
||||||
|
nullStr(entry.AccountPlatform),
|
||||||
|
coalesce(entry.EventType, "api_call"),
|
||||||
|
nullStr(entry.Method),
|
||||||
|
nullStr(entry.FullURL),
|
||||||
|
mapToString(entry.RequestHeaders),
|
||||||
|
bytesToString(entry.RequestBody),
|
||||||
|
entry.RequestSize,
|
||||||
|
entry.ResponseStatus,
|
||||||
|
mapToString(entry.ResponseHeaders),
|
||||||
|
nullStr(entry.ResponseBodyPreview),
|
||||||
|
entry.ResponseSize,
|
||||||
|
nullStr(entry.ModelRequested),
|
||||||
|
nullStr(entry.ModelUpstream),
|
||||||
|
entry.IsStream,
|
||||||
|
entry.DurationMs,
|
||||||
|
nullStr(entry.TLSProfile),
|
||||||
|
nullStr(entry.ErrorMessage),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
slog.Warn("gateway debug log write failed", "error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogUpstreamRequest captures request+response from a gateway forward call.
|
||||||
|
func (l *GatewayDebugLogger) LogUpstreamRequest(
|
||||||
|
account *Account,
|
||||||
|
upstreamReq *http.Request,
|
||||||
|
upstreamBody []byte,
|
||||||
|
resp *http.Response,
|
||||||
|
responsePreview string,
|
||||||
|
responseSize int,
|
||||||
|
originalModel string,
|
||||||
|
upstreamModel string,
|
||||||
|
isStream bool,
|
||||||
|
duration time.Duration,
|
||||||
|
tlsProfile string,
|
||||||
|
errMsg string,
|
||||||
|
) {
|
||||||
|
if !l.IsEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
entry := GatewayDebugLogEntry{
|
||||||
|
AccountID: account.ID,
|
||||||
|
AccountEmail: account.Name,
|
||||||
|
AccountPlatform: account.Platform,
|
||||||
|
EventType: "api_call",
|
||||||
|
Method: upstreamReq.Method,
|
||||||
|
FullURL: upstreamReq.URL.String(),
|
||||||
|
RequestHeaders: extractHeaders(upstreamReq.Header),
|
||||||
|
RequestBody: upstreamBody,
|
||||||
|
RequestSize: len(upstreamBody),
|
||||||
|
ModelRequested: originalModel,
|
||||||
|
ModelUpstream: upstreamModel,
|
||||||
|
IsStream: isStream,
|
||||||
|
DurationMs: int(duration.Milliseconds()),
|
||||||
|
TLSProfile: tlsProfile,
|
||||||
|
ErrorMessage: errMsg,
|
||||||
|
}
|
||||||
|
|
||||||
|
if resp != nil {
|
||||||
|
entry.UpstreamRequestID = resp.Header.Get("x-request-id")
|
||||||
|
entry.ResponseStatus = resp.StatusCode
|
||||||
|
entry.ResponseHeaders = extractHeaders(resp.Header)
|
||||||
|
entry.ResponseBodyPreview = debugTruncate(responsePreview, 4096)
|
||||||
|
entry.ResponseSize = responseSize
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log(entry)
|
||||||
|
}
|
||||||
|
|
||||||
|
// LogOAuthRefresh logs an OAuth token refresh event.
|
||||||
|
func (l *GatewayDebugLogger) LogOAuthRefresh(accountID int64, accountEmail string, duration time.Duration, errMsg string) {
|
||||||
|
if !l.IsEnabled() {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
l.Log(GatewayDebugLogEntry{
|
||||||
|
AccountID: accountID,
|
||||||
|
AccountEmail: accountEmail,
|
||||||
|
EventType: "oauth_refresh",
|
||||||
|
DurationMs: int(duration.Milliseconds()),
|
||||||
|
ErrorMessage: errMsg,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- helpers ---
|
||||||
|
|
||||||
|
func extractHeaders(h http.Header) map[string]string {
|
||||||
|
out := make(map[string]string, len(h))
|
||||||
|
for k, vals := range h {
|
||||||
|
lower := strings.ToLower(k)
|
||||||
|
if lower == "authorization" || lower == "x-api-key" {
|
||||||
|
out[k] = "[REDACTED]"
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
out[k] = strings.Join(vals, ", ")
|
||||||
|
}
|
||||||
|
return out
|
||||||
|
}
|
||||||
|
|
||||||
|
func debugTruncate(s string, maxLen int) string {
|
||||||
|
if len(s) <= maxLen {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
return s[:maxLen]
|
||||||
|
}
|
||||||
|
|
||||||
|
func nullStr(s string) interface{} {
|
||||||
|
if s == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
// bytesToString converts raw bytes to string for TEXT column. No validation.
|
||||||
|
func bytesToString(data []byte) interface{} {
|
||||||
|
if len(data) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
// mapToString serializes a map to JSON string for TEXT column.
|
||||||
|
func mapToString(m map[string]string) interface{} {
|
||||||
|
if len(m) == 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
data, err := json.Marshal(m)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return string(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
func coalesce(s, fallback string) string {
|
||||||
|
if s == "" {
|
||||||
|
return fallback
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
37
backend/migrations/082_create_gateway_debug_logs.sql
Normal file
37
backend/migrations/082_create_gateway_debug_logs.sql
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS gateway_debug_logs (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
|
||||||
|
upstream_request_id TEXT,
|
||||||
|
account_id BIGINT,
|
||||||
|
account_email TEXT,
|
||||||
|
account_platform TEXT,
|
||||||
|
|
||||||
|
event_type TEXT NOT NULL DEFAULT 'api_call',
|
||||||
|
|
||||||
|
method TEXT,
|
||||||
|
full_url TEXT,
|
||||||
|
request_headers TEXT,
|
||||||
|
request_body TEXT,
|
||||||
|
request_size INTEGER,
|
||||||
|
|
||||||
|
response_status INTEGER,
|
||||||
|
response_headers TEXT,
|
||||||
|
response_body_preview TEXT,
|
||||||
|
response_size INTEGER,
|
||||||
|
|
||||||
|
model_requested TEXT,
|
||||||
|
model_upstream TEXT,
|
||||||
|
is_stream BOOLEAN DEFAULT FALSE,
|
||||||
|
duration_ms INTEGER,
|
||||||
|
|
||||||
|
tls_profile TEXT,
|
||||||
|
|
||||||
|
error_message TEXT,
|
||||||
|
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_account_id ON gateway_debug_logs (account_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_created_at ON gateway_debug_logs (created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_event_type ON gateway_debug_logs (event_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_model ON gateway_debug_logs (model_requested);
|
||||||
70
backend/migrations/083_reconcile_gateway_debug_logs.sql
Normal file
70
backend/migrations/083_reconcile_gateway_debug_logs.sql
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
CREATE TABLE IF NOT EXISTS gateway_debug_logs (
|
||||||
|
id BIGSERIAL PRIMARY KEY,
|
||||||
|
upstream_request_id TEXT,
|
||||||
|
account_id BIGINT,
|
||||||
|
account_email TEXT,
|
||||||
|
account_platform TEXT,
|
||||||
|
event_type TEXT NOT NULL DEFAULT 'api_call',
|
||||||
|
method TEXT,
|
||||||
|
full_url TEXT,
|
||||||
|
request_headers TEXT,
|
||||||
|
request_body TEXT,
|
||||||
|
request_size INTEGER,
|
||||||
|
response_status INTEGER,
|
||||||
|
response_headers TEXT,
|
||||||
|
response_body_preview TEXT,
|
||||||
|
response_size INTEGER,
|
||||||
|
model_requested TEXT,
|
||||||
|
model_upstream TEXT,
|
||||||
|
is_stream BOOLEAN NOT NULL DEFAULT FALSE,
|
||||||
|
duration_ms INTEGER,
|
||||||
|
tls_profile TEXT,
|
||||||
|
error_message TEXT,
|
||||||
|
created_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
|
||||||
|
);
|
||||||
|
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS upstream_request_id TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS account_id BIGINT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS account_email TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS account_platform TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS event_type TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS method TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS full_url TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS request_headers TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS request_body TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS request_size INTEGER;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS response_status INTEGER;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS response_headers TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS response_body_preview TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS response_size INTEGER;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS model_requested TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS model_upstream TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS is_stream BOOLEAN;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS duration_ms INTEGER;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS tls_profile TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS error_message TEXT;
|
||||||
|
ALTER TABLE gateway_debug_logs ADD COLUMN IF NOT EXISTS created_at TIMESTAMPTZ;
|
||||||
|
|
||||||
|
UPDATE gateway_debug_logs
|
||||||
|
SET event_type = 'api_call'
|
||||||
|
WHERE event_type IS NULL;
|
||||||
|
|
||||||
|
UPDATE gateway_debug_logs
|
||||||
|
SET is_stream = FALSE
|
||||||
|
WHERE is_stream IS NULL;
|
||||||
|
|
||||||
|
UPDATE gateway_debug_logs
|
||||||
|
SET created_at = NOW()
|
||||||
|
WHERE created_at IS NULL;
|
||||||
|
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN event_type SET DEFAULT 'api_call';
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN event_type SET NOT NULL;
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN is_stream SET DEFAULT FALSE;
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN is_stream SET NOT NULL;
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN created_at SET DEFAULT NOW();
|
||||||
|
ALTER TABLE gateway_debug_logs ALTER COLUMN created_at SET NOT NULL;
|
||||||
|
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_account_id ON gateway_debug_logs (account_id);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_created_at ON gateway_debug_logs (created_at);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_event_type ON gateway_debug_logs (event_type);
|
||||||
|
CREATE INDEX IF NOT EXISTS idx_gdl_model ON gateway_debug_logs (model_requested);
|
||||||
Loading…
x
Reference in New Issue
Block a user