Make nmon easier to install and run without editing YAML by hand: - Settings: point-and-click forms over config.yaml (GET/PUT /api/v1/settings/form); the YAML editor moves to /settings/yaml. - First install: with no sign-in configured nmon starts in install mode, serves only the setup wizard (sign-in, basics, devices, alerting) and closes it for good once sign-in is saved. A self-signed certificate is generated when no TLS source is given. poller.interval becomes a config setting. - Reverse proxies: opt-in --http-listen plain-HTTP listener beside HTTPS; sign-in stays required. - Webhooks: Generic, Teams Adaptive Card, Slack and Discord presets, template preview and test send. - Help: the operations, coverage and profile docs served at /help. - Polling: per-device "Poll now", a dry-run test poll returning an SNMP trace, and time-limited in-memory SNMP debug recording, in the UI and as `nmon device poll`. - Ansible: deploys into install mode when no sign-in is set, and no longer overwrites the config.yaml the wizard wrote. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017TuKgyKve2XLFtrrXWMi2w
38 lines
1.3 KiB
Go
38 lines
1.3 KiB
Go
package store
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"time"
|
|
)
|
|
|
|
// setupState is the live model of setup_state (migration 16).
|
|
type setupState = m0016SetupState
|
|
|
|
// SetupState records whether first-install setup has finished. The flag only ever goes from false to true: there
|
|
// is deliberately no way to clear it, so install mode (which runs without sign-in) cannot be reopened.
|
|
type SetupState interface {
|
|
// SetupCompleted reports whether setup was marked completed, and when.
|
|
SetupCompleted(ctx context.Context) (bool, time.Time, error)
|
|
// MarkSetupCompleted records completion by who. It is idempotent: the first completion time is kept.
|
|
MarkSetupCompleted(ctx context.Context, by string) error
|
|
}
|
|
|
|
func (s *bunStore) SetupCompleted(ctx context.Context) (bool, time.Time, error) {
|
|
var row setupState
|
|
err := s.db.NewSelect().Model(&row).Where("id = 1").Limit(1).Scan(ctx)
|
|
if err != nil {
|
|
if errors.Is(mapErr(err), ErrNotFound) {
|
|
return false, time.Time{}, nil
|
|
}
|
|
return false, time.Time{}, mapErr(err)
|
|
}
|
|
return true, row.CompletedAt, nil
|
|
}
|
|
|
|
func (s *bunStore) MarkSetupCompleted(ctx context.Context, by string) error {
|
|
row := &setupState{ID: 1, CompletedAt: s.now(), CompletedBy: by}
|
|
_, err := s.db.NewInsert().Model(row).On("CONFLICT (id) DO NOTHING").Exec(ctx)
|
|
return mapErr(err)
|
|
}
|