first commit

This commit is contained in:
Michael Dong
2026-02-05 11:24:40 +08:00
commit a98e12f286
144 changed files with 26459 additions and 0 deletions

View File

@@ -0,0 +1,22 @@
[package]
edition = "2024"
name = "migration"
publish = false
version = "0.1.0"
[lib]
name = "migration"
path = "src/lib.rs"
[dependencies]
tokio = { version = "1", features = ["macros", "rt", "rt-multi-thread"] }
[dependencies.sea-orm-migration]
features = [
# Enable at least one `ASYNC_RUNTIME` and `DATABASE_DRIVER` feature if you want to run migration via CLI.
# View the list of supported features at https://www.sea-ql.org/SeaORM/docs/install-and-config/database-and-async-runtime.
# e.g.
"runtime-tokio-rustls",
"sqlx-postgres",
]
version = "~2.0.0-rc"

View File

@@ -0,0 +1,47 @@
# Running Migrator CLI
- Generate a new migration file
```sh
cargo run -- generate MIGRATION_NAME
```
- Apply all pending migrations
```sh
cargo run
```
```sh
cargo run -- up
```
- Apply first 10 pending migrations
```sh
cargo run -- up -n 10
```
- Rollback last applied migrations
```sh
cargo run -- down
```
- Rollback last 10 applied migrations
```sh
cargo run -- down -n 10
```
- Drop all tables from the database, then reapply all migrations
```sh
cargo run -- fresh
```
- Rollback all applied migrations, then reapply all migrations
```sh
cargo run -- refresh
```
- Rollback all applied migrations
```sh
cargo run -- reset
```
- Check the status of all migrations
```sh
cargo run -- status
```
- Generate entity from sea-orm-cli
```sh
sea-orm-cli generate entity --database-url postgres://dyc:dycdyc89@192.168.150.142/notify --output-dir ./src/entity --entity-format dense
```

View File

@@ -0,0 +1,42 @@
pub use sea_orm_migration::prelude::*;
mod m20220101_000001_create_user;
mod m20220101_000002_create_enums;
mod m20220101_000003_create_invite;
mod m20220101_000004_create_recurrence_rule;
mod m20220101_000005_create_todo;
mod m20220101_000006_create_reminder_task;
mod m20220101_000007_create_reminder_task_recipient;
mod m20220101_000008_create_reminder_offset;
mod m20220101_000009_create_notification;
mod m20220101_000010_create_delivery_log;
mod m20260128_000011_modify_todo;
mod m20260129_000012_add_bark_params;
mod m20260129_000013_add_notification_offset_id;
mod m20260129_000014_convert_timestamps_to_timestamptz;
mod m20260129_000015_add_user_invite_id;
pub struct Migrator;
#[async_trait::async_trait]
impl MigratorTrait for Migrator {
fn migrations() -> Vec<Box<dyn MigrationTrait>> {
vec![
Box::new(m20220101_000001_create_user::Migration),
Box::new(m20220101_000002_create_enums::Migration),
Box::new(m20220101_000003_create_invite::Migration),
Box::new(m20220101_000004_create_recurrence_rule::Migration),
Box::new(m20220101_000005_create_todo::Migration),
Box::new(m20220101_000006_create_reminder_task::Migration),
Box::new(m20220101_000007_create_reminder_task_recipient::Migration),
Box::new(m20220101_000008_create_reminder_offset::Migration),
Box::new(m20220101_000009_create_notification::Migration),
Box::new(m20220101_000010_create_delivery_log::Migration),
Box::new(m20260128_000011_modify_todo::Migration),
Box::new(m20260129_000012_add_bark_params::Migration),
Box::new(m20260129_000013_add_notification_offset_id::Migration),
Box::new(m20260129_000014_convert_timestamps_to_timestamptz::Migration),
Box::new(m20260129_000015_add_user_invite_id::Migration),
]
}
}

View File

