Fix "no such file or directory" on first use of new collections

scribble.ReadAll only returns its own ErrMissingCollection when the
collection name is empty - when the collection's directory simply
doesn't exist yet (e.g. before the first firewall rule or IP list entry
is ever created), it returns a raw os.IsNotExist error instead, which
GetFirewallRules/GetIPListEntries/GetServers didn't handle, surfacing as
"open db/ip_list_entries: no such file or directory" on a fresh install.
New isMissingCollectionErr() helper treats both cases as "empty
collection", not a real failure.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-07-12 17:55:37 +02:00
co-authored by Claude Sonnet 5
parent 77ba2b4799
commit 0f92317abb
+14 -3
View File
@@ -555,7 +555,7 @@ func (o *JsonDB) GetServers() ([]model.Server, error) {
var servers []model.Server var servers []model.Server
results, err := o.conn.ReadAll("servers") results, err := o.conn.ReadAll("servers")
if err != nil { if err != nil {
if err == scribble.ErrMissingCollection { if isMissingCollectionErr(err) {
return servers, nil return servers, nil
} }
return servers, err return servers, err
@@ -570,6 +570,17 @@ func (o *JsonDB) GetServers() ([]model.Server, error) {
return servers, nil return servers, nil
} }
// isMissingCollectionErr reports whether err from scribble.ReadAll just
// means "this collection doesn't exist yet" (nothing written there so
// far). scribble only returns its own ErrMissingCollection when the
// collection name itself is empty - if the collection's directory simply
// hasn't been created yet, ReadAll instead returns a raw os.IsNotExist
// error, which must be treated the same way (empty collection, not a
// real failure).
func isMissingCollectionErr(err error) bool {
return err == scribble.ErrMissingCollection || os.IsNotExist(err)
}
// validateServerID checks a server ID before it is ever used as a jsondb // validateServerID checks a server ID before it is ever used as a jsondb
// record key, shared by every server-scoped store method below. // record key, shared by every server-scoped store method below.
func validateServerID(serverID string) error { func validateServerID(serverID string) error {
@@ -686,7 +697,7 @@ func (o *JsonDB) GetFirewallRules(serverID string) ([]model.FirewallRule, error)
rules := make([]model.FirewallRule, 0) rules := make([]model.FirewallRule, 0)
records, err := o.conn.ReadAll("firewall_rules") records, err := o.conn.ReadAll("firewall_rules")
if err != nil { if err != nil {
if err == scribble.ErrMissingCollection { if isMissingCollectionErr(err) {
return rules, nil return rules, nil
} }
return nil, err return nil, err
@@ -742,7 +753,7 @@ func (o *JsonDB) GetIPListEntries() ([]model.IPListEntry, error) {
entries := make([]model.IPListEntry, 0) entries := make([]model.IPListEntry, 0)
records, err := o.conn.ReadAll("ip_list_entries") records, err := o.conn.ReadAll("ip_list_entries")
if err != nil { if err != nil {
if err == scribble.ErrMissingCollection { if isMissingCollectionErr(err) {
return entries, nil return entries, nil
} }
return nil, err return nil, err