Add notification channels and a provider abstraction
Users get a notification_channels table (replacing users.ntfy_topic); pages fan
out to every enabled channel through a notify.Provider registry, with ntfy as
the first provider. Fallbacks are typed {type, address}. Schema edited in place:
existing databases must be recreated.
Claude-Session: https://claude.ai/code/session_016mBLURvJoMuUEr9cB2RpUN
This commit is contained in:
+14
-8
@@ -95,9 +95,9 @@ func NewRouter(db *sql.DB, notify NotifyConfig, cfg config.Config, version strin
|
||||
r.Post("/api/oidc/device/deny", handleDeviceDecision(db, false))
|
||||
}
|
||||
r.Put("/api/me/onboarding", handleDismissOnboarding(db))
|
||||
// Proves the topic works, which is the only part of "notifications are
|
||||
// set up" that the person holding the phone can confirm.
|
||||
r.Post("/api/me/notify/test", handleTestNotification(notify, db))
|
||||
// Which kinds of notification channel this server can deliver, so the
|
||||
// UI offers only those.
|
||||
r.Get("/api/notify/providers", handleListProviders(notify.Providers))
|
||||
|
||||
// Readable by anyone signed in: the queue's assignment control and the
|
||||
// on-call schedule both need to name people.
|
||||
@@ -107,7 +107,13 @@ func NewRouter(db *sql.DB, notify NotifyConfig, cfg config.Config, version strin
|
||||
// requireSelfOrAdmin rather than sitting behind AdminOnly, because
|
||||
// which rule applies depends on the {id} in the path.
|
||||
r.Get("/api/users/{id}/teams", handleUserTeams(db))
|
||||
r.Put("/api/users/{id}/notify", handleSetNotifyTarget(db))
|
||||
r.Get("/api/users/{id}/channels", handleListChannels(db))
|
||||
r.Post("/api/users/{id}/channels", handleCreateChannel(db, notify.Providers))
|
||||
r.Patch("/api/users/{id}/channels/{channelID}", handleUpdateChannel(db, notify.Providers))
|
||||
r.Delete("/api/users/{id}/channels/{channelID}", handleDeleteChannel(db))
|
||||
// Proves the channel works, which is the only part of "notifications are
|
||||
// set up" that the person holding the phone can confirm.
|
||||
r.Post("/api/users/{id}/channels/{channelID}/test", handleTestChannel(db, notify))
|
||||
r.Put("/api/users/{id}/password", handleSetPassword(db))
|
||||
r.Get("/api/users/{id}/api-keys", handleListAPIKeys(db))
|
||||
r.Post("/api/users/{id}/api-keys", handleCreateAPIKey(db))
|
||||
@@ -130,7 +136,7 @@ func NewRouter(db *sql.DB, notify NotifyConfig, cfg config.Config, version strin
|
||||
// an administrator from outside; this is a different
|
||||
// question, so it is a different endpoint.
|
||||
r.Get("/api/admin/teams/{teamID}", handleAdminGetTeam(db))
|
||||
r.Get("/api/admin/settings", handleGetSettings(db, cfg))
|
||||
r.Get("/api/admin/settings", handleGetSettings(db, cfg, notify.Providers))
|
||||
r.Put("/api/admin/settings", handleSetSettings(db))
|
||||
})
|
||||
|
||||
@@ -175,7 +181,7 @@ func NewRouter(db *sql.DB, notify NotifyConfig, cfg config.Config, version strin
|
||||
r.With(opMode).Post("/api/teams", handleCreateTeam(db))
|
||||
r.With(opMode).Put("/api/teams/{teamID}", handleRenameTeam(db))
|
||||
r.With(opMode).Delete("/api/teams/{teamID}", handleDeleteTeam(db))
|
||||
r.Get("/api/teams/{teamID}/members", handleListTeamMembers(db))
|
||||
r.Get("/api/teams/{teamID}/members", handleListTeamMembers(db, notify.Providers))
|
||||
r.Post("/api/teams/{teamID}/members", handleAddTeamMember(db))
|
||||
r.Delete("/api/teams/{teamID}/members/{userID}", handleRemoveTeamMember(db))
|
||||
|
||||
@@ -192,8 +198,8 @@ func NewRouter(db *sql.DB, notify NotifyConfig, cfg config.Config, version strin
|
||||
r.Delete("/api/teams/{teamID}/invites/{inviteID}", handleRevokeInvite(db))
|
||||
|
||||
// A team's escalation ladder: who is paged when nobody answers.
|
||||
r.Get("/api/teams/{teamID}/escalation", handleGetEscalation(db))
|
||||
r.With(opMode).Put("/api/teams/{teamID}/escalation", handleSetEscalation(db))
|
||||
r.Get("/api/teams/{teamID}/escalation", handleGetEscalation(db, notify.Providers))
|
||||
r.With(opMode).Put("/api/teams/{teamID}/escalation", handleSetEscalation(db, notify.Providers))
|
||||
|
||||
// A team's own dead man's switches: which of its alerts are heartbeats,
|
||||
// and how long a silence has to last before somebody is paged.
|
||||
|
||||
Reference in New Issue
Block a user