@@ -0,0 +1,74 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(User::Table)
.if_not_exists()
.col(ColumnDef::new(User::Id).uuid().primary_key().not_null())
.col(
ColumnDef::new(User::Username)
.string()
.unique_key()
.not_null(),
)
.col(ColumnDef::new(User::PasswordHash).string().not_null())
.col(ColumnDef::new(User::Avatar).string().null())
.col(
ColumnDef::new(User::Timezone)
.string()
.not_null()
.default("Asia/Shanghai"),
)
.col(ColumnDef::new(User::BarkUrl).string().null())
.col(
ColumnDef::new(User::InappEnabled)
.boolean()
.not_null()
.default(true),
)
.col(
ColumnDef::new(User::BarkEnabled)
.boolean()
.not_null()
.default(false),
)
.col(
ColumnDef::new(User::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.col(ColumnDef::new(User::UpdatedAt).timestamp().not_null())
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table("User").to_owned())
.await
}
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
Username,
PasswordHash,
Avatar,
Timezone,
BarkUrl,
InappEnabled,
BarkEnabled,
CreatedAt,
UpdatedAt,
}

View File

@@ -0,0 +1,137 @@
use sea_orm_migration::prelude::*;
use sea_query::extension::postgres::Type;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Create RecurrenceType enum
manager
.create_type(
Type::create()
.as_enum(RecurrenceType::Type)
.values([
RecurrenceType::Hourly,
RecurrenceType::Daily,
RecurrenceType::Weekly,
RecurrenceType::Monthly,
RecurrenceType::Yearly,
])
.to_owned(),
)
.await?;
// Create TargetType enum
manager
.create_type(
Type::create()
.as_enum(TargetType::Type)
.values([TargetType::Todo, TargetType::ReminderTask])
.to_owned(),
)
.await?;
// Create ChannelType enum
manager
.create_type(
Type::create()
.as_enum(ChannelType::Type)
.values([ChannelType::Inapp, ChannelType::Bark])
.to_owned(),
)
.await?;
// Create NotificationStatus enum
manager
.create_type(
Type::create()
.as_enum(NotificationStatus::Type)
.values([
NotificationStatus::Pending,
NotificationStatus::Queued,
NotificationStatus::Sent,
NotificationStatus::Failed,
])
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_type(Type::drop().name(NotificationStatus::Type).to_owned())
.await?;
manager
.drop_type(Type::drop().name(ChannelType::Type).to_owned())
.await?;
manager
.drop_type(Type::drop().name(TargetType::Type).to_owned())
.await?;
manager
.drop_type(Type::drop().name(RecurrenceType::Type).to_owned())
.await?;
Ok(())
}
}
// RecurrenceType enum
#[derive(DeriveIden)]
pub enum RecurrenceType {
#[sea_orm(iden = "recurrence_type")]
Type,
#[sea_orm(iden = "hourly")]
Hourly,
#[sea_orm(iden = "daily")]
Daily,
#[sea_orm(iden = "weekly")]
Weekly,
#[sea_orm(iden = "monthly")]
Monthly,
#[sea_orm(iden = "yearly")]
Yearly,
}
// TargetType enum
#[derive(DeriveIden)]
pub enum TargetType {
#[sea_orm(iden = "target_type")]
Type,
#[sea_orm(iden = "todo")]
Todo,
#[sea_orm(iden = "reminder_task")]
ReminderTask,
}
// ChannelType enum
#[derive(DeriveIden)]
pub enum ChannelType {
#[sea_orm(iden = "channel_type")]
Type,
#[sea_orm(iden = "inapp")]
Inapp,
#[sea_orm(iden = "bark")]
Bark,
}
// NotificationStatus enum
#[derive(DeriveIden)]
pub enum NotificationStatus {
#[sea_orm(iden = "notification_status")]
Type,
#[sea_orm(iden = "pending")]
Pending,
#[sea_orm(iden = "queued")]
Queued,
#[sea_orm(iden = "sent")]
Sent,
#[sea_orm(iden = "failed")]
Failed,
}

View File

