feat(phase3): reminders, ICS feeds, templates and nightly backups
Reminders are absolute or relative ("10 minutes before"). A relative one
is resolved against the task in SQL, so moving a task moves its reminders
too. They are marked sent before delivery, not after: a reminder that
goes out unrecorded repeats every minute forever, which is worse than
one recorded that fails to send.
The calendar feed is a per-user token in the path, because Apple Calendar
and Google fetch with no credentials and no way to prompt for any. Tasks
go out as VTODO — the same component CalDAV will serve — and timed ones
also as VEVENT, since Google ignores VTODO entirely.
Templates store relative day offsets rather than dates, which is what
makes them reusable: an onboarding checklist is "day 1, day 3, day 14",
not three dates in March. Building a project from one is a single
transaction, so a failure cannot leave half a checklist.
Backups use SQLite's VACUUM INTO, not a file copy — a copy of a live
WAL database can miss recent writes or catch a page mid-update, and the
result looks like a database right up until it is needed. Rotation keeps
fourteen by count, not by age: a container that was off for a month must
not come back and delete every backup it has.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
4d142b2fa7
commit
7e4c730049
@@ -59,6 +59,22 @@ Linket virker i %s. Hvis du ikke ved, hvad det her handler om, kan du roligt sle
|
||||
return s.send(ctx, to, subject, body)
|
||||
}
|
||||
|
||||
func (s *Sender) SendReminder(ctx context.Context, to, name, task, link string) error {
|
||||
subject := "Påmindelse: " + task
|
||||
body := fmt.Sprintf(`Hej %s
|
||||
|
||||
Du bad om en påmindelse om:
|
||||
|
||||
%s
|
||||
|
||||
%s
|
||||
|
||||
— verdande
|
||||
`, name, task, link)
|
||||
|
||||
return s.send(ctx, to, subject, body)
|
||||
}
|
||||
|
||||
func (s *Sender) SendPasswordReset(ctx context.Context, to, name, link string, ttl time.Duration) error {
|
||||
subject := "Nulstil din adgangskode i verdande"
|
||||
body := fmt.Sprintf(`Hej %s
|
||||
|
||||
Reference in New Issue
Block a user