diff --git a/internal/entities/entities.go b/internal/entities/entities.go index e6eb8ee..c8b5b38 100644 --- a/internal/entities/entities.go +++ b/internal/entities/entities.go @@ -37,6 +37,10 @@ func GetAllEntities() []interface{} { &OtpSession{}, // Analytics entities are not database tables, they are query results &UserDevice{}, + // Notification entities + &Notification{}, + &NotificationReceiver{}, + &NotificationDelivery{}, } } diff --git a/migrations/000064_create_notifications_table.down.sql b/migrations/000064_create_notifications_table.down.sql new file mode 100644 index 0000000..d5d18e1 --- /dev/null +++ b/migrations/000064_create_notifications_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS notifications; diff --git a/migrations/000064_create_notifications_table.up.sql b/migrations/000064_create_notifications_table.up.sql new file mode 100644 index 0000000..d20da42 --- /dev/null +++ b/migrations/000064_create_notifications_table.up.sql @@ -0,0 +1,28 @@ +-- Notifications table (master notification record) +CREATE TABLE notifications ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + title VARCHAR(255) NOT NULL, + body TEXT, + type VARCHAR(100), + category VARCHAR(100), + priority VARCHAR(50) NOT NULL DEFAULT 'normal' CHECK (priority IN ('low', 'normal', 'high')), + image_url VARCHAR(512), + action_url VARCHAR(512), + notifiable_type VARCHAR(100), + notifiable_id UUID, + data JSONB, + scheduled_at TIMESTAMP WITH TIME ZONE, + sent_at TIMESTAMP WITH TIME ZONE, + expired_at TIMESTAMP WITH TIME ZONE, + created_by UUID REFERENCES users(id) ON DELETE SET NULL, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Indexes +CREATE INDEX idx_notifications_created_by ON notifications(created_by); +CREATE INDEX idx_notifications_type ON notifications(type); +CREATE INDEX idx_notifications_category ON notifications(category); +CREATE INDEX idx_notifications_notifiable ON notifications(notifiable_type, notifiable_id); +CREATE INDEX idx_notifications_scheduled_at ON notifications(scheduled_at); +CREATE INDEX idx_notifications_sent_at ON notifications(sent_at); diff --git a/migrations/000065_create_notification_receivers_table.down.sql b/migrations/000065_create_notification_receivers_table.down.sql new file mode 100644 index 0000000..e148f2d --- /dev/null +++ b/migrations/000065_create_notification_receivers_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS notification_receivers; diff --git a/migrations/000065_create_notification_receivers_table.up.sql b/migrations/000065_create_notification_receivers_table.up.sql new file mode 100644 index 0000000..995bd34 --- /dev/null +++ b/migrations/000065_create_notification_receivers_table.up.sql @@ -0,0 +1,18 @@ +-- Notification receivers table (links a notification to a specific user) +CREATE TABLE notification_receivers ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + notification_id UUID NOT NULL REFERENCES notifications(id) ON DELETE CASCADE, + user_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE, + is_read BOOLEAN NOT NULL DEFAULT FALSE, + read_at TIMESTAMP WITH TIME ZONE, + is_deleted BOOLEAN NOT NULL DEFAULT FALSE, + deleted_at TIMESTAMP WITH TIME ZONE, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Indexes +CREATE INDEX idx_notification_receivers_notification_id ON notification_receivers(notification_id); +CREATE INDEX idx_notification_receivers_user_id ON notification_receivers(user_id); +CREATE INDEX idx_notification_receivers_user_unread ON notification_receivers(user_id, is_read) WHERE is_deleted = FALSE; +CREATE UNIQUE INDEX idx_notification_receivers_unique ON notification_receivers(notification_id, user_id); diff --git a/migrations/000066_create_notification_deliveries_table.down.sql b/migrations/000066_create_notification_deliveries_table.down.sql new file mode 100644 index 0000000..cf6dd18 --- /dev/null +++ b/migrations/000066_create_notification_deliveries_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS notification_deliveries; diff --git a/migrations/000066_create_notification_deliveries_table.up.sql b/migrations/000066_create_notification_deliveries_table.up.sql new file mode 100644 index 0000000..fd8904c --- /dev/null +++ b/migrations/000066_create_notification_deliveries_table.up.sql @@ -0,0 +1,23 @@ +-- Notification deliveries table (tracks per-device delivery attempts) +CREATE TABLE notification_deliveries ( + id UUID PRIMARY KEY DEFAULT gen_random_uuid(), + notification_receiver_id UUID NOT NULL REFERENCES notification_receivers(id) ON DELETE CASCADE, + user_device_id UUID NOT NULL REFERENCES user_devices(id) ON DELETE CASCADE, + channel VARCHAR(50) NOT NULL DEFAULT 'push' CHECK (channel IN ('push', 'websocket', 'email')), + delivery_status VARCHAR(50) NOT NULL DEFAULT 'pending' CHECK (delivery_status IN ('pending', 'sent', 'delivered', 'failed')), + provider VARCHAR(50) CHECK (provider IN ('firebase', 'onesignal')), + provider_message_id VARCHAR(255), + sent_at TIMESTAMP WITH TIME ZONE, + delivered_at TIMESTAMP WITH TIME ZONE, + failed_at TIMESTAMP WITH TIME ZONE, + failure_reason TEXT, + retry_count INT NOT NULL DEFAULT 0, + created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW(), + updated_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() +); + +-- Indexes +CREATE INDEX idx_notification_deliveries_receiver_id ON notification_deliveries(notification_receiver_id); +CREATE INDEX idx_notification_deliveries_device_id ON notification_deliveries(user_device_id); +CREATE INDEX idx_notification_deliveries_status ON notification_deliveries(delivery_status); +CREATE INDEX idx_notification_deliveries_provider ON notification_deliveries(provider);