use migration::{Migrator, MigratorTrait}; use sea_orm::{ConnectOptions, Database, DbConn}; use std::env; use std::path::PathBuf; use tokio::sync::mpsc; use tracing::info; use crate::timer::{NotificationWorker, SharedTimeWheel, WorkerCommand}; #[derive(Clone)] pub struct AppData { pub db: DbConn, pub jwt_secret: String, pub worker_tx: mpsc::Sender, /// 服务器基础 URL,用于生成头像等资源的完整 URL /// 本地调试: http://localhost:4000 /// 生产环境: https://notify.michaelandmeryl.xyz pub base_url: String, /// 上传文件的存储目录 pub upload_dir: PathBuf, } impl AppData { pub async fn new() -> Result { let url = env::var("DATABASE_URL") .unwrap_or_else(|_| "postgres://postgres:postgres@localhost:5432/notify".to_string()); let mut opt = ConnectOptions::new(url); opt.max_connections(10).sqlx_logging(false); let db = Database::connect(opt).await?; // 自动运行数据库迁移 info!("Running database migrations..."); Migrator::up(&db, None).await?; info!("Database migrations completed."); let jwt_secret = env::var("JWT_SECRET").unwrap_or_else(|_| "dev-secret".to_string()); // 从环境变量读取 BASE_URL,默认为本地开发地址 let base_url = env::var("BASE_URL").unwrap_or_else(|_| "http://localhost:4000".to_string()); // 上传目录,默认为当前目录下的 uploads let upload_dir = env::var("UPLOAD_DIR") .map(PathBuf::from) .unwrap_or_else(|_| PathBuf::from("./uploads")); // 确保上传目录存在 tokio::fs::create_dir_all(&upload_dir).await?; tokio::fs::create_dir_all(upload_dir.join("avatars")).await?; // 创建并启动时间轮 Worker let time_wheel = SharedTimeWheel::new(); let worker = NotificationWorker::new(db.clone(), time_wheel); let worker_tx = worker.start().await; Ok(Self { db, jwt_secret, worker_tx, base_url, upload_dir, }) } /// 发送命令给 Worker pub async fn send_worker_command( &self, cmd: WorkerCommand, ) -> Result<(), mpsc::error::SendError> { self.worker_tx.send(cmd).await } }