mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-03 13:00:52 +08:00
feat(auth): standardize last_refresh metadata handling across executors
- Added `last_refresh` timestamp to metadata for Codex, Claude, Qwen, and Gemini executors. - Implemented `extractLastRefreshTimestamp` utility for parsing diverse timestamp formats in management handlers. - Ensured consistent update and preservation of `last_refresh` in file-based auth handling.
This commit is contained in:
@@ -9,6 +9,7 @@ import (
|
|||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -31,6 +32,61 @@ var (
|
|||||||
oauthStatus = make(map[string]string)
|
oauthStatus = make(map[string]string)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var lastRefreshKeys = []string{"last_refresh", "lastRefresh", "last_refreshed_at", "lastRefreshedAt"}
|
||||||
|
|
||||||
|
func extractLastRefreshTimestamp(meta map[string]any) (time.Time, bool) {
|
||||||
|
if len(meta) == 0 {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
for _, key := range lastRefreshKeys {
|
||||||
|
if val, ok := meta[key]; ok {
|
||||||
|
if ts, ok1 := parseLastRefreshValue(val); ok1 {
|
||||||
|
return ts, true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLastRefreshValue(v any) (time.Time, bool) {
|
||||||
|
switch val := v.(type) {
|
||||||
|
case string:
|
||||||
|
s := strings.TrimSpace(val)
|
||||||
|
if s == "" {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
layouts := []string{time.RFC3339, time.RFC3339Nano, "2006-01-02 15:04:05", "2006-01-02T15:04:05Z07:00"}
|
||||||
|
for _, layout := range layouts {
|
||||||
|
if ts, err := time.Parse(layout, s); err == nil {
|
||||||
|
return ts.UTC(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if unix, err := strconv.ParseInt(s, 10, 64); err == nil && unix > 0 {
|
||||||
|
return time.Unix(unix, 0).UTC(), true
|
||||||
|
}
|
||||||
|
case float64:
|
||||||
|
if val <= 0 {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
return time.Unix(int64(val), 0).UTC(), true
|
||||||
|
case int64:
|
||||||
|
if val <= 0 {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
return time.Unix(val, 0).UTC(), true
|
||||||
|
case int:
|
||||||
|
if val <= 0 {
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
return time.Unix(int64(val), 0).UTC(), true
|
||||||
|
case json.Number:
|
||||||
|
if i, err := val.Int64(); err == nil && i > 0 {
|
||||||
|
return time.Unix(i, 0).UTC(), true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return time.Time{}, false
|
||||||
|
}
|
||||||
|
|
||||||
// List auth files
|
// List auth files
|
||||||
func (h *Handler) ListAuthFiles(c *gin.Context) {
|
func (h *Handler) ListAuthFiles(c *gin.Context) {
|
||||||
entries, err := os.ReadDir(h.cfg.AuthDir)
|
entries, err := os.ReadDir(h.cfg.AuthDir)
|
||||||
@@ -239,6 +295,8 @@ func (h *Handler) registerAuthFromFile(ctx context.Context, path string, data []
|
|||||||
if email, ok := metadata["email"].(string); ok && email != "" {
|
if email, ok := metadata["email"].(string); ok && email != "" {
|
||||||
label = email
|
label = email
|
||||||
}
|
}
|
||||||
|
lastRefresh, hasLastRefresh := extractLastRefreshTimestamp(metadata)
|
||||||
|
|
||||||
attr := map[string]string{
|
attr := map[string]string{
|
||||||
"path": path,
|
"path": path,
|
||||||
"source": path,
|
"source": path,
|
||||||
@@ -253,9 +311,14 @@ func (h *Handler) registerAuthFromFile(ctx context.Context, path string, data []
|
|||||||
CreatedAt: time.Now(),
|
CreatedAt: time.Now(),
|
||||||
UpdatedAt: time.Now(),
|
UpdatedAt: time.Now(),
|
||||||
}
|
}
|
||||||
|
if hasLastRefresh {
|
||||||
|
auth.LastRefreshedAt = lastRefresh
|
||||||
|
}
|
||||||
if existing, ok := h.authManager.GetByID(path); ok {
|
if existing, ok := h.authManager.GetByID(path); ok {
|
||||||
auth.CreatedAt = existing.CreatedAt
|
auth.CreatedAt = existing.CreatedAt
|
||||||
auth.LastRefreshedAt = existing.LastRefreshedAt
|
if !hasLastRefresh {
|
||||||
|
auth.LastRefreshedAt = existing.LastRefreshedAt
|
||||||
|
}
|
||||||
auth.NextRefreshAfter = existing.NextRefreshAfter
|
auth.NextRefreshAfter = existing.NextRefreshAfter
|
||||||
auth.Runtime = existing.Runtime
|
auth.Runtime = existing.Runtime
|
||||||
_, err := h.authManager.Update(ctx, auth)
|
_, err := h.authManager.Update(ctx, auth)
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
claudeauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/claude"
|
claudeauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/claude"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -171,6 +172,8 @@ func (e *ClaudeExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (
|
|||||||
auth.Metadata["email"] = td.Email
|
auth.Metadata["email"] = td.Email
|
||||||
auth.Metadata["expired"] = td.Expire
|
auth.Metadata["expired"] = td.Expire
|
||||||
auth.Metadata["type"] = "claude"
|
auth.Metadata["type"] = "claude"
|
||||||
|
now := time.Now().Format(time.RFC3339)
|
||||||
|
auth.Metadata["last_refresh"] = now
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
codexauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
|
codexauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/codex"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -222,6 +223,8 @@ func (e *CodexExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*
|
|||||||
// Use unified key in files
|
// Use unified key in files
|
||||||
auth.Metadata["expired"] = td.Expire
|
auth.Metadata["expired"] = td.Expire
|
||||||
auth.Metadata["type"] = "codex"
|
auth.Metadata["type"] = "codex"
|
||||||
|
now := time.Now().Format(time.RFC3339)
|
||||||
|
auth.Metadata["last_refresh"] = now
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
"net/http"
|
||||||
"sync"
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/auth/gemini"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -121,6 +122,7 @@ func (e *GeminiWebExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth
|
|||||||
auth.Metadata["secure_1psid"] = ts.Secure1PSID
|
auth.Metadata["secure_1psid"] = ts.Secure1PSID
|
||||||
auth.Metadata["secure_1psidts"] = ts.Secure1PSIDTS
|
auth.Metadata["secure_1psidts"] = ts.Secure1PSIDTS
|
||||||
auth.Metadata["type"] = "gemini-web"
|
auth.Metadata["type"] = "gemini-web"
|
||||||
|
auth.Metadata["last_refresh"] = time.Now().Format(time.RFC3339)
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import (
|
|||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
qwenauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/qwen"
|
qwenauth "github.com/router-for-me/CLIProxyAPI/v6/internal/auth/qwen"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
@@ -179,6 +180,8 @@ func (e *QwenExecutor) Refresh(ctx context.Context, auth *cliproxyauth.Auth) (*c
|
|||||||
// Use "expired" for consistency with existing file format
|
// Use "expired" for consistency with existing file format
|
||||||
auth.Metadata["expired"] = td.Expire
|
auth.Metadata["expired"] = td.Expire
|
||||||
auth.Metadata["type"] = "qwen"
|
auth.Metadata["type"] = "qwen"
|
||||||
|
now := time.Now().Format(time.RFC3339)
|
||||||
|
auth.Metadata["last_refresh"] = now
|
||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user