12 "github.com/mjl-/bstore"
14 "github.com/mjl-/mox/metrics"
15 "github.com/mjl-/mox/mlog"
16 "github.com/mjl-/mox/mox-"
17 "github.com/mjl-/mox/moxvar"
20// AccountRemove represents the scheduled removal of an account, when its last
21// reference goes away.
22type AccountRemove struct {
26// AuthDB and AuthDBTypes are exported for ../backup.go.
28var AuthDBTypes = []any{TLSPublicKey{}, LoginAttempt{}, LoginAttemptState{}, AccountRemove{}}
30var loginAttemptCleanerStop chan chan struct{}
32// Init opens auth.db and starts the login writer.
33func Init(ctx context.Context) error {
35 return fmt.Errorf("already initialized")
37 pkglog := mlog.New("store", nil)
38 p := mox.DataDirPath("auth.db")
39 os.MkdirAll(filepath.Dir(p), 0770)
40 opts := bstore.Options{Timeout: 5 * time.Second, Perm: 0660, RegisterLogger: moxvar.RegisterLogger(p, pkglog.Logger)}
42 AuthDB, err = bstore.Open(ctx, p, &opts, AuthDBTypes...)
47 // List pending account removals, and process them one by one, committing each
49 removals, err := bstore.QueryDB[AccountRemove](ctx, AuthDB).List()
51 return fmt.Errorf("listing scheduled account removals: %v", err)
53 for _, removal := range removals {
54 if err := removeAccount(pkglog, removal.AccountName); err != nil {
55 pkglog.Errorx("removing old account", err, slog.String("account", removal.AccountName))
59 startLoginAttemptWriter()
60 loginAttemptCleanerStop = make(chan chan struct{})
69 mlog.New("store", nil).Error("unhandled panic in LoginAttemptCleanup", slog.Any("err", x))
71 metrics.PanicInc(metrics.Store)
75 t := time.NewTicker(24 * time.Hour)
77 err := LoginAttemptCleanup(ctx)
78 pkglog.Check(err, "cleaning up old historic login attempts")
81 case c := <-loginAttemptCleanerStop:
94// Close closes auth.db and stops the login writer.
97 return fmt.Errorf("not open")
100 stopc := make(chan struct{})
101 writeLoginAttemptStop <- stopc
104 stopc = make(chan struct{})
105 loginAttemptCleanerStop <- stopc
108 err := AuthDB.Close()