fix(clients): сохранять авторизацию при ошибке записи базы

This commit is contained in:
samosvalishe
2026-09-12 17:33:46 +03:00
parent 6570cd9dd7
commit 061e3b882b
2 changed files with 55 additions and 6 deletions
+11 -6
View File
@@ -4,6 +4,7 @@ import (
"encoding/json"
"fmt"
"io"
"maps"
"net"
"os"
"sync"
@@ -61,16 +62,18 @@ func (db *DB) Add(clientID, comment string) error {
db.mu.Lock()
defer db.mu.Unlock()
db.data.Clients[clientID] = ClientInfo{Comment: comment}
return db.save()
next := Data{Clients: maps.Clone(db.data.Clients)}
next.Clients[clientID] = ClientInfo{Comment: comment}
return db.save(next)
}
func (db *DB) Remove(clientID string) error {
db.mu.Lock()
defer db.mu.Unlock()
delete(db.data.Clients, clientID)
return db.save()
next := Data{Clients: maps.Clone(db.data.Clients)}
delete(next.Clients, clientID)
return db.save(next)
}
func (db *DB) List() map[string]ClientInfo {
@@ -126,8 +129,8 @@ func (db *DB) loadIfModified() {
}
}
func (db *DB) save() error {
b, err := json.MarshalIndent(db.data, "", " ")
func (db *DB) save(next Data) error {
b, err := json.MarshalIndent(next, "", " ")
if err != nil {
return err
}
@@ -138,6 +141,8 @@ func (db *DB) save() error {
err = os.Rename(tmpFile, db.path)
}
if err == nil {
// Авторизация меняется только после успешной замены файла.
db.data = next
stat, _ := os.Stat(db.path)
if stat != nil {
db.lastModified = stat.ModTime()
+44
View File
@@ -2,11 +2,55 @@ package clientsdb
import (
"net"
"os"
"path/filepath"
"testing"
"time"
)
func TestFailedSavePreservesAuthorization(t *testing.T) {
path := filepath.Join(t.TempDir(), "clients.json")
db, err := New(path)
if err != nil {
t.Fatal(err)
}
if err := db.Add("existing", "original"); err != nil {
t.Fatal(err)
}
// Каталог вместо временного файла воспроизводит отказ записи без прав root.
if err := os.Mkdir(path+".tmp", 0o700); err != nil {
t.Fatal(err)
}
operations := []struct {
name string
run func() error
}{
{"add", func() error { return db.Add("new", "new") }},
{"update", func() error { return db.Add("existing", "changed") }},
{"remove", func() error { return db.Remove("existing") }},
}
for _, op := range operations {
t.Run(op.name, func(t *testing.T) {
if err := op.run(); err == nil {
t.Fatal("save unexpectedly succeeded")
}
if !db.IsAuthorized("existing") || db.IsAuthorized("new") {
t.Fatal("failed save changed authorization")
}
if got := db.List(); len(got) != 1 || got["existing"].Comment != "original" {
t.Fatalf("failed save changed clients: %+v", got)
}
})
}
loaded, err := New(path)
if err != nil {
t.Fatal(err)
}
if got := loaded.List(); len(got) != 1 || got["existing"].Comment != "original" {
t.Fatalf("disk changed: %+v", got)
}
}
func TestClientsDB(t *testing.T) {
tmpDir := t.TempDir()
dbPath := filepath.Join(tmpDir, "clients.json")