458ba6a7ee
- customer CRUD wizard backend: per-system credential shapes validated via
connector construction, AES-256-GCM at rest, write-only over the API
- ticketing connectors (jira/azure_devops/youtrack/demo) with test-connection
- user management: roles, disable (revokes sessions), force password reset,
delete; self-demotion/disable/delete guards
- admin-editable runtime settings ({_id:app}) incl. atomizer URL override
- audit log written on every privileged mutation + filterable list API
- service status panel: mongo/atomizer/work-performer health + latency with
late-bound breaker, sync and jobs status providers
- tabbed admin UI (customers wizard dialog, users table, settings, status, audit)
- compose: mongo nofile ulimit 64000 (1024 default crashed WiredTiger)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
55 lines
1.6 KiB
Go
55 lines
1.6 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
"go.mongodb.org/mongo-driver/v2/bson"
|
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
|
|
"bountyboard/internal/domain"
|
|
)
|
|
|
|
// GetSettings returns the app settings document, creating defaults on first
|
|
// access.
|
|
func (s *Store) GetSettings(ctx context.Context) (*domain.AppSettings, error) {
|
|
var st domain.AppSettings
|
|
err := s.DB.Collection("settings").FindOne(ctx, bson.M{"_id": "app"}).Decode(&st)
|
|
if errors.Is(err, mongo.ErrNoDocuments) {
|
|
st = domain.AppSettings{
|
|
ID: "app",
|
|
BrandingName: "Bounty Board",
|
|
DefaultPollIntervalSec: 60,
|
|
DefaultCustomerBudget: 1000,
|
|
UpdatedAt: time.Now().UTC(),
|
|
Version: 1,
|
|
}
|
|
if _, err := s.DB.Collection("settings").InsertOne(ctx, st); err != nil &&
|
|
!mongo.IsDuplicateKeyError(err) {
|
|
return nil, fmt.Errorf("init settings: %w", err)
|
|
}
|
|
return &st, nil
|
|
}
|
|
if err != nil {
|
|
return nil, fmt.Errorf("get settings: %w", err)
|
|
}
|
|
return &st, nil
|
|
}
|
|
|
|
// PatchSettings applies a partial settings update (not version-raced in the
|
|
// UI; last write wins is acceptable for a single admin page).
|
|
func (s *Store) PatchSettings(ctx context.Context, set bson.M) (*domain.AppSettings, error) {
|
|
if _, err := s.GetSettings(ctx); err != nil { // ensure the doc exists
|
|
return nil, err
|
|
}
|
|
set["updatedAt"] = time.Now().UTC()
|
|
_, err := s.DB.Collection("settings").UpdateOne(ctx, bson.M{"_id": "app"},
|
|
bson.M{"$set": set, "$inc": bson.M{"version": 1}})
|
|
if err != nil {
|
|
return nil, fmt.Errorf("patch settings: %w", err)
|
|
}
|
|
return s.GetSettings(ctx)
|
|
}
|