wishan / docs/notifications.md
Notifications System
A mobile app for buying asset shares
Notifications System
Overview
Wishan includes a built-in notification system that supports two dispatch channels:
| Dispatcher | Description |
|---|---|
email | HTML emails sent via SMTP using a cron job |
in_app | In-app notifications stored in the database; optionally pushed via Firebase Cloud Messaging (FCM) |
Architecture
notifications domain
├── core/
│ ├── entities.go # Domain types (NotificationType, NotificationTemplate, Notification, etc.)
│ └── service.go # Service interface
└── infra/
├── service.go # Implementation (SMTP + Firebase)
└── templates/
├── email.html # HTML email layout (go:embed)
└── in_app.txt # In-app text layout (go:embed)
Clean Architecture
core/– business types and service interface (no external dependencies)infra/– concrete implementation wiring SMTP, Firebase, and the database
Notification Types
Notification types are defined as a PostgreSQL enum and Go constants:
| Type | Description |
|---|---|
draw_winner | Sent when a user wins a draw |
draw_started | Sent when a draw the user joined becomes active |
draw_cancelled | Sent when a draw is cancelled |
draw_completed | Sent to all participants and admins when a draw is completed (winner selected and reveal date set); includes the reveal date |
welcome | Sent when a user first registers |
kyc_approved | Sent when a user's KYC is approved |
kyc_rejected | Sent when a user's KYC is rejected |
Database Tables
notification_templates
Stores customizable content for each (type, dispatcher) pair.
| Column | Type | Description |
|---|---|---|
id | bigserial | Primary key |
type | notification_type | Notification type enum |
dispatcher | notification_dispatcher | email or in_app |
subject | text (nullable) | Email subject line |
title | text | Notification title |
body | text | Notification body (HTML for email, plain text for in-app) |
created_at | timestamptz | |
updated_at | timestamptz |
Default content for all 12 templates (6 types × 2 dispatchers) is inserted automatically by the database schema.
notifications
Stores individual notification instances sent to users.
| Column | Type | Description |
|---|---|---|
id | bigserial | Primary key |
user_id | bigint | FK to users |
type | notification_type | |
dispatcher | notification_dispatcher | |
title | text | Rendered title |
body | text | Rendered body |
link | text (nullable) | Optional deep-link URL |
is_read | boolean | Whether the user has read this (in-app only) |
sent_at | timestamptz (nullable) | Set when email is sent; NULL = pending |
created_at | timestamptz |
user_device_tokens
Stores Firebase Cloud Messaging device tokens for push notifications.
| Column | Type | Description |
|---|---|---|
id | bigserial | Primary key |
user_id | bigint | FK to users |
token | text | FCM registration token |
platform | text | ios, android, or web |
Template Variables
Template bodies support Go template syntax (text/template). The following variables are substituted at send-time:
| Variable | Description |
|---|---|
{{.UserName}} | The recipient's name |
{{.DrawName}} | The name of the relevant draw/prize |
Example body with variables:
Congratulations {{.UserName}}! You have won the draw for {{.DrawName}}.
Storing a Notification
Call StoreNotification from anywhere in the application:
err := notificationsService.StoreNotification(ctx, notifications_core.StoreNotificationParams{
UserID: userID,
Type: notifications_core.NotificationTypeDrawWinner,
Dispatcher: notifications_core.DispatcherEmail,
Link: &drawLink,
Vars: map[string]string{
"UserName": "John Doe",
"DrawName": "Luxury Car Draw",
},
})
This will:
- Look up the template for
(draw_winner, email)from the database - Substitute template variables in the title and body
- Store the rendered notification in the
notificationstable - For
in_appdispatchers: trigger a Firebase push notification (if device tokens are registered) - For
emaildispatchers: the email is queued (pending); the cron job sends it
Email Sending (Cron Job)
The SendPendingEmails function is called by a cron job (default: every 5 minutes) to send all pending email notifications.
- Looks up user email from
users_identitieswhereprovider_id = 'email' - Renders the notification body into the embedded
email.htmllayout - Sends via SMTP using
github.com/jordan-wright/email - Marks the notification as sent (
sent_at = NOW())
SMTP Configuration
Add to config.yaml:
smtp:
host: "smtp.gmail.com"
port: 587
username: "notifications@example.com"
password: "your-app-password"
from: "notifications@example.com"
from_name: "Wishan"
enabled: true
Set enabled: false (default) to disable email sending without errors.
Cron Schedule
cron:
send_pending_emails_schedule: "@every 5m" # Every 5 minutes
Firebase Push Notifications (In-App)
For in-app notifications, the system optionally sends a Firebase Cloud Messaging (FCM) push notification if the user has registered device tokens.
Device Token Registration (Mobile)
POST /device-tokens
Authorization: Bearer <firebase-token>
Content-Type: application/json
{
"token": "<FCM registration token>",
"platform": "android" // "ios", "android", or "web"
}
DELETE /device-tokens
Authorization: Bearer <firebase-token>
Content-Type: application/json
{
"token": "<FCM registration token>"
}
Admin API Endpoints
| Method | Path | Permission | Description |
|---|---|---|---|
GET | /admin/notifications | read:notifications | List all notifications (paginated) |
GET | /admin/notifications/templates | read:notifications | Get all notification templates |
PUT | /admin/notifications/templates/:id | write:notifications | Update a notification template |
Mobile API Endpoints
| Method | Path | Description |
|---|---|---|
GET | /notifications?page=1&size=20 | Get user's in-app notifications |
PATCH | /notifications/:id/read | Mark a notification as read |
PATCH | /notifications/read-all | Mark all notifications as read |
POST | /device-tokens | Register FCM device token |
DELETE | /device-tokens | Remove FCM device token |
Admin Panel (custom_admin)
The admin panel includes a Notifications page at /admin/notifications where admin users can:
- View all notification templates grouped by dispatcher (Email / In-App)
- Edit template content (subject, title, body)
- Use template variables (
{{.UserName}},{{.DrawName}}) in the content
Navigate to Notifications in the admin sidebar.
Permissions
| Role | Permissions |
|---|---|
| Admin | Read + Write notifications |
| Editor | Read + Write notifications |
| Reviewer | Read notifications only |