Documentation
¶
Overview ¶
Package native implements the notification package's v2 routes in the package itself, on the kernel's notification and Telegram tables adopted in place (kernel.storage.adopt), and the e-mail configuration through the kernel's KernelSettings contract. Legacy handlers and native routes share the tables, so a route can switch between them at any time. Responses are byte-compatible with the legacy handlers (internal/tests/notificationcompat).
The e-mail configuration (GET and PUT) and the test send read and write the notification.email.config key of the kernel's v2_system_config through KernelSettings, namespace mail. Its value holds the SMTP password, a secret: the package reads it in clear (kernel.settings.mail.secrets.v1) because it sends the test e-mail itself. Administrators see it masked, as the kernel's handler shows it, and an update that keeps it sends the placeholder, which the kernel replaces with the stored password. A host without the contract leaves the three routes legacy.
Setting the Telegram webhook derives its default URL from the request's scheme and host, which the kernel sends package hosts as request metadata (RequestMetadata.Scheme and Host). The public Telegram webhook has no native handler and stays bridged: it answers bot commands from subscriber data (the subscription token) that no kernel view exposes.
Index ¶
- Constants
- func EmailConfigRequestID(token string) string
- type Binding
- type Bot
- type EmailConfig
- type Log
- type NotifySettingsRequest
- type Service
- func (s *Service) AdminBindings(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminCreateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminDeleteTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminDeleteWebhook(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminEmailConfig(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminGetBot(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminLogs(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminSetWebhook(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminTelegramBroadcast(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminTelegramNotify(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminTemplates(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminTestNotification(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminUpdateBindingNotify(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminUpdateBot(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminUpdateEmailConfig(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) AdminUpdateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) Handlers() map[string]pluginhostsdk.NativeHandler
- func (s *Service) UserMarkAllRead(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserMarkRead(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserNotifications(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserTelegramNotify(_ context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserTelegramStatus(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserTelegramUnbind(_ context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- func (s *Service) UserUnreadCount(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
- type SetWebhookRequest
- type Settings
- type Template
- type UpdateBotRequest
Constants ¶
const ( EmailConfigGetRouteID = "notification.admin.notification.email.config.get" EmailConfigPutRouteID = "notification.admin.notification.email.config.put" TestSendRouteID = "notification.admin.notification.test.post" )
Route ids of the routes that read or write the e-mail configuration.
const SetWebhookRouteID = "notification.admin.telegram.webhook.post"
SetWebhookRouteID is POST /api/v2/admin/telegram/webhook.
Variables ¶
This section is empty.
Functions ¶
func EmailConfigRequestID ¶
EmailConfigRequestID names an e-mail configuration update in the kernel's settings request ledger, so a retried request is applied once. token identifies the HTTP request: its Idempotency-Key, else its request id, else a fresh value.
Types ¶
type Binding ¶
type Binding struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID uint `gorm:"uniqueIndex" json:"user_id"`
TelegramID int64 `gorm:"uniqueIndex" json:"telegram_id"`
Username string `gorm:"size:100" json:"username"`
FirstName string `gorm:"size:100" json:"first_name"`
LastName string `gorm:"size:100" json:"last_name"`
LanguageCode string `gorm:"size:10" json:"language_code"`
IsBanned bool `gorm:"default:false" json:"is_banned"`
BannedAt *time.Time `json:"banned_at"`
NotifyExpire bool `gorm:"default:true" json:"notify_expire"`
NotifyTraffic bool `gorm:"default:true" json:"notify_traffic"`
NotifyTicket bool `gorm:"default:true" json:"notify_ticket"`
MessageCount int64 `json:"message_count"`
LastActive time.Time `json:"last_active"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Binding is a v2_telegram_user row: a member's Telegram account. The kernel model's User relation is not loaded; the answers that show the member's e-mail read it from kapi_user_directory_v1.
type Bot ¶
type Bot struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Token string `gorm:"size:200;not null" json:"token"`
Enabled bool `gorm:"default:false" json:"enabled"`
WebhookURL string `gorm:"size:255" json:"webhook_url"`
WebhookSet bool `gorm:"default:false" json:"webhook_set"`
AdminIDs string `gorm:"type:text" json:"admin_ids"`
WelcomeMsg string `gorm:"type:text" json:"welcome_msg"`
AllowBind bool `gorm:"default:true" json:"allow_bind"`
AllowSub bool `gorm:"default:true" json:"allow_sub"`
AllowTicket bool `gorm:"default:true" json:"allow_ticket"`
AllowInfo bool `gorm:"default:true" json:"allow_info"`
TotalUsers int64 `json:"total_users"`
TotalChats int64 `json:"total_chats"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Bot is a v2_telegram_bot row.
type EmailConfig ¶
type EmailConfig struct {
Host string `json:"host"`
Port int `json:"port"`
Username string `json:"username"`
Password string `json:"password"`
FromName string `json:"from_name"`
FromAddress string `json:"from_address"`
Encryption string `json:"encryption"`
}
EmailConfig is the kernel's model.EmailConfig: the stored JSON value has its fields in this order.
type Log ¶
type Log struct {
ID uint `gorm:"primaryKey" json:"id"`
UserID *uint `gorm:"index" json:"user_id"`
Type string `gorm:"size:20" json:"type"`
Event string `gorm:"size:50" json:"event"`
Title string `gorm:"size:200" json:"title"`
Content string `gorm:"type:text" json:"content"`
Status int `json:"status"`
Error string `gorm:"size:500" json:"error"`
SentAt *time.Time `json:"sent_at"`
ReadAt *time.Time `json:"read_at"`
CreatedAt time.Time `gorm:"index" json:"created_at"`
}
Log is a v2_notification_log row. Its JSON matches the kernel model, which the member's notification list returns as is.
type NotifySettingsRequest ¶
type NotifySettingsRequest struct {
NotifyExpire *bool `json:"notify_expire"`
NotifyTraffic *bool `json:"notify_traffic"`
NotifyTicket *bool `json:"notify_ticket"`
}
NotifySettingsRequest is the member's notification settings body; its name is part of the decoding messages, as in the kernel's handler.
type Service ¶
type Service struct {
// Open returns the package's storage connection, on which the adopted
// tables and the kapi_user_directory_v1 view are visible.
Open func(ctx context.Context) (*gorm.DB, error)
// Settings is the kernel's KernelSettings; without it the e-mail
// configuration and test send have no native handler and stay legacy.
Settings Settings
// NewToken names a request that carries neither an Idempotency-Key
// nor a request id; it defaults to a random UUID.
NewToken func() string
// Now defaults to time.Now.
Now func() time.Time
}
Service holds what the native routes need.
func (*Service) AdminBindings ¶
func (s *Service) AdminBindings(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminBindings is GET /api/v2/admin/telegram/users: the members' Telegram bindings, newest first, one page or (all=true) every one.
func (*Service) AdminCreateTemplate ¶
func (s *Service) AdminCreateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminCreateTemplate is POST /api/v2/admin/notification/templates.
func (*Service) AdminDeleteTemplate ¶
func (s *Service) AdminDeleteTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminDeleteTemplate is DELETE /api/v2/admin/notification/templates/:id.
func (*Service) AdminDeleteWebhook ¶
func (s *Service) AdminDeleteWebhook(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminDeleteWebhook is DELETE /api/v2/admin/telegram/webhook: it calls the Bot API's deleteWebhook and records that no webhook is set.
func (*Service) AdminEmailConfig ¶
func (s *Service) AdminEmailConfig(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminEmailConfig is GET /api/v2/admin/notification/email/config: the stored configuration over the defaults, with the SMTP password masked as the kernel's handler masks it.
func (*Service) AdminGetBot ¶
func (s *Service) AdminGetBot(ctx context.Context, _ pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminGetBot is GET /api/v2/admin/telegram/bot.
func (*Service) AdminLogs ¶
func (s *Service) AdminLogs(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminLogs is GET /api/v2/admin/notification/logs.
func (*Service) AdminSetWebhook ¶
func (s *Service) AdminSetWebhook(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminSetWebhook is POST /api/v2/admin/telegram/webhook: it calls the Bot API's setWebhook and records the webhook. Without a url in the body the webhook is this Control's /api/v2/telegram/webhook, at the scheme and host of the administrator's request, as the kernel's handler builds it: the request_scheme and request_host the kernel sends. The kernel resolves them from the connection, or from X-Forwarded-Proto/Host only when the peer is a trusted reverse proxy (server.trusted_proxies); forwarding headers never reach the package and are not read here.
A kernel that sends no request scheme or host leaves the request to the legacy handler (pluginhostsdk.ErrNativeUnavailable), which builds the URL from what the kernel's bridge saw.
func (*Service) AdminTelegramBroadcast ¶
func (s *Service) AdminTelegramBroadcast(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminTelegramBroadcast is POST /api/v2/admin/telegram/broadcast: the message goes, one after another, to every member who is not banned and takes at least one kind of notification.
func (*Service) AdminTelegramNotify ¶
func (s *Service) AdminTelegramNotify(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminTelegramNotify is POST /api/v2/admin/telegram/notify: one message to one Telegram chat.
func (*Service) AdminTemplates ¶
func (s *Service) AdminTemplates(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminTemplates is GET /api/v2/admin/notification/templates.
func (*Service) AdminTestNotification ¶
func (s *Service) AdminTestNotification(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminTestNotification is POST /api/v2/admin/notification/test. An e-mail test is sent from the package host with the stored SMTP configuration, as the kernel's handler sends it; the Telegram and webhook tests are the kernel's stubs, which send nothing and succeed.
func (*Service) AdminUpdateBindingNotify ¶
func (s *Service) AdminUpdateBindingNotify(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminUpdateBindingNotify is PUT /api/v2/admin/telegram/users/:id/notify. notify_enabled sets all three switches unless one is given on its own.
func (*Service) AdminUpdateBot ¶
func (s *Service) AdminUpdateBot(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminUpdateBot is PUT /api/v2/admin/telegram/bot. Without a bot row it creates one.
func (*Service) AdminUpdateEmailConfig ¶
func (s *Service) AdminUpdateEmailConfig(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminUpdateEmailConfig is PUT /api/v2/admin/notification/email/config: host, port and sender address are required; a blank password or the placeholder keeps the stored one, and an absent encryption the stored one. The kernel writes the value through KernelSettings (namespace mail); a kept password is sent as the placeholder, which the kernel replaces with the stored one. The kernel records the system configuration audit entry its own handler records, in the same transaction.
func (*Service) AdminUpdateTemplate ¶
func (s *Service) AdminUpdateTemplate(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
AdminUpdateTemplate is PUT /api/v2/admin/notification/templates/:id.
func (*Service) Handlers ¶
func (s *Service) Handlers() map[string]pluginhostsdk.NativeHandler
Handlers returns the native handlers by route id.
func (*Service) UserMarkAllRead ¶
func (s *Service) UserMarkAllRead(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserMarkAllRead is POST /api/v2/user/notifications/read-all. Like the legacy handler it answers success even when the update fails.
func (*Service) UserMarkRead ¶
func (s *Service) UserMarkRead(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserMarkRead is POST /api/v2/user/notifications/:id/read. The id is compared as given, so one that is not a number matches no row.
func (*Service) UserNotifications ¶
func (s *Service) UserNotifications(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserNotifications is GET /api/v2/user/notifications: the caller's notifications, newest first. Unlike the admin log list it does not clamp the page.
func (*Service) UserTelegramNotify ¶
func (s *Service) UserTelegramNotify(_ context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserTelegramNotify is POST /api/v2/user/telegram/notify. The legacy handler validates the body and changes nothing; so does this one.
func (*Service) UserTelegramStatus ¶
func (s *Service) UserTelegramStatus(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserTelegramStatus is GET /api/v2/user/telegram/status.
func (*Service) UserTelegramUnbind ¶
func (s *Service) UserTelegramUnbind(_ context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserTelegramUnbind is POST /api/v2/user/telegram/unbind. The legacy handler is a stub that deletes nothing; so is this one.
func (*Service) UserUnreadCount ¶
func (s *Service) UserUnreadCount(ctx context.Context, request pluginhostsdk.NativeRequest) (pluginhostsdk.NativeResponse, error)
UserUnreadCount is GET /api/v2/user/notifications/unread-count. Like the legacy handler it answers 0 when the count fails.
type SetWebhookRequest ¶
type SetWebhookRequest struct {
URL string `json:"url" binding:"omitempty,url,max=512"`
}
SetWebhookRequest is the kernel's SetWebhookRequest. Its name and tags are the kernel's, so binding and validation errors read the same.
type Settings ¶
type Settings interface {
GetSettings(ctx context.Context, in *kernelsettingsv1.GetSettingsRequest, opts ...grpc.CallOption) (*kernelsettingsv1.GetSettingsResponse, error)
PutSettings(ctx context.Context, in *kernelsettingsv1.PutSettingsRequest, opts ...grpc.CallOption) (*kernelsettingsv1.PutSettingsResponse, error)
}
Settings is the part of the kernel's KernelSettings contract the native routes call.
type Template ¶
type Template struct {
ID uint `gorm:"primaryKey" json:"id"`
Name string `gorm:"size:100;not null" json:"name"`
Type string `gorm:"size:20;not null" json:"type"`
Event string `gorm:"size:50;not null" json:"event"`
Title string `gorm:"size:200" json:"title"`
Content string `gorm:"type:text" json:"content"`
Enabled bool `gorm:"default:true" json:"enabled"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
}
Template is a v2_notification_template row. Its JSON matches the kernel model, which the template answers return as is.
type UpdateBotRequest ¶
type UpdateBotRequest struct {
Name string `json:"name" binding:"omitempty,max=255"`
Token string `json:"token" binding:"omitempty,min=1"`
WelcomeMsg string `json:"welcome_msg" binding:"omitempty,max=1024"`
WelcomeMessage string `json:"welcome_message" binding:"omitempty,max=1024"`
AdminIDs any `json:"admin_ids"`
AllowBind *bool `json:"allow_bind"`
AllowSub *bool `json:"allow_sub"`
AllowTicket *bool `json:"allow_ticket"`
AllowInfo *bool `json:"allow_info"`
}
UpdateBotRequest is the bot update body. Its name is part of the validation messages, so it matches the kernel's handler.UpdateBotRequest.