Merge pull request #54 from router-for-me/v6-test
fix(auth): Improve file-based auth handling and consistency
This commit is contained in:
@@ -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.
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user