@@ -0,0 +1,102 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Invite::Table)
.if_not_exists()
.col(ColumnDef::new(Invite::Id).uuid().primary_key().not_null())
.col(
ColumnDef::new(Invite::Code)
.string()
.unique_key()
.not_null(),
)
.col(ColumnDef::new(Invite::CreatorId).uuid().not_null())
.col(
ColumnDef::new(Invite::MaxUses)
.integer()
.not_null()
.default(5),
)
.col(
ColumnDef::new(Invite::UsedCount)
.integer()
.not_null()
.default(0),
)
.col(ColumnDef::new(Invite::ExpiresAt).timestamp().not_null())
.col(ColumnDef::new(Invite::RevokedAt).timestamp().null())
.col(
ColumnDef::new(Invite::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.foreign_key(
ForeignKey::create()
.name("FK_invite_creator")
.from(Invite::Table, Invite::CreatorId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create indexes
manager
.create_index(
Index::create()
.name("IDX_invite_creator_id")
.table(Invite::Table)
.col(Invite::CreatorId)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("IDX_invite_expires_at")
.table(Invite::Table)
.col(Invite::ExpiresAt)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Invite::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Invite {
Table,
Id,
Code,
CreatorId,
MaxUses,
UsedCount,
ExpiresAt,
RevokedAt,
CreatedAt,
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
}

View File

@@ -0,0 +1,75 @@
use sea_orm_migration::prelude::*;
use crate::m20220101_000002_create_enums::RecurrenceType;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(RecurrenceRule::Table)
.if_not_exists()
.col(
ColumnDef::new(RecurrenceRule::Id)
.uuid()
.primary_key()
.not_null(),
)
.col(
ColumnDef::new(RecurrenceRule::Type)
.custom(RecurrenceType::Type)
.not_null(),
)
.col(
ColumnDef::new(RecurrenceRule::Interval)
.integer()
.not_null()
.default(1),
)
.col(ColumnDef::new(RecurrenceRule::ByWeekday).integer().null())
.col(ColumnDef::new(RecurrenceRule::ByMonthday).integer().null())
.col(
ColumnDef::new(RecurrenceRule::Timezone)
.string()
.not_null()
.default("Asia/Shanghai"),
)
.col(
ColumnDef::new(RecurrenceRule::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.col(
ColumnDef::new(RecurrenceRule::UpdatedAt)
.timestamp()
.not_null(),
)
.to_owned(),
)
.await
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(RecurrenceRule::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum RecurrenceRule {
Table,
Id,
Type,
Interval,
ByWeekday,
ByMonthday,
Timezone,
CreatedAt,
UpdatedAt,
}

View File

@@ -0,0 +1,102 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Todo::Table)
.if_not_exists()
.col(ColumnDef::new(Todo::Id).uuid().primary_key().not_null())
.col(ColumnDef::new(Todo::OwnerId).uuid().not_null())
.col(ColumnDef::new(Todo::Title).string().not_null())
.col(ColumnDef::new(Todo::Description).string().null())
.col(ColumnDef::new(Todo::DueAt).timestamp().not_null())
.col(ColumnDef::new(Todo::RecurrenceRuleId).uuid().null())
.col(
ColumnDef::new(Todo::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.col(ColumnDef::new(Todo::UpdatedAt).timestamp().not_null())
.foreign_key(
ForeignKey::create()
.name("FK_todo_owner")
.from(Todo::Table, Todo::OwnerId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("FK_todo_recurrence_rule")
.from(Todo::Table, Todo::RecurrenceRuleId)
.to(RecurrenceRule::Table, RecurrenceRule::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create indexes
manager
.create_index(
Index::create()
.name("IDX_todo_owner_due")
.table(Todo::Table)
.col(Todo::OwnerId)
.col(Todo::DueAt)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("IDX_todo_recurrence_rule_id")
.table(Todo::Table)
.col(Todo::RecurrenceRuleId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Todo::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Todo {
Table,
Id,
OwnerId,
Title,
Description,
DueAt,
RecurrenceRuleId,
CreatedAt,
UpdatedAt,
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
}
#[derive(DeriveIden)]
enum RecurrenceRule {
Table,
Id,
}

View File

@@ -0,0 +1,111 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ReminderTask::Table)
.if_not_exists()
.col(
ColumnDef::new(ReminderTask::Id)
.uuid()
.primary_key()
.not_null(),
)
.col(ColumnDef::new(ReminderTask::CreatorId).uuid().not_null())
.col(ColumnDef::new(ReminderTask::Title).string().not_null())
.col(ColumnDef::new(ReminderTask::Description).string().null())
.col(ColumnDef::new(ReminderTask::DueAt).timestamp().not_null())
.col(ColumnDef::new(ReminderTask::RecurrenceRuleId).uuid().null())
.col(
ColumnDef::new(ReminderTask::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.col(
ColumnDef::new(ReminderTask::UpdatedAt)
.timestamp()
.not_null(),
)
.foreign_key(
ForeignKey::create()
.name("FK_reminder_task_creator")
.from(ReminderTask::Table, ReminderTask::CreatorId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("FK_reminder_task_recurrence_rule")
.from(ReminderTask::Table, ReminderTask::RecurrenceRuleId)
.to(RecurrenceRule::Table, RecurrenceRule::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create indexes
manager
.create_index(
Index::create()
.name("IDX_reminder_task_creator_due")
.table(ReminderTask::Table)
.col(ReminderTask::CreatorId)
.col(ReminderTask::DueAt)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("IDX_reminder_task_recurrence_rule_id")
.table(ReminderTask::Table)
.col(ReminderTask::RecurrenceRuleId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ReminderTask::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum ReminderTask {
Table,
Id,
CreatorId,
Title,
Description,
DueAt,
RecurrenceRuleId,
CreatedAt,
UpdatedAt,
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
}
#[derive(DeriveIden)]
enum RecurrenceRule {
Table,
Id,
}

View File

@@ -0,0 +1,87 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ReminderTaskRecipient::Table)
.if_not_exists()
.col(
ColumnDef::new(ReminderTaskRecipient::TaskId)
.uuid()
.not_null(),
)
.col(
ColumnDef::new(ReminderTaskRecipient::UserId)
.uuid()
.not_null(),
)
.primary_key(
Index::create()
.col(ReminderTaskRecipient::TaskId)
.col(ReminderTaskRecipient::UserId),
)
.foreign_key(
ForeignKey::create()
.name("FK_reminder_task_recipient_task")
.from(ReminderTaskRecipient::Table, ReminderTaskRecipient::TaskId)
.to(ReminderTask::Table, ReminderTask::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.foreign_key(
ForeignKey::create()
.name("FK_reminder_task_recipient_user")
.from(ReminderTaskRecipient::Table, ReminderTaskRecipient::UserId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create index on user_id for reverse lookups
manager
.create_index(
Index::create()
.name("IDX_reminder_task_recipient_user_id")
.table(ReminderTaskRecipient::Table)
.col(ReminderTaskRecipient::UserId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ReminderTaskRecipient::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum ReminderTaskRecipient {
Table,
TaskId,
UserId,
}
#[derive(DeriveIden)]
enum ReminderTask {
Table,
Id,
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
}

View File

@@ -0,0 +1,87 @@
use sea_orm_migration::prelude::*;
use crate::m20220101_000002_create_enums::TargetType;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(ReminderOffset::Table)
.if_not_exists()
.col(
ColumnDef::new(ReminderOffset::Id)
.uuid()
.primary_key()
.not_null(),
)
.col(
ColumnDef::new(ReminderOffset::TargetType)
.custom(TargetType::Type)
.not_null(),
)
.col(ColumnDef::new(ReminderOffset::TargetId).uuid().not_null())
.col(
ColumnDef::new(ReminderOffset::OffsetMinutes)
.integer()
.not_null(),
)
.col(
ColumnDef::new(ReminderOffset::ChannelInapp)
.boolean()
.not_null()
.default(true),
)
.col(
ColumnDef::new(ReminderOffset::ChannelBark)
.boolean()
.not_null()
.default(false),
)
.col(
ColumnDef::new(ReminderOffset::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.to_owned(),
)
.await?;
// Create index for polymorphic lookup
manager
.create_index(
Index::create()
.name("IDX_reminder_offset_target")
.table(ReminderOffset::Table)
.col(ReminderOffset::TargetType)
.col(ReminderOffset::TargetId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(ReminderOffset::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum ReminderOffset {
Table,
Id,
TargetType,
TargetId,
OffsetMinutes,
ChannelInapp,
ChannelBark,
CreatedAt,
}

View File

@@ -0,0 +1,141 @@
use sea_orm_migration::prelude::*;
use crate::m20220101_000002_create_enums::{ChannelType, NotificationStatus, TargetType};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(Notification::Table)
.if_not_exists()
.col(
ColumnDef::new(Notification::Id)
.uuid()
.primary_key()
.not_null(),
)
.col(ColumnDef::new(Notification::RecipientId).uuid().not_null())
.col(
ColumnDef::new(Notification::TargetType)
.custom(TargetType::Type)
.not_null(),
)
.col(ColumnDef::new(Notification::TargetId).uuid().not_null())
.col(
ColumnDef::new(Notification::TriggerAt)
.timestamp()
.not_null(),
)
.col(
ColumnDef::new(Notification::Channel)
.custom(ChannelType::Type)
.not_null(),
)
.col(
ColumnDef::new(Notification::Status)
.custom(NotificationStatus::Type)
.not_null()
.default("pending"),
)
.col(ColumnDef::new(Notification::LockedAt).timestamp().null())
.col(ColumnDef::new(Notification::SentAt).timestamp().null())
.col(ColumnDef::new(Notification::ReadAt).timestamp().null())
.col(
ColumnDef::new(Notification::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.col(
ColumnDef::new(Notification::UpdatedAt)
.timestamp()
.not_null(),
)
.foreign_key(
ForeignKey::create()
.name("FK_notification_recipient")
.from(Notification::Table, Notification::RecipientId)
.to(User::Table, User::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create unique constraint
manager
.create_index(
Index::create()
.name("UQ_notification_recipient_target_trigger_channel")
.table(Notification::Table)
.col(Notification::RecipientId)
.col(Notification::TargetType)
.col(Notification::TargetId)
.col(Notification::TriggerAt)
.col(Notification::Channel)
.unique()
.to_owned(),
)
.await?;
// Create indexes
manager
.create_index(
Index::create()
.name("IDX_notification_status_trigger")
.table(Notification::Table)
.col(Notification::Status)
.col(Notification::TriggerAt)
.to_owned(),
)
.await?;
manager
.create_index(
Index::create()
.name("IDX_notification_recipient_read")
.table(Notification::Table)
.col(Notification::RecipientId)
.col(Notification::ReadAt)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(Notification::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum Notification {
Table,
Id,
RecipientId,
TargetType,
TargetId,
TriggerAt,
Channel,
Status,
LockedAt,
SentAt,
ReadAt,
CreatedAt,
UpdatedAt,
}
#[derive(DeriveIden)]
enum User {
Table,
Id,
}

View File

@@ -0,0 +1,94 @@
use sea_orm_migration::prelude::*;
use crate::m20220101_000002_create_enums::{ChannelType, NotificationStatus};
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.create_table(
Table::create()
.table(DeliveryLog::Table)
.if_not_exists()
.col(
ColumnDef::new(DeliveryLog::Id)
.uuid()
.primary_key()
.not_null(),
)
.col(
ColumnDef::new(DeliveryLog::NotificationId)
.uuid()
.not_null(),
)
.col(ColumnDef::new(DeliveryLog::AttemptNo).integer().not_null())
.col(
ColumnDef::new(DeliveryLog::Channel)
.custom(ChannelType::Type)
.not_null(),
)
.col(
ColumnDef::new(DeliveryLog::Status)
.custom(NotificationStatus::Type)
.not_null(),
)
.col(ColumnDef::new(DeliveryLog::ResponseMeta).json_binary().null())
.col(
ColumnDef::new(DeliveryLog::CreatedAt)
.timestamp()
.not_null()
.extra("DEFAULT NOW()"),
)
.foreign_key(
ForeignKey::create()
.name("FK_delivery_log_notification")
.from(DeliveryLog::Table, DeliveryLog::NotificationId)
.to(Notification::Table, Notification::Id)
.on_delete(ForeignKeyAction::Cascade)
.on_update(ForeignKeyAction::Cascade),
)
.to_owned(),
)
.await?;
// Create index
manager
.create_index(
Index::create()
.name("IDX_delivery_log_notification_id")
.table(DeliveryLog::Table)
.col(DeliveryLog::NotificationId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.drop_table(Table::drop().table(DeliveryLog::Table).to_owned())
.await
}
}
#[derive(DeriveIden)]
enum DeliveryLog {
Table,
Id,
NotificationId,
AttemptNo,
Channel,
Status,
ResponseMeta,
CreatedAt,
}
#[derive(DeriveIden)]
enum Notification {
Table,
Id,
}

View File

@@ -0,0 +1,38 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Todo::Table)
.add_column(ColumnDef::new(Todo::CheckInAt).timestamp().null())
.add_column(
ColumnDef::new(Todo::CheckInCount)
.integer()
.not_null()
.default(0),
)
.add_column(
ColumnDef::new(Todo::IsCheckedIn)
.boolean()
.not_null()
.default(false),
)
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(DeriveIden)]
enum Todo {
Table,
CheckInAt,
CheckInCount,
IsCheckedIn,
}

View File

@@ -0,0 +1,49 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(ReminderOffset::Table)
.add_column(ColumnDef::new(ReminderOffset::BarkTitle).string().null())
.add_column(ColumnDef::new(ReminderOffset::BarkSubtitle).string().null())
.add_column(ColumnDef::new(ReminderOffset::BarkBodyMarkdown).text().null())
.add_column(ColumnDef::new(ReminderOffset::BarkLevel).string().null())
.add_column(ColumnDef::new(ReminderOffset::BarkIcon).string().null())
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(ReminderOffset::Table)
.drop_column(ReminderOffset::BarkTitle)
.drop_column(ReminderOffset::BarkSubtitle)
.drop_column(ReminderOffset::BarkBodyMarkdown)
.drop_column(ReminderOffset::BarkLevel)
.drop_column(ReminderOffset::BarkIcon)
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(DeriveIden)]
enum ReminderOffset {
Table,
BarkTitle,
BarkSubtitle,
BarkBodyMarkdown,
BarkLevel,
BarkIcon,
}

View File

@@ -0,0 +1,37 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Notification::Table)
.add_column(ColumnDef::new(Notification::OffsetId).uuid().null())
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
manager
.alter_table(
Table::alter()
.table(Notification::Table)
.drop_column(Notification::OffsetId)
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(DeriveIden)]
enum Notification {
Table,
OffsetId,
}

View File

@@ -0,0 +1,143 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
// User table: created_at, updated_at
db.execute_unprepared(
"ALTER TABLE \"user\"
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Invite table: expires_at, revoked_at, created_at
db.execute_unprepared(
"ALTER TABLE invite
ALTER COLUMN expires_at TYPE TIMESTAMP WITH TIME ZONE USING expires_at AT TIME ZONE 'UTC',
ALTER COLUMN revoked_at TYPE TIMESTAMP WITH TIME ZONE USING revoked_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC'"
).await?;
// RecurrenceRule table: created_at, updated_at
db.execute_unprepared(
"ALTER TABLE recurrence_rule
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Todo table: due_at, created_at, updated_at, check_in_at
db.execute_unprepared(
"ALTER TABLE todo
ALTER COLUMN due_at TYPE TIMESTAMP WITH TIME ZONE USING due_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE USING updated_at AT TIME ZONE 'UTC',
ALTER COLUMN check_in_at TYPE TIMESTAMP WITH TIME ZONE USING check_in_at AT TIME ZONE 'UTC'"
).await?;
// ReminderTask table: due_at, created_at, updated_at
db.execute_unprepared(
"ALTER TABLE reminder_task
ALTER COLUMN due_at TYPE TIMESTAMP WITH TIME ZONE USING due_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE USING updated_at AT TIME ZONE 'UTC'"
).await?;
// ReminderOffset table: created_at
db.execute_unprepared(
"ALTER TABLE reminder_offset
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC'"
).await?;
// Notification table: trigger_at, locked_at, sent_at, read_at, created_at, updated_at
db.execute_unprepared(
"ALTER TABLE notification
ALTER COLUMN trigger_at TYPE TIMESTAMP WITH TIME ZONE USING trigger_at AT TIME ZONE 'UTC',
ALTER COLUMN locked_at TYPE TIMESTAMP WITH TIME ZONE USING locked_at AT TIME ZONE 'UTC',
ALTER COLUMN sent_at TYPE TIMESTAMP WITH TIME ZONE USING sent_at AT TIME ZONE 'UTC',
ALTER COLUMN read_at TYPE TIMESTAMP WITH TIME ZONE USING read_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP WITH TIME ZONE USING updated_at AT TIME ZONE 'UTC'"
).await?;
// DeliveryLog table: created_at
db.execute_unprepared(
"ALTER TABLE delivery_log
ALTER COLUMN created_at TYPE TIMESTAMP WITH TIME ZONE USING created_at AT TIME ZONE 'UTC'"
).await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
let db = manager.get_connection();
// Revert User table
db.execute_unprepared(
"ALTER TABLE \"user\"
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Revert Invite table
db.execute_unprepared(
"ALTER TABLE invite
ALTER COLUMN expires_at TYPE TIMESTAMP USING expires_at AT TIME ZONE 'UTC',
ALTER COLUMN revoked_at TYPE TIMESTAMP USING revoked_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC'"
).await?;
// Revert RecurrenceRule table
db.execute_unprepared(
"ALTER TABLE recurrence_rule
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Revert Todo table
db.execute_unprepared(
"ALTER TABLE todo
ALTER COLUMN due_at TYPE TIMESTAMP USING due_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP USING updated_at AT TIME ZONE 'UTC',
ALTER COLUMN check_in_at TYPE TIMESTAMP USING check_in_at AT TIME ZONE 'UTC'"
).await?;
// Revert ReminderTask table
db.execute_unprepared(
"ALTER TABLE reminder_task
ALTER COLUMN due_at TYPE TIMESTAMP USING due_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Revert ReminderOffset table
db.execute_unprepared(
"ALTER TABLE reminder_offset
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC'"
).await?;
// Revert Notification table
db.execute_unprepared(
"ALTER TABLE notification
ALTER COLUMN trigger_at TYPE TIMESTAMP USING trigger_at AT TIME ZONE 'UTC',
ALTER COLUMN locked_at TYPE TIMESTAMP USING locked_at AT TIME ZONE 'UTC',
ALTER COLUMN sent_at TYPE TIMESTAMP USING sent_at AT TIME ZONE 'UTC',
ALTER COLUMN read_at TYPE TIMESTAMP USING read_at AT TIME ZONE 'UTC',
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC',
ALTER COLUMN updated_at TYPE TIMESTAMP USING updated_at AT TIME ZONE 'UTC'"
).await?;
// Revert DeliveryLog table
db.execute_unprepared(
"ALTER TABLE delivery_log
ALTER COLUMN created_at TYPE TIMESTAMP USING created_at AT TIME ZONE 'UTC'"
).await?;
Ok(())
}
}

View File

@@ -0,0 +1,86 @@
use sea_orm_migration::prelude::*;
#[derive(DeriveMigrationName)]
pub struct Migration;
#[async_trait::async_trait]
impl MigrationTrait for Migration {
async fn up(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Add invite_id column to user table
manager
.alter_table(
Table::alter()
.table(User::Table)
.add_column(ColumnDef::new(User::InviteId).uuid().null())
.to_owned(),
)
.await?;
// Add foreign key constraint
manager
.create_foreign_key(
ForeignKey::create()
.name("fk_user_invite_id")
.from(User::Table, User::InviteId)
.to(Invite::Table, Invite::Id)
.on_delete(ForeignKeyAction::SetNull)
.on_update(ForeignKeyAction::Cascade)
.to_owned(),
)
.await?;
// Add index for better query performance
manager
.create_index(
Index::create()
.name("idx_user_invite_id")
.table(User::Table)
.col(User::InviteId)
.to_owned(),
)
.await?;
Ok(())
}
async fn down(&self, manager: &SchemaManager) -> Result<(), DbErr> {
// Drop index
manager
.drop_index(Index::drop().name("idx_user_invite_id").to_owned())
.await?;
// Drop foreign key
manager
.drop_foreign_key(
ForeignKey::drop()
.table(User::Table)
.name("fk_user_invite_id")
.to_owned(),
)
.await?;
// Drop column
manager
.alter_table(
Table::alter()
.table(User::Table)
.drop_column(User::InviteId)
.to_owned(),
)
.await?;
Ok(())
}
}
#[derive(DeriveIden)]
enum User {
Table,
InviteId,
}
#[derive(DeriveIden)]
enum Invite {
Table,
Id,
}

View File

@@ -0,0 +1,6 @@
use sea_orm_migration::prelude::*;
#[tokio::main]
async fn main() {
cli::run_cli(migration::Migrator).await;
}