feat: v0.3.0 — standardize codebase, centralize infra, merge QR into CMS
- Enforce axum best practices across all 13 workspace crates (max 200 LOC/file, no comments, no unwrap, clean architecture) - Fix domain→infrastructure dependency inversions in imphnen-iam and imphnen-dimentorin - Extract imphnen-storage (MinIO) and imphnen-email (Lettre) as standalone crates - Centralize all config in ENV struct: CDN_URL, CORS_ALLOWED_ORIGINS - Centralize SMTP through imphnen-email; remove dead HackathonConfig - Centralize database: QR crate now shares main DB pool (single DATABASE_URL) - Rename QR users table to qr_users to avoid collision with main users table - Merge imphnen-qr into imphnen-cms/src/qr (13 crates, down from 14) - Restructure imphnen-hackathon flat modules into clean architecture - Remove all stale env vars from .env.example (SurrealDB, QR_JWT, Hackathon infra) - Fix Dockerfile to include all current workspace crates - Bump all crate versions 0.2.0 → 0.3.0 Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
2ae43b3bcc
commit
331a4a4e88
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "imphnen-backend"
|
||||
version = "0.2.0"
|
||||
version = "0.3.0"
|
||||
edition = "2021"
|
||||
|
||||
[[bin]]
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
// API entry point using PostgreSQL (SurrealDB migration complete)
|
||||
// This file has been updated to use SeaORM with PostgreSQL instead of SurrealDB
|
||||
use imphnen_gateway::gateway_service;
|
||||
use imphnen_libs::axum_init;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
axum_init(|postgres_db| async {
|
||||
// Gateway service now uses PostgreSQL exclusively (SeaORM)
|
||||
// SurrealDB dependencies have been completely removed
|
||||
gateway_service(postgres_db).await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
use imphnen_gateway::gateway_service;
|
||||
use imphnen_libs::axum_init;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
axum_init(|postgres_db| async {
|
||||
gateway_service(postgres_db).await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
|
||||
@@ -1,93 +1,91 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::{Statement, ConnectionTrait};
|
||||
use std::error::Error;
|
||||
use std::env;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
// New default behavior: execute by default; use --dry-run to preview only.
|
||||
let dry_run = args.iter().any(|s| s == "--dry-run" || s == "--no-exec" || s == "--dry");
|
||||
let force = args.iter().any(|s| s == "--force" || s == "-f");
|
||||
|
||||
println!("🔎 Clear DB script - WARNING: This will remove data from tables\n");
|
||||
println!("Note: script now runs by default (no --yes required). To preview without executing, use --dry-run.\n");
|
||||
|
||||
// List of tables to truncate (order doesn't matter with CASCADE)
|
||||
let tables = vec![
|
||||
"gacha_claims",
|
||||
"gacha_rolls",
|
||||
"gacha_items",
|
||||
"gacha_credits",
|
||||
"audit_logs",
|
||||
"rate_limits",
|
||||
"testimonials",
|
||||
"events",
|
||||
"app_mentors",
|
||||
"app_sessions",
|
||||
"app_roles_permissions",
|
||||
"app_permissions",
|
||||
"app_roles",
|
||||
"app_users",
|
||||
];
|
||||
|
||||
let postgres_config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(postgres_config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
// Filter tables that actually exist in the database
|
||||
let mut existing_tables: Vec<&str> = vec![];
|
||||
for t in tables.iter() {
|
||||
let check_sql = format!(
|
||||
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '{}') as exists;",
|
||||
t
|
||||
);
|
||||
let stmt = Statement::from_string(db.get_database_backend(), check_sql);
|
||||
if let Ok(Some(row)) = pg_conn.query_one(stmt).await {
|
||||
let exists_val: Option<bool> = row.try_get("", "exists").ok();
|
||||
if exists_val.unwrap_or(false) {
|
||||
existing_tables.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if existing_tables.is_empty() {
|
||||
println!("No configured tables found to clear - nothing to do.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let truncate_sql = format!(
|
||||
"TRUNCATE TABLE {} RESTART IDENTITY CASCADE;",
|
||||
existing_tables.join(", ")
|
||||
);
|
||||
|
||||
println!("The script will run the following SQL (on the DB configured by env vars):\n\n{}", truncate_sql);
|
||||
|
||||
// Prevent accidental execution in production without explicit force flag
|
||||
let env_name = std::env::var("RUST_ENV").unwrap_or_else(|_| "development".to_string());
|
||||
if env_name == "production" && !force {
|
||||
println!("Security: RUST_ENV=production; the script will NOT run without --force. Use --force to override.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
println!("Dry run enabled. No changes applied. To execute, re-run without --dry-run or use --force (in production).");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Executing truncate...\n");
|
||||
|
||||
let postgres_config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(postgres_config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
let stmt = Statement::from_string(db.get_database_backend(), truncate_sql);
|
||||
match pg_conn.execute(stmt).await {
|
||||
Ok(_) => println!("✅ Successfully cleared DB tables"),
|
||||
Err(e) => println!("❌ Failed to clear DB tables: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::{ConnectionTrait, Statement};
|
||||
use std::env;
|
||||
use std::error::Error;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
let dry_run = args
|
||||
.iter()
|
||||
.any(|s| s == "--dry-run" || s == "--no-exec" || s == "--dry");
|
||||
let force = args.iter().any(|s| s == "--force" || s == "-f");
|
||||
|
||||
println!("🔎 Clear DB script - WARNING: This will remove data from tables\n");
|
||||
println!("Note: script now runs by default (no --yes required). To preview without executing, use --dry-run.\n");
|
||||
|
||||
let tables = vec![
|
||||
"gacha_claims",
|
||||
"gacha_rolls",
|
||||
"gacha_items",
|
||||
"gacha_credits",
|
||||
"audit_logs",
|
||||
"rate_limits",
|
||||
"testimonials",
|
||||
"events",
|
||||
"app_mentors",
|
||||
"app_sessions",
|
||||
"app_roles_permissions",
|
||||
"app_permissions",
|
||||
"app_roles",
|
||||
"app_users",
|
||||
];
|
||||
|
||||
let postgres_config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(postgres_config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
let mut existing_tables: Vec<&str> = vec![];
|
||||
for t in tables.iter() {
|
||||
let check_sql = format!(
|
||||
"SELECT EXISTS (SELECT 1 FROM information_schema.tables WHERE table_schema = 'public' AND table_name = '{}') as exists;",
|
||||
t
|
||||
);
|
||||
let stmt = Statement::from_string(db.get_database_backend(), check_sql);
|
||||
if let Ok(Some(row)) = pg_conn.query_one(stmt).await {
|
||||
let exists_val: Option<bool> = row.try_get("", "exists").ok();
|
||||
if exists_val.unwrap_or(false) {
|
||||
existing_tables.push(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if existing_tables.is_empty() {
|
||||
println!("No configured tables found to clear - nothing to do.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let truncate_sql = format!(
|
||||
"TRUNCATE TABLE {} RESTART IDENTITY CASCADE;",
|
||||
existing_tables.join(", ")
|
||||
);
|
||||
|
||||
println!("The script will run the following SQL (on the DB configured by env vars):\n\n{}", truncate_sql);
|
||||
|
||||
let env_name = imphnen_libs::ENV.rust_env.clone();
|
||||
if env_name == "production" && !force {
|
||||
println!("Security: RUST_ENV=production; the script will NOT run without --force. Use --force to override.");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
if dry_run {
|
||||
println!("Dry run enabled. No changes applied. To execute, re-run without --dry-run or use --force (in production).");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
println!("Executing truncate...\n");
|
||||
|
||||
let postgres_config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(postgres_config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
let stmt = Statement::from_string(db.get_database_backend(), truncate_sql);
|
||||
match pg_conn.execute(stmt).await {
|
||||
Ok(_) => println!("✅ Successfully cleared DB tables"),
|
||||
Err(e) => println!("❌ Failed to clear DB tables: {}", e),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,65 +1,76 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use sea_orm::{ConnectionTrait, Database, Schema, DbBackend, EntityTrait};
|
||||
use imphnen_libs::postgres::PostgresConfig;
|
||||
use imphnen_entities::seaorm::{auth, common, gacha};
|
||||
use sea_orm::sea_query::Table;
|
||||
use imphnen_libs::postgres::PostgresConfig;
|
||||
use sea_orm::sea_query::Table;
|
||||
use sea_orm::{ConnectionTrait, Database, DbBackend, EntityTrait, Schema};
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🛠️ Creating database schema...");
|
||||
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let db = Database::connect(&config.database_url).await?;
|
||||
let builder = db.get_database_backend();
|
||||
println!("🛠️ Creating database schema...");
|
||||
|
||||
println!(" Database connected. Creating/updating tables...");
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let db = Database::connect(&config.database_url).await?;
|
||||
let builder = db.get_database_backend();
|
||||
|
||||
// Dropping and recreating tables to ensure schema is up-to-date
|
||||
// This is safer for development/testing environments to prevent schema drift.
|
||||
drop_and_create_table(&db, builder, "app_roles", auth::roles::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_permissions", auth::permissions::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_users", auth::users::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_roles_permissions", auth::roles_permissions::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_mentors", auth::mentors::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_sessions", auth::sessions::Entity).await?;
|
||||
|
||||
drop_and_create_table(&db, builder, "events", common::events::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "testimonials", common::testimonials::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "audit_logs", common::audit_log::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "rate_limits", common::rate_limit::Entity).await?;
|
||||
|
||||
drop_and_create_table(&db, builder, "gacha_credits", gacha::gacha_credits::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "gacha_items", gacha::gacha_items::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "gacha_rolls", gacha::gacha_rolls::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "gacha_claims", gacha::gacha_claims::Entity).await?;
|
||||
println!(" Database connected. Creating/updating tables...");
|
||||
|
||||
println!("✅ Schema creation completed.");
|
||||
Ok(())
|
||||
drop_and_create_table(&db, builder, "app_roles", auth::roles::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_permissions", auth::permissions::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "app_users", auth::users::Entity).await?;
|
||||
drop_and_create_table(
|
||||
&db,
|
||||
builder,
|
||||
"app_roles_permissions",
|
||||
auth::roles_permissions::Entity,
|
||||
)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "app_mentors", auth::mentors::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "app_sessions", auth::sessions::Entity)
|
||||
.await?;
|
||||
|
||||
drop_and_create_table(&db, builder, "events", common::events::Entity).await?;
|
||||
drop_and_create_table(&db, builder, "testimonials", common::testimonials::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "audit_logs", common::audit_log::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "rate_limits", common::rate_limit::Entity)
|
||||
.await?;
|
||||
|
||||
drop_and_create_table(&db, builder, "gacha_credits", gacha::gacha_credits::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "gacha_items", gacha::gacha_items::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "gacha_rolls", gacha::gacha_rolls::Entity)
|
||||
.await?;
|
||||
drop_and_create_table(&db, builder, "gacha_claims", gacha::gacha_claims::Entity)
|
||||
.await?;
|
||||
|
||||
println!("✅ Schema creation completed.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn drop_and_create_table<E>(
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
builder: DbBackend,
|
||||
name: &str,
|
||||
entity: E,
|
||||
) -> Result<(), Box<dyn std::error::Error>> // Return Result
|
||||
db: &sea_orm::DatabaseConnection,
|
||||
builder: DbBackend,
|
||||
name: &str,
|
||||
entity: E,
|
||||
) -> Result<(), Box<dyn std::error::Error>>
|
||||
where
|
||||
E: EntityTrait,
|
||||
E: EntityTrait,
|
||||
{
|
||||
let schema = Schema::new(builder);
|
||||
let schema = Schema::new(builder);
|
||||
|
||||
// Drop table if it exists
|
||||
let drop_stmt = Table::drop().table(entity).if_exists().cascade().to_owned(); // Added .cascade()
|
||||
db.execute(builder.build(&drop_stmt)).await?; // Propagate error
|
||||
println!(" Dropped table if exists: {}", name);
|
||||
let drop_stmt = Table::drop().table(entity).if_exists().cascade().to_owned();
|
||||
db.execute(builder.build(&drop_stmt)).await?;
|
||||
println!(" Dropped table if exists: {}", name);
|
||||
|
||||
// Create table
|
||||
let mut create_stmt = schema.create_table_from_entity(entity);
|
||||
create_stmt.if_not_exists();
|
||||
let mut create_stmt = schema.create_table_from_entity(entity);
|
||||
create_stmt.if_not_exists();
|
||||
|
||||
db.execute(builder.build(&create_stmt)).await?; // Propagate error
|
||||
println!(" ✅ Created table: {}", name);
|
||||
db.execute(builder.build(&create_stmt)).await?;
|
||||
println!(" ✅ Created table: {}", name);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,21 +1,20 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_libs::jsonwebtoken::encode_access_token;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: mk_token <email_or_sub>");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let sub = args[1].clone();
|
||||
// Use sub as both sub and user_id
|
||||
match encode_access_token(sub.clone(), sub.clone()) {
|
||||
Ok(token) => println!("{}", token),
|
||||
Err(e) => {
|
||||
eprintln!("Failed to generate token: {:?}", e);
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_libs::jsonwebtoken::encode_access_token;
|
||||
use std::env;
|
||||
|
||||
fn main() {
|
||||
let args: Vec<String> = env::args().collect();
|
||||
if args.len() < 2 {
|
||||
eprintln!("Usage: mk_token <email_or_sub>");
|
||||
std::process::exit(1);
|
||||
}
|
||||
let sub = args[1].clone();
|
||||
match encode_access_token(sub.clone(), sub.clone()) {
|
||||
Ok(token) => println!("{}", token),
|
||||
Err(e) => {
|
||||
eprintln!("Failed to generate token: {:?}", e);
|
||||
std::process::exit(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,15 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use std::error::Error;
|
||||
use chrono::Utc;
|
||||
use imphnen_entities::seaorm::common::events::{
|
||||
ActiveModel as EventsActiveModel, Entity as EventEntity,
|
||||
};
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::common::events::{ActiveModel as EventsActiveModel, Entity as EventEntity};
|
||||
use sea_orm::{ActiveValue::Set, ActiveModelTrait, EntityTrait, ColumnTrait, QueryFilter};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ActiveValue::Set, ColumnTrait, EntityTrait, QueryFilter,
|
||||
};
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc; // Removed NaiveDateTime as it was unused
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -54,7 +58,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
"2025-09-20T13:00:00Z",
|
||||
"2025-09-22T15:00:00Z",
|
||||
),
|
||||
// Additional Events
|
||||
(
|
||||
"Rust Programming Bootcamp",
|
||||
"Intensive 3-day bootcamp to master Rust fundamentals and advanced concepts.",
|
||||
@@ -154,18 +157,20 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
price,
|
||||
location,
|
||||
is_online,
|
||||
start_date_str, // Renamed to avoid conflict
|
||||
end_date_str, // Renamed to avoid conflict
|
||||
start_date_str,
|
||||
end_date_str,
|
||||
) in events
|
||||
{
|
||||
// Check if event already exists by name
|
||||
let existing = EventEntity::find().filter(<EventEntity as EntityTrait>::Column::Name.eq(name)).one(db).await?;
|
||||
let existing = EventEntity::find()
|
||||
.filter(<EventEntity as EntityTrait>::Column::Name.eq(name))
|
||||
.one(db)
|
||||
.await?;
|
||||
if existing.is_some() {
|
||||
println!("ℹ️ Skipping (already exists): {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
let uuid = Uuid::new_v4(); // Generate a Uuid
|
||||
let uuid = Uuid::new_v4();
|
||||
let mut event_model: EventsActiveModel = Default::default();
|
||||
event_model.id = Set(uuid);
|
||||
event_model.name = Set(name.to_string());
|
||||
@@ -174,12 +179,17 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
event_model.price = Set(price);
|
||||
event_model.is_online = Set(is_online);
|
||||
event_model.location = Set(location.clone());
|
||||
event_model.start_date = Set(chrono::DateTime::parse_from_rfc3339(start_date_str)?.with_timezone(&chrono::Utc));
|
||||
event_model.end_date = Set(chrono::DateTime::parse_from_rfc3339(end_date_str)?.with_timezone(&chrono::Utc));
|
||||
event_model.is_deleted = Set(false); // Explicitly set is_deleted
|
||||
event_model.created_at = Set(Utc::now()); // Explicitly set created_at
|
||||
event_model.updated_at = Set(Utc::now()); // Explicitly set updated_at
|
||||
|
||||
event_model.start_date = Set(
|
||||
chrono::DateTime::parse_from_rfc3339(start_date_str)?
|
||||
.with_timezone(&chrono::Utc),
|
||||
);
|
||||
event_model.end_date = Set(
|
||||
chrono::DateTime::parse_from_rfc3339(end_date_str)?
|
||||
.with_timezone(&chrono::Utc),
|
||||
);
|
||||
event_model.is_deleted = Set(false);
|
||||
event_model.created_at = Set(Utc::now());
|
||||
event_model.updated_at = Set(Utc::now());
|
||||
|
||||
event_model.insert(db).await?;
|
||||
|
||||
@@ -192,4 +202,4 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
|
||||
println!("✅ All Events seeded");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use std::error::Error;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::gacha::gacha_items::ActiveModel as GachaItemActiveModel;
|
||||
use imphnen_entities::seaorm::gacha::gacha_rolls::ActiveModel as GachaRollActiveModel;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use uuid::Uuid;
|
||||
use sea_orm::ConnectionTrait;
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -15,18 +15,25 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
// Check if gacha item already exists
|
||||
let check_item_sql = "SELECT id FROM app_gacha_items WHERE item_code = 'ITEM_TEST_1' LIMIT 1";
|
||||
let item_result = pg_conn.query_one(sea_orm::Statement::from_string(db.get_database_backend(), check_item_sql)).await?;
|
||||
let check_item_sql =
|
||||
"SELECT id FROM app_gacha_items WHERE item_code = 'ITEM_TEST_1' LIMIT 1";
|
||||
let item_result = pg_conn
|
||||
.query_one(sea_orm::Statement::from_string(
|
||||
db.get_database_backend(),
|
||||
check_item_sql,
|
||||
))
|
||||
.await?;
|
||||
let gacha_item_uuid = if let Some(ref row) = item_result {
|
||||
// Item exists, get its ID
|
||||
row.try_get("", "id")?
|
||||
} else {
|
||||
// Item doesn't exist, create it
|
||||
// Note: We can't easily delete by a fixed ID since it's a UUID, but the insert will fail if there's a conflict
|
||||
let _ = pg_conn.execute(sea_orm::Statement::from_string(db.get_database_backend(), "DELETE FROM app_gacha_items WHERE item_code = 'ITEM_TEST_1'".to_string())).await.ok();
|
||||
|
||||
// Create gacha item via SeaORM
|
||||
let _ = pg_conn
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
db.get_database_backend(),
|
||||
"DELETE FROM app_gacha_items WHERE item_code = 'ITEM_TEST_1'".to_string(),
|
||||
))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
let new_uuid = Uuid::new_v4();
|
||||
let mut item_model: GachaItemActiveModel = Default::default();
|
||||
item_model.id = Set(new_uuid);
|
||||
@@ -47,7 +54,6 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
new_uuid
|
||||
};
|
||||
|
||||
// Always try to insert the roll, relying on the database constraints to prevent duplicates if needed
|
||||
let gacha_roll_id = Uuid::new_v4();
|
||||
let mut roll_model: GachaRollActiveModel = Default::default();
|
||||
roll_model.id = Set(gacha_roll_id);
|
||||
@@ -61,18 +67,18 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
roll_model.updated_at = Set(Some(chrono::Utc::now().naive_utc()));
|
||||
roll_model.insert(db).await?;
|
||||
println!("Gacha Roll seeded successfully!");
|
||||
let gacha_roll_id = Uuid::new_v4();
|
||||
let mut roll_model: GachaRollActiveModel = Default::default();
|
||||
roll_model.id = Set(gacha_roll_id);
|
||||
roll_model.user_id = Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
roll_model.gacha_id = Set(Uuid::new_v4().to_string());
|
||||
roll_model.item_id = Set(gacha_item_uuid);
|
||||
roll_model.weight = Set(1.0);
|
||||
roll_model.quantity = Set(10);
|
||||
roll_model.is_deleted = Set(false);
|
||||
roll_model.created_at = Set(Some(chrono::Utc::now().naive_utc()));
|
||||
roll_model.updated_at = Set(Some(chrono::Utc::now().naive_utc()));
|
||||
roll_model.insert(db).await?;
|
||||
let gacha_roll_id = Uuid::new_v4();
|
||||
let mut roll_model: GachaRollActiveModel = Default::default();
|
||||
roll_model.id = Set(gacha_roll_id);
|
||||
roll_model.user_id = Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
roll_model.gacha_id = Set(Uuid::new_v4().to_string());
|
||||
roll_model.item_id = Set(gacha_item_uuid);
|
||||
roll_model.weight = Set(1.0);
|
||||
roll_model.quantity = Set(10);
|
||||
roll_model.is_deleted = Set(false);
|
||||
roll_model.created_at = Set(Some(chrono::Utc::now().naive_utc()));
|
||||
roll_model.updated_at = Set(Some(chrono::Utc::now().naive_utc()));
|
||||
roll_model.insert(db).await?;
|
||||
println!("✅ Gacha items and rolls seeded.");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_entities::seaorm::auth::mentors::ActiveModel as MentorsActiveModel;
|
||||
use imphnen_entities::seaorm::auth::roles::{
|
||||
Column as RoleColumn, Entity as RoleEntity,
|
||||
};
|
||||
use imphnen_entities::seaorm::auth::users::ActiveModel as UsersActiveModel;
|
||||
use imphnen_libs::hash_password;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, ActiveValue::Set, ColumnTrait, ConnectionTrait, EntityTrait,
|
||||
QueryFilter,
|
||||
};
|
||||
use serde_json::json;
|
||||
use std::error::Error;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::auth::users::ActiveModel as UsersActiveModel;
|
||||
use imphnen_entities::seaorm::auth::mentors::ActiveModel as MentorsActiveModel;
|
||||
use imphnen_entities::seaorm::auth::roles::{Entity as RoleEntity, Column as RoleColumn};
|
||||
use sea_orm::{ActiveModelTrait, ConnectionTrait, ActiveValue::Set, EntityTrait, QueryFilter, ColumnTrait};
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
@@ -16,17 +21,28 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
let _ = pg_conn.execute(sea_orm::Statement::from_string(db.get_database_backend(), "DELETE FROM app_mentors WHERE id = 'e6f78d23-83bf-5c2b-bcd4-001345678901'".to_string())).await.ok();
|
||||
let _ = pg_conn.execute(sea_orm::Statement::from_string(db.get_database_backend(), "DELETE FROM app_users WHERE email = 'mentor@example.com'".to_string())).await.ok();
|
||||
let _ = pg_conn
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
db.get_database_backend(),
|
||||
"DELETE FROM app_mentors WHERE id = 'e6f78d23-83bf-5c2b-bcd4-001345678901'"
|
||||
.to_string(),
|
||||
))
|
||||
.await
|
||||
.ok();
|
||||
let _ = pg_conn
|
||||
.execute(sea_orm::Statement::from_string(
|
||||
db.get_database_backend(),
|
||||
"DELETE FROM app_users WHERE email = 'mentor@example.com'".to_string(),
|
||||
))
|
||||
.await
|
||||
.ok();
|
||||
|
||||
// Find Mentor role
|
||||
let role = RoleEntity::find()
|
||||
.filter(RoleColumn::Name.eq("Mentor"))
|
||||
.one(db)
|
||||
.await?
|
||||
.ok_or("Role 'Mentor' not found")?;
|
||||
|
||||
// Insert user with Mentor role
|
||||
let user_id = Uuid::new_v4();
|
||||
let mut user_model: UsersActiveModel = Default::default();
|
||||
user_model.id = Set(user_id);
|
||||
@@ -43,21 +59,23 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
user_model.updated_at = Set(chrono::Utc::now());
|
||||
user_model.insert(db).await?;
|
||||
|
||||
// Insert mentor
|
||||
let mentor_id = Uuid::new_v4();
|
||||
let mut mentor_model: MentorsActiveModel = Default::default();
|
||||
mentor_model.id = Set(mentor_id);
|
||||
mentor_model.user_id = Set(user_id);
|
||||
mentor_model.industries = Set(Some(json!( ["Software", "Education"] )));
|
||||
mentor_model.expertise = Set(Some(json!( ["Rust", "Microservices"] )));
|
||||
mentor_model.languages = Set(Some(json!( ["Indonesian", "English"] )));
|
||||
mentor_model.industries = Set(Some(json!(["Software", "Education"])));
|
||||
mentor_model.expertise = Set(Some(json!(["Rust", "Microservices"])));
|
||||
mentor_model.languages = Set(Some(json!(["Indonesian", "English"])));
|
||||
mentor_model.current_company = Set(Some("PT Contoh".to_string()));
|
||||
mentor_model.current_role = Set(Some("Senior Backend Engineer".to_string()));
|
||||
mentor_model.years_of_experience = Set(Some(5));
|
||||
mentor_model.topics_of_interest = Set(Some(json!( ["Rust Programming", "Backend Development"] )));
|
||||
mentor_model.topics_of_interest =
|
||||
Set(Some(json!(["Rust Programming", "Backend Development"])));
|
||||
mentor_model.preferred_mentee_level = Set(Some("beginner".to_string()));
|
||||
mentor_model.preferred_mentoring_formats = Set(Some(json!( ["online", "offline"] )));
|
||||
mentor_model.availability_commitment = Set(Some("2 jam per minggu untuk mentoring online dan offline".to_string()));
|
||||
mentor_model.preferred_mentoring_formats = Set(Some(json!(["online", "offline"])));
|
||||
mentor_model.availability_commitment = Set(Some(
|
||||
"2 jam per minggu untuk mentoring online dan offline".to_string(),
|
||||
));
|
||||
mentor_model.mentoring_rate = Set(Some(100000.0));
|
||||
mentor_model.status = Set(Some("verified".to_string()));
|
||||
mentor_model.is_deleted = Set(false);
|
||||
|
||||
@@ -1,81 +1,79 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_iam::PermissionsEnum;
|
||||
use std::error::Error;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::auth::permissions::ActiveModel as PermissionActiveModel;
|
||||
use imphnen_entities::seaorm::auth::permissions::Entity as PermissionEntity;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::{ActiveModelTrait};
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
for permission in [
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::CreateUsers,
|
||||
PermissionsEnum::DeleteUsers,
|
||||
PermissionsEnum::UpdateUsers,
|
||||
PermissionsEnum::ActivateUsers,
|
||||
PermissionsEnum::ReadListRoles,
|
||||
PermissionsEnum::ReadDetailRoles,
|
||||
PermissionsEnum::CreateRoles,
|
||||
PermissionsEnum::DeleteRoles,
|
||||
PermissionsEnum::UpdateRoles,
|
||||
PermissionsEnum::ReadListPermissions,
|
||||
PermissionsEnum::ReadDetailPermissions,
|
||||
PermissionsEnum::CreatePermissions,
|
||||
PermissionsEnum::DeletePermissions,
|
||||
PermissionsEnum::UpdatePermissions,
|
||||
PermissionsEnum::CreateGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaClaims,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::CreateGachaItems,
|
||||
PermissionsEnum::DeleteGachaItems,
|
||||
PermissionsEnum::UpdateGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::RegisterMentors,
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::UpdateOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
PermissionsEnum::UpdateMentors,
|
||||
PermissionsEnum::VerifyMentors,
|
||||
PermissionsEnum::DeleteMentors,
|
||||
PermissionsEnum::Administrator,
|
||||
] {
|
||||
// permission.id() returns a string, try parse to uuid
|
||||
let parsed_id = Uuid::parse_str(&permission.id()).unwrap_or_else(|_| Uuid::new_v4());
|
||||
|
||||
// Check if permission already exists
|
||||
let existing = PermissionEntity::find_by_id(parsed_id).one(db).await?;
|
||||
if existing.is_some() {
|
||||
println!("ℹ️ Skipping (already exists): {permission}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Insert permission using active model
|
||||
let mut perm_model: PermissionActiveModel = Default::default();
|
||||
perm_model.id = Set(parsed_id);
|
||||
perm_model.name = Set(permission.to_string());
|
||||
perm_model.is_deleted = Set(false);
|
||||
perm_model.created_at = Set(Utc::now());
|
||||
perm_model.updated_at = Set(Utc::now());
|
||||
perm_model.insert(db).await?;
|
||||
println!("✅ Inserted: {permission}");
|
||||
}
|
||||
|
||||
println!("✅ All Permissions seeded");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use chrono::Utc;
|
||||
use imphnen_entities::seaorm::auth::permissions::ActiveModel as PermissionActiveModel;
|
||||
use imphnen_entities::seaorm::auth::permissions::Entity as PermissionEntity;
|
||||
use imphnen_iam::PermissionsEnum;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
for permission in [
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::CreateUsers,
|
||||
PermissionsEnum::DeleteUsers,
|
||||
PermissionsEnum::UpdateUsers,
|
||||
PermissionsEnum::ActivateUsers,
|
||||
PermissionsEnum::ReadListRoles,
|
||||
PermissionsEnum::ReadDetailRoles,
|
||||
PermissionsEnum::CreateRoles,
|
||||
PermissionsEnum::DeleteRoles,
|
||||
PermissionsEnum::UpdateRoles,
|
||||
PermissionsEnum::ReadListPermissions,
|
||||
PermissionsEnum::ReadDetailPermissions,
|
||||
PermissionsEnum::CreatePermissions,
|
||||
PermissionsEnum::DeletePermissions,
|
||||
PermissionsEnum::UpdatePermissions,
|
||||
PermissionsEnum::CreateGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaClaims,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::CreateGachaItems,
|
||||
PermissionsEnum::DeleteGachaItems,
|
||||
PermissionsEnum::UpdateGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::RegisterMentors,
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::UpdateOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
PermissionsEnum::UpdateMentors,
|
||||
PermissionsEnum::VerifyMentors,
|
||||
PermissionsEnum::DeleteMentors,
|
||||
PermissionsEnum::Administrator,
|
||||
] {
|
||||
let parsed_id =
|
||||
Uuid::parse_str(&permission.id()).unwrap_or_else(|_| Uuid::new_v4());
|
||||
|
||||
let existing = PermissionEntity::find_by_id(parsed_id).one(db).await?;
|
||||
if existing.is_some() {
|
||||
println!("ℹ️ Skipping (already exists): {permission}");
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut perm_model: PermissionActiveModel = Default::default();
|
||||
perm_model.id = Set(parsed_id);
|
||||
perm_model.name = Set(permission.to_string());
|
||||
perm_model.is_deleted = Set(false);
|
||||
perm_model.created_at = Set(Utc::now());
|
||||
perm_model.updated_at = Set(Utc::now());
|
||||
perm_model.insert(db).await?;
|
||||
println!("✅ Inserted: {permission}");
|
||||
}
|
||||
|
||||
println!("✅ All Permissions seeded");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
use std::error::Error;
|
||||
use chrono::Utc;
|
||||
use imphnen_entities::seaorm::auth::roles::{Entity as RoleEntity, RoleBuilder};
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::auth::roles::{RoleBuilder, Entity as RoleEntity};
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::Set, EntityTrait};
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc; // Added chrono
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -50,19 +50,15 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
),
|
||||
];
|
||||
|
||||
for (id, name, _created_at_str, _updated_at_str) in roles { // Renamed to avoid conflict
|
||||
for (id, name, _created_at_str, _updated_at_str) in roles {
|
||||
let uuid = Uuid::parse_str(id).unwrap_or_else(|_| Uuid::new_v4());
|
||||
|
||||
// Check if role already exists
|
||||
|
||||
let existing = RoleEntity::find_by_id(uuid).one(db).await?;
|
||||
if existing.is_some() {
|
||||
println!("ℹ️ Skipping (already exists): {name}");
|
||||
continue;
|
||||
}
|
||||
|
||||
// Delete existing by id to avoid duplicates (original logic, replaced by existence check)
|
||||
// let _ = pg_conn.execute(sea_orm::Statement::from_string(db.get_database_backend(), format!("DELETE FROM app_roles WHERE id = '{}'", uuid))).await.ok();
|
||||
|
||||
let role_model = RoleBuilder::new()
|
||||
.name(name.to_string())
|
||||
.description("System generated role".to_string())
|
||||
@@ -71,13 +67,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
.build()?;
|
||||
let mut role_model = role_model;
|
||||
role_model.id = Set(uuid);
|
||||
role_model.is_system_role = Set(true); // Set the missing field
|
||||
role_model.created_at = Set(Utc::now()); // Set created_at
|
||||
role_model.updated_at = Set(Utc::now()); // Set updated_at
|
||||
role_model.is_system_role = Set(true);
|
||||
role_model.created_at = Set(Utc::now());
|
||||
role_model.updated_at = Set(Utc::now());
|
||||
|
||||
role_model.insert(db).await?;
|
||||
println!("✅ Inserted role: {name}");
|
||||
}
|
||||
println!("✅ All Roles seeded");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,112 +1,109 @@
|
||||
use imphnen_iam::PermissionsEnum;
|
||||
use std::error::Error;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::auth::roles::Entity as RolesEntity;
|
||||
use imphnen_entities::seaorm::auth::roles::ActiveModel as RoleActiveModel;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::EntityTrait;
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use uuid::Uuid;
|
||||
use serde_json::Value as JsonValue;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
// Ensure indexes are present if needed (placeholders) - we don't modify schema here
|
||||
|
||||
println!("✅ Index 'user_email_index' defined on table 'users' for column 'email'.");
|
||||
|
||||
let roles_permissions = vec![
|
||||
(
|
||||
"f6b03f25-e416-4893-ac88-caaa690afb07",
|
||||
vec![
|
||||
// Only Administrator permission - grants access to everything
|
||||
PermissionsEnum::Administrator,
|
||||
],
|
||||
),
|
||||
(
|
||||
"3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a",
|
||||
vec![
|
||||
PermissionsEnum::ReadListUsers, // Added ReadListUsers permission
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::UpdateOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
],
|
||||
),
|
||||
(
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
vec![
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::CreateGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
PermissionsEnum::RegisterMentors,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
],
|
||||
),
|
||||
(
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
|
||||
vec![
|
||||
// Staff should be able to list roles and permissions in tests
|
||||
PermissionsEnum::ReadListRoles,
|
||||
PermissionsEnum::ReadListPermissions,
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::ActivateUsers,
|
||||
PermissionsEnum::ReadDetailRoles,
|
||||
PermissionsEnum::ReadDetailPermissions,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
],
|
||||
),
|
||||
(
|
||||
"60f1aeb7-dad2-4e06-bcb5-be1ba510c906",
|
||||
vec![PermissionsEnum::ActivateUsers],
|
||||
),
|
||||
("6d4fea5d-4a08-4b8a-9782-f2ab2183dcf0", vec![]),
|
||||
];
|
||||
|
||||
for (role_id, permissions) in roles_permissions {
|
||||
let role_uuid = Uuid::parse_str(role_id).unwrap_or_else(|_| Uuid::new_v4());
|
||||
// Map permissions enum to JSON array of permission ids
|
||||
let json_permissions = JsonValue::Array(
|
||||
permissions.iter().map(|p| JsonValue::String(p.id())).collect()
|
||||
);
|
||||
|
||||
// Find role and update permissions
|
||||
if let Some(role_model) = RolesEntity::find_by_id(role_uuid).one(db).await? {
|
||||
let mut am: RoleActiveModel = role_model.into();
|
||||
am.permissions = Set(Some(json_permissions));
|
||||
am.update(db).await?;
|
||||
println!("✅ Permissions updated for role: {role_id}");
|
||||
} else {
|
||||
println!("⚠️ Role with id {role_id} not found, skipping permissions update");
|
||||
}
|
||||
}
|
||||
|
||||
println!("✅ All roles permissions updated!");
|
||||
Ok(())
|
||||
}
|
||||
use imphnen_entities::seaorm::auth::roles::ActiveModel as RoleActiveModel;
|
||||
use imphnen_entities::seaorm::auth::roles::Entity as RolesEntity;
|
||||
use imphnen_iam::PermissionsEnum;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use sea_orm::EntityTrait;
|
||||
use serde_json::Value as JsonValue;
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
println!(
|
||||
"✅ Index 'user_email_index' defined on table 'users' for column 'email'."
|
||||
);
|
||||
|
||||
let roles_permissions = vec![
|
||||
(
|
||||
"f6b03f25-e416-4893-ac88-caaa690afb07",
|
||||
vec![PermissionsEnum::Administrator],
|
||||
),
|
||||
(
|
||||
"3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a",
|
||||
vec![
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::UpdateOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
],
|
||||
),
|
||||
(
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
vec![
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::CreateGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaClaims,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
PermissionsEnum::RegisterMentors,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadOwnMentorProfile,
|
||||
PermissionsEnum::ReadOwnMentorStatus,
|
||||
],
|
||||
),
|
||||
(
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
|
||||
vec![
|
||||
PermissionsEnum::ReadListRoles,
|
||||
PermissionsEnum::ReadListPermissions,
|
||||
PermissionsEnum::ReadListUsers,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailUsers,
|
||||
PermissionsEnum::ActivateUsers,
|
||||
PermissionsEnum::ReadDetailRoles,
|
||||
PermissionsEnum::ReadDetailPermissions,
|
||||
PermissionsEnum::ReadListGachaItems,
|
||||
PermissionsEnum::ReadDetailGachaItems,
|
||||
PermissionsEnum::ReadListMentors,
|
||||
PermissionsEnum::ReadDetailMentors,
|
||||
PermissionsEnum::ReadDetailGachaRolls,
|
||||
PermissionsEnum::CreateGachaRolls,
|
||||
PermissionsEnum::ExecuteGachaRolls,
|
||||
],
|
||||
),
|
||||
(
|
||||
"60f1aeb7-dad2-4e06-bcb5-be1ba510c906",
|
||||
vec![PermissionsEnum::ActivateUsers],
|
||||
),
|
||||
("6d4fea5d-4a08-4b8a-9782-f2ab2183dcf0", vec![]),
|
||||
];
|
||||
|
||||
for (role_id, permissions) in roles_permissions {
|
||||
let role_uuid = Uuid::parse_str(role_id).unwrap_or_else(|_| Uuid::new_v4());
|
||||
let json_permissions = JsonValue::Array(
|
||||
permissions
|
||||
.iter()
|
||||
.map(|p| JsonValue::String(p.id()))
|
||||
.collect(),
|
||||
);
|
||||
|
||||
if let Some(role_model) = RolesEntity::find_by_id(role_uuid).one(db).await? {
|
||||
let mut am: RoleActiveModel = role_model.into();
|
||||
am.permissions = Set(Some(json_permissions));
|
||||
am.update(db).await?;
|
||||
println!("✅ Permissions updated for role: {role_id}");
|
||||
} else {
|
||||
println!("⚠️ Role with id {role_id} not found, skipping permissions update");
|
||||
}
|
||||
}
|
||||
|
||||
println!("✅ All roles permissions updated!");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,77 +1,79 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use std::error::Error;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use chrono::Utc;
|
||||
use imphnen_entities::seaorm::auth::mentors::ActiveModel as MentorsActiveModel;
|
||||
use imphnen_entities::seaorm::common::events::ActiveModel as EventsActiveModel;
|
||||
use imphnen_entities::seaorm::common::testimonials::ActiveModel as TestimonialsActiveModel;
|
||||
use imphnen_entities::seaorm::auth::mentors::ActiveModel as MentorsActiveModel;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use sea_orm::ActiveModelTrait;
|
||||
use uuid::Uuid;
|
||||
use sea_orm::ActiveValue::Set;
|
||||
use serde_json::json;
|
||||
use chrono::Utc;
|
||||
use std::error::Error;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
let config = PostgresConfig::from_env()?;
|
||||
let pg_conn = PostgresConnection::new(config).await?;
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
// Seed Events - handle existing data
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
let mut event_model: EventsActiveModel = Default::default();
|
||||
event_model.id = Set(Uuid::parse_str(&uuid)?);
|
||||
event_model.name = Set("Test Event".to_string());
|
||||
event_model.description = Set("Test event description".to_string());
|
||||
event_model.detail_link = Set("https://example.com/event".to_string());
|
||||
event_model.price = Set(50.0);
|
||||
event_model.is_online = Set(true);
|
||||
event_model.start_date = Set(Utc::now());
|
||||
event_model.end_date = Set(Utc::now() + chrono::Duration::days(1));
|
||||
event_model.location = Set(None);
|
||||
event_model.is_deleted = Set(false);
|
||||
match event_model.insert(db).await {
|
||||
Ok(_) => println!("✅ Inserted test event"),
|
||||
Err(_) => println!("⚠️ Test event already exists or could not be inserted, skipping"),
|
||||
};
|
||||
let uuid = Uuid::new_v4().to_string();
|
||||
let mut event_model: EventsActiveModel = Default::default();
|
||||
event_model.id = Set(Uuid::parse_str(&uuid)?);
|
||||
event_model.name = Set("Test Event".to_string());
|
||||
event_model.description = Set("Test event description".to_string());
|
||||
event_model.detail_link = Set("https://example.com/event".to_string());
|
||||
event_model.price = Set(50.0);
|
||||
event_model.is_online = Set(true);
|
||||
event_model.start_date = Set(Utc::now());
|
||||
event_model.end_date = Set(Utc::now() + chrono::Duration::days(1));
|
||||
event_model.location = Set(None);
|
||||
event_model.is_deleted = Set(false);
|
||||
match event_model.insert(db).await {
|
||||
Ok(_) => println!("✅ Inserted test event"),
|
||||
Err(_) => {
|
||||
println!("⚠️ Test event already exists or could not be inserted, skipping")
|
||||
}
|
||||
};
|
||||
|
||||
// Seed Testimonials - handle existing data
|
||||
let mut testimonial_model: TestimonialsActiveModel = Default::default();
|
||||
testimonial_model.id = Set(Uuid::parse_str("00000000-0000-0000-0000-000000000001")?);
|
||||
testimonial_model.user_id = Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
testimonial_model.role = Set("Student".to_string());
|
||||
testimonial_model.content = Set("This is a great platform!".to_string());
|
||||
testimonial_model.is_deleted = Set(false);
|
||||
match testimonial_model.insert(db).await {
|
||||
Ok(_) => println!("✅ Inserted test testimonial"),
|
||||
Err(_) => println!("⚠️ Test testimonial already exists or could not be inserted, skipping"),
|
||||
};
|
||||
let mut testimonial_model: TestimonialsActiveModel = Default::default();
|
||||
testimonial_model.id =
|
||||
Set(Uuid::parse_str("00000000-0000-0000-0000-000000000001")?);
|
||||
testimonial_model.user_id =
|
||||
Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
testimonial_model.role = Set("Student".to_string());
|
||||
testimonial_model.content = Set("This is a great platform!".to_string());
|
||||
testimonial_model.is_deleted = Set(false);
|
||||
match testimonial_model.insert(db).await {
|
||||
Ok(_) => println!("✅ Inserted test testimonial"),
|
||||
Err(_) => println!(
|
||||
"⚠️ Test testimonial already exists or could not be inserted, skipping"
|
||||
),
|
||||
};
|
||||
|
||||
// Seed Mentor - handle existing data
|
||||
let mentor_id = Uuid::new_v4();
|
||||
let mut mentor_model: MentorsActiveModel = Default::default();
|
||||
mentor_model.id = Set(mentor_id);
|
||||
// Use the admin user ID instead of a random one
|
||||
mentor_model.user_id = Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
mentor_model.industries = Set(Some(json!( ["Technology", "Education"] )));
|
||||
mentor_model.expertise = Set(Some(json!( ["Software Development"] )));
|
||||
mentor_model.languages = Set(Some(json!( ["English", "Indonesian"] )));
|
||||
mentor_model.current_company = Set(Some("Tech Corp".to_string()));
|
||||
mentor_model.current_role = Set(Some("Senior Engineer".to_string()));
|
||||
mentor_model.years_of_experience = Set(Some(5));
|
||||
mentor_model.topics_of_interest = Set(Some(json!( ["Rust", "Web Development"] )));
|
||||
mentor_model.preferred_mentee_level = Set(Some("Beginner".to_string()));
|
||||
mentor_model.preferred_mentoring_formats = Set(Some(json!( ["1:1", "Group"] )));
|
||||
mentor_model.availability_commitment = Set(Some("Weekly".to_string()));
|
||||
mentor_model.mentoring_rate = Set(Some(100.0));
|
||||
mentor_model.status = Set(Some("active".to_string()));
|
||||
mentor_model.is_deleted = Set(false);
|
||||
mentor_model.created_at = Set(chrono::Utc::now());
|
||||
mentor_model.updated_at = Set(chrono::Utc::now());
|
||||
// Create mentor record via SeaORM active model
|
||||
mentor_model.insert(db).await?;
|
||||
println!("✅ Inserted test mentor via SeaORM");
|
||||
let mentor_id = Uuid::new_v4();
|
||||
let mut mentor_model: MentorsActiveModel = Default::default();
|
||||
mentor_model.id = Set(mentor_id);
|
||||
mentor_model.user_id =
|
||||
Set(Uuid::parse_str("c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")?);
|
||||
mentor_model.industries = Set(Some(json!(["Technology", "Education"])));
|
||||
mentor_model.expertise = Set(Some(json!(["Software Development"])));
|
||||
mentor_model.languages = Set(Some(json!(["English", "Indonesian"])));
|
||||
mentor_model.current_company = Set(Some("Tech Corp".to_string()));
|
||||
mentor_model.current_role = Set(Some("Senior Engineer".to_string()));
|
||||
mentor_model.years_of_experience = Set(Some(5));
|
||||
mentor_model.topics_of_interest = Set(Some(json!(["Rust", "Web Development"])));
|
||||
mentor_model.preferred_mentee_level = Set(Some("Beginner".to_string()));
|
||||
mentor_model.preferred_mentoring_formats = Set(Some(json!(["1:1", "Group"])));
|
||||
mentor_model.availability_commitment = Set(Some("Weekly".to_string()));
|
||||
mentor_model.mentoring_rate = Set(Some(100.0));
|
||||
mentor_model.status = Set(Some("active".to_string()));
|
||||
mentor_model.is_deleted = Set(false);
|
||||
mentor_model.created_at = Set(chrono::Utc::now());
|
||||
mentor_model.updated_at = Set(chrono::Utc::now());
|
||||
mentor_model.insert(db).await?;
|
||||
println!("✅ Inserted test mentor via SeaORM");
|
||||
|
||||
println!("✅ All test data seeded successfully");
|
||||
Ok(())
|
||||
}
|
||||
println!("✅ All test data seeded successfully");
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
#![allow(clippy::all)]
|
||||
|
||||
use imphnen_entities::seaorm::auth::users::ActiveModel as UsersActiveModel;
|
||||
use imphnen_entities::seaorm::auth::users::Entity as UserEntity;
|
||||
use imphnen_libs::hash_password;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
|
||||
use imphnen_entities::seaorm::auth::users::Entity as UserEntity; // Added for dynamic role lookup
|
||||
use imphnen_entities::seaorm::auth::users::ActiveModel as UsersActiveModel;
|
||||
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::Set, EntityTrait, IntoActiveModel};
|
||||
use uuid::Uuid;
|
||||
|
||||
use chrono::Utc;
|
||||
use sea_orm::{ActiveModelTrait, ActiveValue::Set, EntityTrait, IntoActiveModel};
|
||||
use std::error::Error;
|
||||
use chrono::Utc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn Error>> {
|
||||
@@ -17,145 +17,148 @@ async fn main() -> Result<(), Box<dyn Error>> {
|
||||
let db = &pg_conn.conn;
|
||||
|
||||
let users = vec![
|
||||
(
|
||||
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2",
|
||||
"admin@example.com",
|
||||
"Admin",
|
||||
"f6b03f25-e416-4893-ac88-caaa690afb07",
|
||||
),
|
||||
(
|
||||
"a4d23fb5-9e31-423c-9842-fbd6e75a5298",
|
||||
"staff@example.com",
|
||||
"Staff",
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
|
||||
),
|
||||
(
|
||||
"d5e89c12-72af-4b1a-abc3-ff1234567890",
|
||||
"user@example.com",
|
||||
"User",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"665a3cfc-ea5f-4bcd-8769-4a6d8d1451d4",
|
||||
"testuser1@example.com",
|
||||
"Test User 1",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059", // Fixed UUID
|
||||
),
|
||||
(
|
||||
"3972c139-a450-416c-93b0-c42539dc780f",
|
||||
"testuser2@example.com",
|
||||
"Test User 2",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"b426c0a9-0efb-4e26-b078-4f18767255f3",
|
||||
"testuser3@example.com",
|
||||
"Test User 3",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059", // Fixed UUID
|
||||
),
|
||||
// Additional Users for Volume and Variety
|
||||
(
|
||||
"11111111-1111-1111-1111-111111111111",
|
||||
"user4@example.com",
|
||||
"User Four",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"22222222-2222-2222-2222-222222222222",
|
||||
"user5@example.com",
|
||||
"User Five",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"33333333-3333-3333-3333-333333333333",
|
||||
"mentor2@example.com",
|
||||
"Mentor Two",
|
||||
"3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a", // Mentor Role
|
||||
),
|
||||
(
|
||||
"44444444-4444-4444-4444-444444444444",
|
||||
"staff2@example.com",
|
||||
"Staff Two",
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86", // Staff Role
|
||||
),
|
||||
(
|
||||
"55555555-5555-5555-5555-555555555555",
|
||||
"user6@example.com",
|
||||
"User Six",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"66666666-6666-6666-6666-666666666666",
|
||||
"user7@example.com",
|
||||
"User Seven",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"77777777-7777-7777-7777-777777777777",
|
||||
"user8@example.com",
|
||||
"User Eight",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"88888888-8888-8888-8888-888888888888",
|
||||
"user9@example.com",
|
||||
"User Nine",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"99999999-9999-9999-9999-999999999999",
|
||||
"user10@example.com",
|
||||
"User Ten",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2",
|
||||
"admin@example.com",
|
||||
"Admin",
|
||||
"f6b03f25-e416-4893-ac88-caaa690afb07",
|
||||
),
|
||||
(
|
||||
"a4d23fb5-9e31-423c-9842-fbd6e75a5298",
|
||||
"staff@example.com",
|
||||
"Staff",
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
|
||||
),
|
||||
(
|
||||
"d5e89c12-72af-4b1a-abc3-ff1234567890",
|
||||
"user@example.com",
|
||||
"User",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"665a3cfc-ea5f-4bcd-8769-4a6d8d1451d4",
|
||||
"testuser1@example.com",
|
||||
"Test User 1",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"3972c139-a450-416c-93b0-c42539dc780f",
|
||||
"testuser2@example.com",
|
||||
"Test User 2",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"b426c0a9-0efb-4e26-b078-4f18767255f3",
|
||||
"testuser3@example.com",
|
||||
"Test User 3",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"11111111-1111-1111-1111-111111111111",
|
||||
"user4@example.com",
|
||||
"User Four",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"22222222-2222-2222-2222-222222222222",
|
||||
"user5@example.com",
|
||||
"User Five",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"33333333-3333-3333-3333-333333333333",
|
||||
"mentor2@example.com",
|
||||
"Mentor Two",
|
||||
"3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a",
|
||||
),
|
||||
(
|
||||
"44444444-4444-4444-4444-444444444444",
|
||||
"staff2@example.com",
|
||||
"Staff Two",
|
||||
"50133429-f4b1-4249-9f97-7b86e6ee9d86",
|
||||
),
|
||||
(
|
||||
"55555555-5555-5555-5555-555555555555",
|
||||
"user6@example.com",
|
||||
"User Six",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"66666666-6666-6666-6666-666666666666",
|
||||
"user7@example.com",
|
||||
"User Seven",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"77777777-7777-7777-7777-777777777777",
|
||||
"user8@example.com",
|
||||
"User Eight",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"88888888-8888-8888-8888-888888888888",
|
||||
"user9@example.com",
|
||||
"User Nine",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
(
|
||||
"99999999-9999-9999-9999-999999999999",
|
||||
"user10@example.com",
|
||||
"User Ten",
|
||||
"5713cb37-dc02-4e87-8048-d7a41d352059",
|
||||
),
|
||||
];
|
||||
|
||||
for (id, email, fullname, role_id_str) in users { // role_id_str directly contains UUID
|
||||
let role_uuid = Some(Uuid::parse_str(role_id_str)
|
||||
.map_err(|e| format!("Invalid UUID for role: {role_id_str} - {e}"))?);
|
||||
|
||||
// Build SeaORM ActiveModel for users
|
||||
let uid = Uuid::parse_str(id)?; // Should always be valid UUID strings from test data
|
||||
|
||||
let names: Vec<&str> = fullname.split_whitespace().collect();
|
||||
let first_name = names.first().map(|s| s.to_string());
|
||||
let last_name = if names.len() > 1 { Some(names[1..].join(" ")) } else { None };
|
||||
|
||||
let password = "password";
|
||||
let hashed = hash_password(password).unwrap();
|
||||
for (id, email, fullname, role_id_str) in users {
|
||||
let role_uuid = Some(
|
||||
Uuid::parse_str(role_id_str)
|
||||
.map_err(|e| format!("Invalid UUID for role: {role_id_str} - {e}"))?,
|
||||
);
|
||||
|
||||
// Explicit Upsert Logic
|
||||
let existing_user = UserEntity::find_by_id(uid).one(db).await?;
|
||||
let is_update = existing_user.is_some();
|
||||
|
||||
let mut user_model: UsersActiveModel = if let Some(existing) = existing_user {
|
||||
println!("🔄 Updating user: {fullname} ({email})");
|
||||
existing.into_active_model()
|
||||
} else {
|
||||
println!("✅ Inserting user: {fullname} ({email})");
|
||||
let mut active: UsersActiveModel = Default::default();
|
||||
active.id = Set(uid);
|
||||
active.created_at = Set(Utc::now());
|
||||
active
|
||||
};
|
||||
|
||||
user_model.email = Set(email.to_string());
|
||||
user_model.password_hash = Set(hashed);
|
||||
user_model.username = Set(email.to_string());
|
||||
user_model.first_name = Set(first_name);
|
||||
user_model.last_name = Set(last_name);
|
||||
user_model.avatar_url = Set(Some("https://example.com/avatar.jpg".to_string()));
|
||||
user_model.is_verified = Set(true);
|
||||
user_model.is_active = Set(true);
|
||||
user_model.role_id = Set(role_uuid);
|
||||
user_model.updated_at = Set(Utc::now());
|
||||
|
||||
if is_update {
|
||||
user_model.update(db).await?;
|
||||
} else {
|
||||
user_model.insert(db).await?;
|
||||
}
|
||||
}
|
||||
let uid = Uuid::parse_str(id)?;
|
||||
|
||||
let names: Vec<&str> = fullname.split_whitespace().collect();
|
||||
let first_name = names.first().map(|s| s.to_string());
|
||||
let last_name = if names.len() > 1 {
|
||||
Some(names[1..].join(" "))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
let password = "password";
|
||||
let hashed = hash_password(password).unwrap();
|
||||
|
||||
let existing_user = UserEntity::find_by_id(uid).one(db).await?;
|
||||
let is_update = existing_user.is_some();
|
||||
|
||||
let mut user_model: UsersActiveModel = if let Some(existing) = existing_user {
|
||||
println!("🔄 Updating user: {fullname} ({email})");
|
||||
existing.into_active_model()
|
||||
} else {
|
||||
println!("✅ Inserting user: {fullname} ({email})");
|
||||
let mut active: UsersActiveModel = Default::default();
|
||||
active.id = Set(uid);
|
||||
active.created_at = Set(Utc::now());
|
||||
active
|
||||
};
|
||||
|
||||
user_model.email = Set(email.to_string());
|
||||
user_model.password_hash = Set(hashed);
|
||||
user_model.username = Set(email.to_string());
|
||||
user_model.first_name = Set(first_name);
|
||||
user_model.last_name = Set(last_name);
|
||||
user_model.avatar_url = Set(Some("https://example.com/avatar.jpg".to_string()));
|
||||
user_model.is_verified = Set(true);
|
||||
user_model.is_active = Set(true);
|
||||
user_model.role_id = Set(role_uuid);
|
||||
user_model.updated_at = Set(Utc::now());
|
||||
|
||||
if is_update {
|
||||
user_model.update(db).await?;
|
||||
} else {
|
||||
user_model.insert(db).await?;
|
||||
}
|
||||
}
|
||||
println!("✅ All Users seeded");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,368 +1,395 @@
|
||||
//! PostgreSQL Connection Test Program
|
||||
//! This program tests the PostgreSQL integration with SeaORM
|
||||
|
||||
use std::sync::Arc;
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection, PostgresError};
|
||||
use imphnen_entities::seaorm::auth::users::{Entity as UsersEntity, Model as UserModel};
|
||||
use imphnen_entities::seaorm::auth::roles::{Entity as RolesEntity, Model as RoleModel};
|
||||
use sea_orm::{EntityTrait, ActiveModelTrait, Set, TransactionTrait, DbErr, PaginatorTrait};
|
||||
use uuid::Uuid;
|
||||
use chrono::Utc;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 Starting PostgreSQL Connection Test");
|
||||
println!("=====================================");
|
||||
|
||||
// Load configuration from environment
|
||||
let config = PostgresConfig::from_env()?;
|
||||
println!("✅ Configuration loaded successfully");
|
||||
println!(" Database URL: {}", config.database_url.replace("postgres://", "postgres://****:****@"));
|
||||
println!(" Pool size: {}", config.pool_size);
|
||||
println!(" Connect timeout: {}s", config.connect_timeout);
|
||||
println!(" Retry attempts: {}", config.retry_attempts);
|
||||
|
||||
// Test connection
|
||||
println!("\n🔌 Testing PostgreSQL connection...");
|
||||
match test_connection(config).await {
|
||||
Ok(()) => {
|
||||
println!("✅ All PostgreSQL tests passed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("❌ PostgreSQL test failed: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn test_connection(config: PostgresConfig) -> Result<(), PostgresError> {
|
||||
// Create connection
|
||||
println!(" Creating PostgreSQL connection...");
|
||||
let postgres_conn = PostgresConnection::new(config).await?;
|
||||
let connection = Arc::new(postgres_conn);
|
||||
println!(" ✅ Connection established successfully");
|
||||
|
||||
// Test basic connectivity
|
||||
println!(" Testing basic connectivity...");
|
||||
test_basic_connectivity(&connection).await?;
|
||||
println!(" ✅ Basic connectivity test passed");
|
||||
|
||||
// Test table existence
|
||||
println!(" Testing table existence...");
|
||||
test_table_existence(&connection).await?;
|
||||
println!(" ✅ Table existence test passed");
|
||||
|
||||
// Test CRUD operations
|
||||
println!(" Testing CRUD operations...");
|
||||
test_crud_operations(&connection).await?;
|
||||
println!(" ✅ CRUD operations test passed");
|
||||
|
||||
// Test transaction support
|
||||
println!(" Testing transaction support...");
|
||||
test_transactions(&connection).await?;
|
||||
println!(" ✅ Transaction support test passed");
|
||||
|
||||
// Test error handling
|
||||
println!(" Testing error handling...");
|
||||
test_error_handling(&connection).await?;
|
||||
println!(" ✅ Error handling test passed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_basic_connectivity(connection: &Arc<PostgresConnection>) -> Result<(), PostgresError> {
|
||||
// Execute a simple query
|
||||
let statement = sea_orm::Statement::from_string(
|
||||
connection.get_database_backend(),
|
||||
"SELECT 1 as test_value, current_timestamp as current_time".to_string()
|
||||
);
|
||||
|
||||
let result = connection.query_one(statement).await?
|
||||
.ok_or_else(|| PostgresError::ConnectionError(sea_orm::DbErr::Custom("No results returned".to_string())))?;
|
||||
|
||||
// Verify we got expected results
|
||||
let test_value: Option<i32> = result.try_get("", "test_value").ok();
|
||||
let current_time: Option<String> = result.try_get("", "current_time").ok();
|
||||
|
||||
if test_value != Some(1) {
|
||||
return Err(PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
format!("Expected test_value=1, got {:?}", test_value)
|
||||
)));
|
||||
}
|
||||
|
||||
if current_time.is_none() {
|
||||
return Err(PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
"Expected current_time to be set".to_string()
|
||||
)));
|
||||
}
|
||||
|
||||
println!(" 📝 Query result: test_value={:?}, current_time={:?}", test_value, current_time);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_table_existence(connection: &Arc<PostgresConnection>) -> Result<(), PostgresError> {
|
||||
// Test if our tables exist
|
||||
use sea_orm::EntityTrait;
|
||||
|
||||
println!(" 📋 Checking users table...");
|
||||
let user_count = UsersEntity::find()
|
||||
.count(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
println!(" 📊 Users table accessible, current count: {}", user_count);
|
||||
|
||||
println!(" 📋 Checking roles table...");
|
||||
let role_count = RolesEntity::find()
|
||||
.count(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
println!(" 📊 Roles table accessible, current count: {}", role_count);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_crud_operations(connection: &Arc<PostgresConnection>) -> Result<(), PostgresError> {
|
||||
use sea_orm::{ActiveModelTrait, Set};
|
||||
|
||||
// Create test user
|
||||
println!(" ➕ Creating test user...");
|
||||
let test_user_id = Uuid::new_v4();
|
||||
let now = Utc::now();
|
||||
|
||||
let user_model = imphnen_entities::seaorm::auth::users::ActiveModel {
|
||||
id: Set(test_user_id),
|
||||
email: Set(format!("test_user_{}@example.com", test_user_id)),
|
||||
password_hash: Set("test_password_hash".to_string()),
|
||||
username: Set(format!("testuser_{}", test_user_id)),
|
||||
first_name: Set(Some("Test".to_string())),
|
||||
last_name: Set(Some("User".to_string())),
|
||||
avatar_url: Set(None),
|
||||
is_verified: Set(false),
|
||||
is_active: Set(true),
|
||||
metadata: Set(None),
|
||||
role_id: Set(None),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
deleted_at: Set(None),
|
||||
};
|
||||
|
||||
let created_user = user_model.insert(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(" ✅ Created user with ID: {}", created_user.id);
|
||||
|
||||
// Read user
|
||||
println!(" 🔍 Reading test user...");
|
||||
let found_user = UsersEntity::find_by_id(test_user_id)
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
.ok_or_else(|| PostgresError::ConnectionError(sea_orm::DbErr::Custom("User not found after creation".to_string())))?;
|
||||
|
||||
println!(" ✅ Found user: {} ({})", found_user.username, found_user.email);
|
||||
|
||||
// Update user
|
||||
println!(" ✏️ Updating test user...");
|
||||
let mut update_model: imphnen_entities::seaorm::auth::users::ActiveModel = found_user.into();
|
||||
update_model.first_name = Set(Some("Updated".to_string()));
|
||||
update_model.updated_at = Set(Utc::now());
|
||||
|
||||
let updated_user = update_model.update(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(" ✅ Updated user first name to: {:?}", updated_user.first_name);
|
||||
|
||||
// Delete user
|
||||
println!(" 🗑️ Deleting test user...");
|
||||
UsersEntity::delete_by_id(updated_user.id)
|
||||
.exec(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(" ✅ Test user deleted successfully");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_transactions(connection: &Arc<PostgresConnection>) -> Result<(), PostgresError> {
|
||||
println!(" 💰 Testing transaction support...");
|
||||
|
||||
// Test transaction with rollback
|
||||
let transaction_result = connection.conn.transaction(|txn| {
|
||||
Box::pin(async move {
|
||||
// Create a test user within transaction
|
||||
let test_user_id = Uuid::new_v4();
|
||||
let now = Utc::now();
|
||||
|
||||
let user_model = imphnen_entities::seaorm::auth::users::ActiveModel {
|
||||
id: Set(test_user_id),
|
||||
email: Set(format!("transaction_test_{}@example.com", test_user_id)),
|
||||
password_hash: Set("transaction_password_hash".to_string()),
|
||||
username: Set(format!("transaction_user_{}", test_user_id)),
|
||||
first_name: Set(Some("Transaction".to_string())),
|
||||
last_name: Set(Some("Test".to_string())),
|
||||
avatar_url: Set(None),
|
||||
is_verified: Set(false),
|
||||
is_active: Set(true),
|
||||
metadata: Set(None),
|
||||
role_id: Set(None),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
deleted_at: Set(None),
|
||||
};
|
||||
|
||||
let _created_user = user_model.insert(txn)
|
||||
.await?;
|
||||
|
||||
// Simulate an error to trigger rollback (return a sea_orm::DbErr so the TransactionError matches)
|
||||
Err::<(), DbErr>(DbErr::Custom("Simulated transaction failure".to_string()))
|
||||
})
|
||||
}).await;
|
||||
|
||||
// Transaction should fail and rollback
|
||||
match transaction_result {
|
||||
Err(e) => {
|
||||
let e_text = format!("{:?}", e);
|
||||
if e_text.contains("Simulated transaction failure") {
|
||||
println!(" ✅ Transaction failed as expected, rollback successful");
|
||||
} else {
|
||||
return Err(PostgresError::OperationFailed(format!("Unexpected transaction result: {}", e_text)));
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
return Err(PostgresError::OperationFailed("Unexpected transaction result: transaction unexpectedly succeeded".to_string()));
|
||||
}
|
||||
}
|
||||
|
||||
// Verify user was not created (due to rollback)
|
||||
let user_exists = UsersEntity::find_by_id(Uuid::nil()) // Use nil UUID as we don't know the actual ID
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
.is_some();
|
||||
|
||||
if user_exists {
|
||||
println!(" ⚠️ User found despite rollback - this might indicate an issue");
|
||||
} else {
|
||||
println!(" ✅ Transaction rollback verified - user not found");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_error_handling(connection: &Arc<PostgresConnection>) -> Result<(), PostgresError> {
|
||||
println!(" ⚠️ Testing error handling...");
|
||||
|
||||
// Test invalid UUID
|
||||
println!(" 🔍 Testing invalid UUID handling...");
|
||||
let invalid_uuid = Uuid::nil(); // This should exist or be handled gracefully
|
||||
|
||||
match UsersEntity::find_by_id(invalid_uuid)
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
{
|
||||
Some(_) => println!(" ✅ Found user with nil UUID (expected in some cases)"),
|
||||
None => println!(" ✅ No user found with nil UUID (expected)"),
|
||||
}
|
||||
|
||||
// Test invalid query
|
||||
println!(" 🔍 Testing invalid query handling...");
|
||||
let invalid_statement = sea_orm::Statement::from_string(
|
||||
connection.get_database_backend(),
|
||||
"SELECT * FROM non_existent_table".to_string()
|
||||
);
|
||||
|
||||
match connection.execute(invalid_statement).await {
|
||||
Err(_) => println!(" ✅ Invalid query properly handled with error"),
|
||||
Ok(_) => println!(" ⚠️ Invalid query unexpectedly succeeded"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Additional utility functions for comprehensive testing
|
||||
pub mod test_utils {
|
||||
use super::*;
|
||||
|
||||
/// Create a test PostgreSQL configuration
|
||||
pub fn create_test_config() -> PostgresConfig {
|
||||
PostgresConfig {
|
||||
database_url: "postgres://postgres:postgres@localhost:5432/imphnen_test".to_string(),
|
||||
pool_size: 5,
|
||||
connect_timeout: 10,
|
||||
idle_timeout: 30,
|
||||
max_lifetime: Some(600),
|
||||
retry_attempts: 2,
|
||||
retry_delay: 1,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a test user model
|
||||
pub fn create_test_user_model() -> UserModel {
|
||||
UserModel {
|
||||
id: Uuid::new_v4(),
|
||||
email: format!("test_{}@example.com", Uuid::new_v4()),
|
||||
password_hash: "test_password_hash".to_string(),
|
||||
username: format!("testuser_{}", Uuid::new_v4()),
|
||||
first_name: Some("Test".to_string()),
|
||||
last_name: Some("User".to_string()),
|
||||
avatar_url: None,
|
||||
is_verified: false,
|
||||
is_active: true,
|
||||
metadata: None,
|
||||
role_id: None,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a test role model
|
||||
pub fn create_test_role_model() -> RoleModel {
|
||||
RoleModel {
|
||||
id: Uuid::new_v4(),
|
||||
name: format!("test_role_{}", Uuid::new_v4()),
|
||||
description: "Test role description".to_string(),
|
||||
permissions: Some(serde_json::json!(["test.permission"])),
|
||||
is_system_role: false,
|
||||
is_default: false,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_create_test_config() {
|
||||
let config = test_utils::create_test_config();
|
||||
assert_eq!(config.pool_size, 5);
|
||||
assert_eq!(config.connect_timeout, 10);
|
||||
assert!(config.database_url.contains("imphnen_test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_test_user_model() {
|
||||
let user = test_utils::create_test_user_model();
|
||||
assert!(!user.email.is_empty());
|
||||
assert!(!user.username.is_empty());
|
||||
assert!(user.is_active);
|
||||
// is_admin field removed; instead, check role-based permission or is_active
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_test_role_model() {
|
||||
let role = test_utils::create_test_role_model();
|
||||
assert!(!role.name.is_empty());
|
||||
assert!(role.permissions.is_some());
|
||||
assert!(!role.is_system_role);
|
||||
}
|
||||
}
|
||||
use chrono::Utc;
|
||||
use imphnen_entities::seaorm::auth::roles::{
|
||||
Entity as RolesEntity, Model as RoleModel,
|
||||
};
|
||||
use imphnen_entities::seaorm::auth::users::{
|
||||
Entity as UsersEntity, Model as UserModel,
|
||||
};
|
||||
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection, PostgresError};
|
||||
use sea_orm::{
|
||||
ActiveModelTrait, DbErr, EntityTrait, PaginatorTrait, Set, TransactionTrait,
|
||||
};
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
println!("🚀 Starting PostgreSQL Connection Test");
|
||||
println!("=====================================");
|
||||
|
||||
let config = PostgresConfig::from_env()?;
|
||||
println!("✅ Configuration loaded successfully");
|
||||
println!(
|
||||
" Database URL: {}",
|
||||
config
|
||||
.database_url
|
||||
.replace("postgres://", "postgres://****:****@")
|
||||
);
|
||||
println!(" Pool size: {}", config.pool_size);
|
||||
println!(" Connect timeout: {}s", config.connect_timeout);
|
||||
println!(" Retry attempts: {}", config.retry_attempts);
|
||||
|
||||
println!("\n🔌 Testing PostgreSQL connection...");
|
||||
match test_connection(config).await {
|
||||
Ok(()) => {
|
||||
println!("✅ All PostgreSQL tests passed successfully!");
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
println!("❌ PostgreSQL test failed: {}", e);
|
||||
Err(e.into())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn test_connection(config: PostgresConfig) -> Result<(), PostgresError> {
|
||||
println!(" Creating PostgreSQL connection...");
|
||||
let postgres_conn = PostgresConnection::new(config).await?;
|
||||
let connection = Arc::new(postgres_conn);
|
||||
println!(" ✅ Connection established successfully");
|
||||
|
||||
println!(" Testing basic connectivity...");
|
||||
test_basic_connectivity(&connection).await?;
|
||||
println!(" ✅ Basic connectivity test passed");
|
||||
|
||||
println!(" Testing table existence...");
|
||||
test_table_existence(&connection).await?;
|
||||
println!(" ✅ Table existence test passed");
|
||||
|
||||
println!(" Testing CRUD operations...");
|
||||
test_crud_operations(&connection).await?;
|
||||
println!(" ✅ CRUD operations test passed");
|
||||
|
||||
println!(" Testing transaction support...");
|
||||
test_transactions(&connection).await?;
|
||||
println!(" ✅ Transaction support test passed");
|
||||
|
||||
println!(" Testing error handling...");
|
||||
test_error_handling(&connection).await?;
|
||||
println!(" ✅ Error handling test passed");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_basic_connectivity(
|
||||
connection: &Arc<PostgresConnection>,
|
||||
) -> Result<(), PostgresError> {
|
||||
let statement = sea_orm::Statement::from_string(
|
||||
connection.get_database_backend(),
|
||||
"SELECT 1 as test_value, current_timestamp as current_time".to_string(),
|
||||
);
|
||||
|
||||
let result = connection.query_one(statement).await?.ok_or_else(|| {
|
||||
PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
"No results returned".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
let test_value: Option<i32> = result.try_get("", "test_value").ok();
|
||||
let current_time: Option<String> = result.try_get("", "current_time").ok();
|
||||
|
||||
if test_value != Some(1) {
|
||||
return Err(PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
format!("Expected test_value=1, got {:?}", test_value),
|
||||
)));
|
||||
}
|
||||
|
||||
if current_time.is_none() {
|
||||
return Err(PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
"Expected current_time to be set".to_string(),
|
||||
)));
|
||||
}
|
||||
|
||||
println!(
|
||||
" 📝 Query result: test_value={:?}, current_time={:?}",
|
||||
test_value, current_time
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_table_existence(
|
||||
connection: &Arc<PostgresConnection>,
|
||||
) -> Result<(), PostgresError> {
|
||||
use sea_orm::EntityTrait;
|
||||
|
||||
println!(" 📋 Checking users table...");
|
||||
let user_count = UsersEntity::find()
|
||||
.count(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
println!(
|
||||
" 📊 Users table accessible, current count: {}",
|
||||
user_count
|
||||
);
|
||||
|
||||
println!(" 📋 Checking roles table...");
|
||||
let role_count = RolesEntity::find()
|
||||
.count(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
println!(
|
||||
" 📊 Roles table accessible, current count: {}",
|
||||
role_count
|
||||
);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_crud_operations(
|
||||
connection: &Arc<PostgresConnection>,
|
||||
) -> Result<(), PostgresError> {
|
||||
use sea_orm::{ActiveModelTrait, Set};
|
||||
|
||||
println!(" ➕ Creating test user...");
|
||||
let test_user_id = Uuid::new_v4();
|
||||
let now = Utc::now();
|
||||
|
||||
let user_model = imphnen_entities::seaorm::auth::users::ActiveModel {
|
||||
id: Set(test_user_id),
|
||||
email: Set(format!("test_user_{}@example.com", test_user_id)),
|
||||
password_hash: Set("test_password_hash".to_string()),
|
||||
username: Set(format!("testuser_{}", test_user_id)),
|
||||
first_name: Set(Some("Test".to_string())),
|
||||
last_name: Set(Some("User".to_string())),
|
||||
avatar_url: Set(None),
|
||||
is_verified: Set(false),
|
||||
is_active: Set(true),
|
||||
metadata: Set(None),
|
||||
role_id: Set(None),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
deleted_at: Set(None),
|
||||
};
|
||||
|
||||
let created_user = user_model
|
||||
.insert(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(" ✅ Created user with ID: {}", created_user.id);
|
||||
|
||||
println!(" 🔍 Reading test user...");
|
||||
let found_user = UsersEntity::find_by_id(test_user_id)
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
.ok_or_else(|| {
|
||||
PostgresError::ConnectionError(sea_orm::DbErr::Custom(
|
||||
"User not found after creation".to_string(),
|
||||
))
|
||||
})?;
|
||||
|
||||
println!(
|
||||
" ✅ Found user: {} ({})",
|
||||
found_user.username, found_user.email
|
||||
);
|
||||
|
||||
println!(" ✏️ Updating test user...");
|
||||
let mut update_model: imphnen_entities::seaorm::auth::users::ActiveModel =
|
||||
found_user.into();
|
||||
update_model.first_name = Set(Some("Updated".to_string()));
|
||||
update_model.updated_at = Set(Utc::now());
|
||||
|
||||
let updated_user = update_model
|
||||
.update(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(
|
||||
" ✅ Updated user first name to: {:?}",
|
||||
updated_user.first_name
|
||||
);
|
||||
|
||||
println!(" 🗑️ Deleting test user...");
|
||||
UsersEntity::delete_by_id(updated_user.id)
|
||||
.exec(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?;
|
||||
|
||||
println!(" ✅ Test user deleted successfully");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_transactions(
|
||||
connection: &Arc<PostgresConnection>,
|
||||
) -> Result<(), PostgresError> {
|
||||
println!(" 💰 Testing transaction support...");
|
||||
|
||||
let transaction_result = connection
|
||||
.conn
|
||||
.transaction(|txn| {
|
||||
Box::pin(async move {
|
||||
let test_user_id = Uuid::new_v4();
|
||||
let now = Utc::now();
|
||||
|
||||
let user_model = imphnen_entities::seaorm::auth::users::ActiveModel {
|
||||
id: Set(test_user_id),
|
||||
email: Set(format!("transaction_test_{}@example.com", test_user_id)),
|
||||
password_hash: Set("transaction_password_hash".to_string()),
|
||||
username: Set(format!("transaction_user_{}", test_user_id)),
|
||||
first_name: Set(Some("Transaction".to_string())),
|
||||
last_name: Set(Some("Test".to_string())),
|
||||
avatar_url: Set(None),
|
||||
is_verified: Set(false),
|
||||
is_active: Set(true),
|
||||
metadata: Set(None),
|
||||
role_id: Set(None),
|
||||
created_at: Set(now),
|
||||
updated_at: Set(now),
|
||||
deleted_at: Set(None),
|
||||
};
|
||||
|
||||
let _created_user = user_model.insert(txn).await?;
|
||||
|
||||
Err::<(), DbErr>(DbErr::Custom("Simulated transaction failure".to_string()))
|
||||
})
|
||||
})
|
||||
.await;
|
||||
|
||||
match transaction_result {
|
||||
Err(e) => {
|
||||
let e_text = format!("{:?}", e);
|
||||
if e_text.contains("Simulated transaction failure") {
|
||||
println!(" ✅ Transaction failed as expected, rollback successful");
|
||||
} else {
|
||||
return Err(PostgresError::OperationFailed(format!(
|
||||
"Unexpected transaction result: {}",
|
||||
e_text
|
||||
)));
|
||||
}
|
||||
}
|
||||
Ok(_) => {
|
||||
return Err(PostgresError::OperationFailed(
|
||||
"Unexpected transaction result: transaction unexpectedly succeeded"
|
||||
.to_string(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let user_exists = UsersEntity::find_by_id(Uuid::nil())
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
.is_some();
|
||||
|
||||
if user_exists {
|
||||
println!(" ⚠️ User found despite rollback - this might indicate an issue");
|
||||
} else {
|
||||
println!(" ✅ Transaction rollback verified - user not found");
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn test_error_handling(
|
||||
connection: &Arc<PostgresConnection>,
|
||||
) -> Result<(), PostgresError> {
|
||||
println!(" ⚠️ Testing error handling...");
|
||||
|
||||
println!(" 🔍 Testing invalid UUID handling...");
|
||||
let invalid_uuid = Uuid::nil();
|
||||
|
||||
match UsersEntity::find_by_id(invalid_uuid)
|
||||
.one(&connection.conn)
|
||||
.await
|
||||
.map_err(PostgresError::ConnectionError)?
|
||||
{
|
||||
Some(_) => {
|
||||
println!(" ✅ Found user with nil UUID (expected in some cases)")
|
||||
}
|
||||
None => println!(" ✅ No user found with nil UUID (expected)"),
|
||||
}
|
||||
|
||||
println!(" 🔍 Testing invalid query handling...");
|
||||
let invalid_statement = sea_orm::Statement::from_string(
|
||||
connection.get_database_backend(),
|
||||
"SELECT * FROM non_existent_table".to_string(),
|
||||
);
|
||||
|
||||
match connection.execute(invalid_statement).await {
|
||||
Err(_) => println!(" ✅ Invalid query properly handled with error"),
|
||||
Ok(_) => println!(" ⚠️ Invalid query unexpectedly succeeded"),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub mod test_utils {
|
||||
use super::*;
|
||||
|
||||
pub fn create_test_config() -> PostgresConfig {
|
||||
PostgresConfig {
|
||||
database_url: "postgres://postgres:postgres@localhost:5432/imphnen_test"
|
||||
.to_string(),
|
||||
pool_size: 5,
|
||||
connect_timeout: 10,
|
||||
idle_timeout: 30,
|
||||
max_lifetime: Some(600),
|
||||
retry_attempts: 2,
|
||||
retry_delay: 1,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_test_user_model() -> UserModel {
|
||||
UserModel {
|
||||
id: Uuid::new_v4(),
|
||||
email: format!("test_{}@example.com", Uuid::new_v4()),
|
||||
password_hash: "test_password_hash".to_string(),
|
||||
username: format!("testuser_{}", Uuid::new_v4()),
|
||||
first_name: Some("Test".to_string()),
|
||||
last_name: Some("User".to_string()),
|
||||
avatar_url: None,
|
||||
is_verified: false,
|
||||
is_active: true,
|
||||
metadata: None,
|
||||
role_id: None,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_test_role_model() -> RoleModel {
|
||||
RoleModel {
|
||||
id: Uuid::new_v4(),
|
||||
name: format!("test_role_{}", Uuid::new_v4()),
|
||||
description: "Test role description".to_string(),
|
||||
permissions: Some(serde_json::json!(["test.permission"])),
|
||||
is_system_role: false,
|
||||
is_default: false,
|
||||
created_at: Utc::now(),
|
||||
updated_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_create_test_config() {
|
||||
let config = test_utils::create_test_config();
|
||||
assert_eq!(config.pool_size, 5);
|
||||
assert_eq!(config.connect_timeout, 10);
|
||||
assert!(config.database_url.contains("imphnen_test"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_test_user_model() {
|
||||
let user = test_utils::create_test_user_model();
|
||||
assert!(!user.email.is_empty());
|
||||
assert!(!user.username.is_empty());
|
||||
assert!(user.is_active);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_create_test_role_model() {
|
||||
let role = test_utils::create_test_role_model();
|
||||
assert!(!role.name.is_empty());
|
||||
assert!(role.permissions.is_some());
|
||||
assert!(!role.is_system_role);
|
||||
}
|
||||
}
|
||||
|
||||
+10
-13
@@ -1,13 +1,10 @@
|
||||
use imphnen_gateway::gateway_service;
|
||||
use imphnen_libs::axum_init;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let _ = axum_init(|postgres_conn| async {
|
||||
// PostgreSQL is now the primary database - SurrealDB has been completely removed
|
||||
gateway_service(postgres_conn).await
|
||||
})
|
||||
.await;
|
||||
}
|
||||
use imphnen_gateway::gateway_service;
|
||||
use imphnen_libs::axum_init;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
tracing_subscriber::fmt::init();
|
||||
|
||||
let _ =
|
||||
axum_init(|postgres_conn| async { gateway_service(postgres_conn).await }).await;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user