Merge pull request #54 from router-for-me/v6-test

fix(auth): Improve file-based auth handling and consistency
This commit is contained in:
Luis Pater
2025-09-22 17:25:27 +08:00
committed by GitHub
3 changed files with 55 additions and 10 deletions
+39 -9
View File
@@ -13,6 +13,7 @@ import (
"path/filepath" "path/filepath"
"regexp" "regexp"
"strings" "strings"
"sync"
"time" "time"
"github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces" "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
@@ -493,6 +494,12 @@ type FileStreamingLogWriter struct {
// statusWritten indicates whether the response status has been written. // statusWritten indicates whether the response status has been written.
statusWritten bool statusWritten bool
// mu protects concurrent access to the writer state.
mu sync.RWMutex
// closed indicates whether the streaming writer has been closed.
closed bool
} }
// WriteChunkAsync writes a response chunk asynchronously (non-blocking). // WriteChunkAsync writes a response chunk asynchronously (non-blocking).
@@ -500,7 +507,10 @@ type FileStreamingLogWriter struct {
// Parameters: // Parameters:
// - chunk: The response chunk to write // - chunk: The response chunk to write
func (w *FileStreamingLogWriter) WriteChunkAsync(chunk []byte) { func (w *FileStreamingLogWriter) WriteChunkAsync(chunk []byte) {
if w.chunkChan == nil { w.mu.RLock()
defer w.mu.RUnlock()
if w.chunkChan == nil || w.closed {
return return
} }
@@ -525,6 +535,9 @@ func (w *FileStreamingLogWriter) WriteChunkAsync(chunk []byte) {
// Returns: // Returns:
// - error: An error if writing fails, nil otherwise // - error: An error if writing fails, nil otherwise
func (w *FileStreamingLogWriter) WriteStatus(status int, headers map[string][]string) error { func (w *FileStreamingLogWriter) WriteStatus(status int, headers map[string][]string) error {
w.mu.Lock()
defer w.mu.Unlock()
if w.file == nil || w.statusWritten { if w.file == nil || w.statusWritten {
return nil return nil
} }
@@ -553,21 +566,38 @@ func (w *FileStreamingLogWriter) WriteStatus(status int, headers map[string][]st
// Returns: // Returns:
// - error: An error if closing fails, nil otherwise // - error: An error if closing fails, nil otherwise
func (w *FileStreamingLogWriter) Close() error { func (w *FileStreamingLogWriter) Close() error {
if w.chunkChan != nil { w.mu.Lock()
close(w.chunkChan) if w.closed {
w.mu.Unlock()
return nil
}
w.closed = true
chunkChan := w.chunkChan
closeChan := w.closeChan
file := w.file
w.mu.Unlock()
if chunkChan != nil {
close(chunkChan)
} }
// Wait for async writer to finish // Wait for async writer to finish
if w.closeChan != nil { if closeChan != nil {
<-w.closeChan <-closeChan
w.chunkChan = nil
} }
if w.file != nil { var err error
return w.file.Close() if file != nil {
err = file.Close()
} }
return nil w.mu.Lock()
w.chunkChan = nil
w.closeChan = nil
w.file = nil
w.mu.Unlock()
return err
} }
// asyncWriter runs in a goroutine to handle async chunk writing. // asyncWriter runs in a goroutine to handle async chunk writing.
+7 -1
View File
@@ -526,8 +526,14 @@ func (w *Watcher) SnapshotCoreAuths() []*coreauth.Auth {
if email, _ := metadata["email"].(string); email != "" { if email, _ := metadata["email"].(string); email != "" {
label = email label = email
} }
// Use relative path under authDir as ID to stay consistent with FileStore
id := full
if rel, errRel := filepath.Rel(w.authDir, full); errRel == nil && rel != "" {
id = rel
}
a := &coreauth.Auth{ a := &coreauth.Auth{
ID: full, ID: id,
Provider: provider, Provider: provider,
Label: label, Label: label,
Status: coreauth.StatusActive, Status: coreauth.StatusActive,
+9
View File
@@ -64,6 +64,15 @@ func (s *FileStore) Save(ctx context.Context, auth *Auth) error {
if path == "" { if path == "" {
return fmt.Errorf("auth filestore: missing file path attribute for %s", auth.ID) return fmt.Errorf("auth filestore: missing file path attribute for %s", auth.ID)
} }
// If the auth has been disabled and the original file was removed, avoid
// recreating it on disk. This lets operators delete auth files explicitly.
if auth.Disabled {
if _, err := os.Stat(path); err != nil {
if os.IsNotExist(err) {
return nil
}
}
}
s.mu.Lock() s.mu.Lock()
defer s.mu.Unlock() defer s.mu.Unlock()
if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil { if err := os.MkdirAll(filepath.Dir(path), 0o700); err != nil {