postgress

This commit is contained in:
MythEclipse
2025-12-01 00:20:42 +07:00
parent 6fe495eed1
commit b429b3a9c7
325 changed files with 35728 additions and 50259 deletions
+7 -15
View File
@@ -8,8 +8,8 @@ name = "api"
path = "src/main.rs"
[[bin]]
name = "clear_db_test"
path = "src/bin/clear_db_test.rs"
name = "create_schema"
path = "src/bin/create_schema.rs"
[[bin]]
name = "seeder"
@@ -39,19 +39,16 @@ path = "src/bin/seed_roles.rs"
name = "seed_roles_permissions"
path = "src/bin/seed_roles_permissions.rs"
[[bin]]
name = "seed_teams"
path = "src/bin/seed_teams.rs"
[[bin]]
name = "seed_hackathons"
path = "src/bin/seed_hackathons.rs"
[[bin]]
name = "seed_test_data"
path = "src/bin/seed_test_data.rs"
[[bin]]
name = "test_postgres"
path = "src/bin/test_postgres.rs"
[dependencies]
sea-orm.workspace = true
imphnen-libs.workspace = true
imphnen-utils.workspace = true
imphnen-gateway.workspace = true
@@ -60,7 +57,6 @@ imphnen-iam.workspace = true
imphnen-cms.workspace = true
imphnen-gacha.workspace = true
imphnen-dimentorin.workspace = true
imphnen-hackathon.workspace = true
axum.workspace = true
serde.workspace = true
serde_json.workspace = true
@@ -69,7 +65,6 @@ lazy_static.workspace = true
regex.workspace = true
validator.workspace = true
axum-test.workspace = true
surrealdb.workspace = true
rand.workspace = true
tokio.workspace = true
chrono.workspace = true
@@ -80,6 +75,3 @@ env_logger.workspace = true
tracing.workspace = true
tracing-subscriber.workspace = true
uuid.workspace=true
tokio-tungstenite.workspace = true
url.workspace = true
futures-util.workspace = true
+14 -11
View File
@@ -1,11 +1,14 @@
use imphnen_gateway::gateway_service;
use imphnen_libs::axum_init;
#[tokio::main]
async fn main() {
env_logger::init();
axum_init(|surrealdb_ws, surrealdb_mem| async {
gateway_service(surrealdb_ws, surrealdb_mem).await
})
.await;
}
// 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;
}
+93
View File
@@ -0,0 +1,93 @@
#![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(())
}
-169
View File
@@ -1,169 +0,0 @@
use tokio_tungstenite::{connect_async, tungstenite::protocol::Message};
use url::Url;
use futures_util::{StreamExt, SinkExt};
use serde_json::json;
// Menentukan kredensial dan detail koneksi secara langsung sebagai string statis
static SURREALDB_URL_WS: &str = "ws://localhost:8000/rpc";
static SURREALDB_USERNAME: &str = "root";
static SURREALDB_PASSWORD: &str = "root";
static SURREALDB_NAMESPACE: &str = "test";
static SURREALDB_DBNAME: &str = "test";
// Daftar tabel sebagai variabel static yang tidak dapat diubah
static TABLES_TO_CLEAR: &[&str] = &[
"app_events", "users", "roles", "permissions", "gacha_rolls",
"mentor_users", "gacha_claims", "gacha_credits", "gacha_items",
"mentor_profiles", "roles_permissions", "testimonials",
];
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Tidak perlu memuat env lagi, karena kita menggunakan nilai hardcoded
// imphnen_libs::enviroment::load_env(); // Baris ini tidak lagi dibutuhkan
// let env = Env::new(); // Baris ini tidak lagi dibutuhkan
println!("DEBUG: URL WS: {}", SURREALDB_URL_WS);
println!("DEBUG: Username: {}", SURREALDB_USERNAME);
println!("DEBUG: Namespace: {}", SURREALDB_NAMESPACE);
println!("DEBUG: Database: {}", SURREALDB_DBNAME);
let url = Url::parse(SURREALDB_URL_WS)?; // Menggunakan SURREALDB_URL_WS statis
let (ws_stream, _) = connect_async(url.as_str()).await?;
let (mut write, mut read) = ws_stream.split();
// Authenticate (signin)
let signin_query = json!({
"method": "signin",
"params": [{
"user": SURREALDB_USERNAME, // Menggunakan SURREALDB_USERNAME statis
"pass": SURREALDB_PASSWORD, // Menggunakan SURREALDB_PASSWORD statis
}],
"id": 1
}).to_string();
println!("DEBUG: Sending signin query: {}", signin_query);
write.send(Message::Text(signin_query.into())).await?;
let signin_response = read.next().await.ok_or("Failed to read signin response")?;
let signin_response_msg = signin_response?;
let signin_response_str = signin_response_msg.to_text()?;
println!("DEBUG: Signin response: {}", signin_response_str);
if signin_response_str.contains("\"error\":") {
return Err(format!("Signin failed: {}", signin_response_str).into());
}
// Use namespace and database
let use_query = json!({
"method": "use",
"params": [SURREALDB_NAMESPACE, SURREALDB_DBNAME], // Menggunakan NS & DB statis
"id": 2
}).to_string();
println!("DEBUG: Sending use query: {}", use_query);
write.send(Message::Text(use_query.into())).await?;
let use_response = read.next().await.ok_or("Failed to read use response")?;
let use_response_msg = use_response?;
let use_response_str = use_response_msg.to_text()?;
println!("DEBUG: Use response: {}", use_response_str);
if use_response_str.contains("\"error\":") {
return Err(format!("USE command failed: {}", use_response_str).into());
}
println!("INFO: Attempting to clear database tables via WebSocket...");
let mut all_clear = true;
for (i, table) in TABLES_TO_CLEAR.iter().enumerate() {
let remove_query = format!("REMOVE TABLE {};", table);
let query_json = json!({
"method": "query",
"params": [remove_query],
"id": i + 3
}).to_string();
println!("DEBUG: Attempting REMOVE TABLE {}: {}", table, query_json);
write.send(Message::Text(query_json.into())).await?;
let response_result = read.next().await.ok_or("Stream ended unexpectedly")?;
match response_result {
Ok(msg) => {
let response_str = msg.to_text()?;
if response_str.contains("\"error\":") {
println!("WARN: Failed to REMOVE TABLE {}: {}. Attempting DELETE type::{}.", table, response_str, table);
let delete_all_query = format!("DELETE FROM {};", table);
let delete_all_json = json!({
"method": "query",
"params": [delete_all_query],
"id": i + 300
}).to_string();
println!("DEBUG: Attempting DELETE {}: {}", table, delete_all_json);
write.send(Message::Text(delete_all_json.into())).await?;
let delete_response_result = read.next().await.ok_or("Stream ended unexpectedly during DELETE type::")?;
match delete_response_result {
Ok(delete_msg) => {
let delete_response_str = delete_msg.to_text()?;
if delete_response_str.contains("\"error\":") {
println!("ERROR: Failed to DELETE type::{} : {}", table, delete_response_str);
all_clear = false;
} else {
println!("INFO: Successfully DELETED type:: table: {}", table);
}
},
Err(delete_e) => {
println!("ERROR: Error receiving response for DELETE type:: table {}: {}", table, delete_e);
all_clear = false;
}
}
} else {
println!("INFO: Successfully REMOVED TABLE: {}", table);
}
},
Err(e) => {
println!("ERROR: Error receiving response for REMOVE TABLE {}: {}", table, e);
all_clear = false;
}
}
// Check if table is empty after deletion attempt
let select_query = format!("SELECT * FROM {} LIMIT 1;", table);
let select_json = json!({
"method": "query",
"params": [select_query],
"id": i + 1000
}).to_string();
write.send(Message::Text(select_json.into())).await?;
let select_response_result = read.next().await.ok_or("Stream ended unexpectedly during SELECT check")?;
match select_response_result {
Ok(select_msg) => {
let select_response_str = select_msg.to_text()?;
if select_response_str.contains("does not exist") {
println!("CHECK: Table '{}' does not exist after clear attempt (success).", table);
} else if select_response_str.contains("\"result\":[]") || select_response_str.contains("\"result\":[[]]") {
println!("CHECK: Table '{}' is empty after clear attempt.", table);
} else {
println!("WARNING: Table '{}' is NOT empty after clear attempt! Response: {}", table, select_response_str);
all_clear = false;
}
},
Err(e) => {
println!("ERROR: Error receiving response for SELECT check on table {}: {}", table, e);
all_clear = false;
}
}
}
println!("INFO: Database clearing complete.");
if !all_clear {
eprintln!("ERROR: One or more tables could not be cleared. Check logs for details.");
return Err("Database clearing failed for one or more tables.".into());
}
Ok(())
}
+65
View File
@@ -0,0 +1,65 @@
#![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;
#[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!(" Database connected. Creating/updating tables...");
// 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!("✅ 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
where
E: EntityTrait,
{
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);
// Create table
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);
Ok(())
}
+21 -19
View File
@@ -1,19 +1,21 @@
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();
// 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);
}
}
}
+127 -35
View File
@@ -1,21 +1,17 @@
use imphnen_cms::v1::landing::events::events_schema::EventsSchema;
use imphnen_utils::{get_iso_date};
#![allow(clippy::all)]
use std::error::Error;
use surrealdb::engine::any;
use surrealdb::{opt::auth::Root, sql::Thing, Uuid}; // Added Uuid
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 uuid::Uuid;
use chrono::Utc; // Removed NaiveDateTime as it was unused
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let postgres_config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(postgres_config).await?;
let db = &pg_conn.conn;
let events = vec![
(
@@ -58,6 +54,97 @@ 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.",
"https://rust-bootcamp.example.com",
200.0,
Some("Bandung Digital Valley".to_string()),
false,
"2025-10-01T09:00:00Z",
"2025-10-03T17:00:00Z",
),
(
"AI & Machine Learning Summit",
"Global summit discussing the future of AI and its impact on industries.",
"https://ai-summit.example.com",
300.0,
Some("Bali Nusa Dua Convention Center".to_string()),
false,
"2025-11-15T08:00:00Z",
"2025-11-17T18:00:00Z",
),
(
"Cybersecurity Awareness Webinar",
"Free webinar on best practices for personal and corporate cybersecurity.",
"https://cybersecurity-webinar.example.com",
0.0,
None,
true,
"2025-12-05T14:00:00Z",
"2025-12-05T16:00:00Z",
),
(
"Cloud Computing Workshop",
"Hands-on workshop on deploying scalable applications using AWS and Azure.",
"https://cloud-workshop.example.com",
120.0,
None,
true,
"2026-01-20T10:00:00Z",
"2026-01-22T15:00:00Z",
),
(
"Blockchain for Finance",
"Exploring the applications of blockchain technology in the financial sector.",
"https://blockchain-finance.example.com",
180.0,
Some("Jakarta Ritz-Carlton".to_string()),
false,
"2026-02-10T09:00:00Z",
"2026-02-11T17:00:00Z",
),
(
"Game Development Jam",
"48-hour game development marathon for indie developers.",
"https://game-jam.example.com",
50.0,
Some("Yogyakarta Creative Hub".to_string()),
false,
"2026-03-15T18:00:00Z",
"2026-03-17T18:00:00Z",
),
(
"UX/UI Design Principles",
"Masterclass on creating intuitive and user-friendly interfaces.",
"https://uxui-design.example.com",
90.0,
None,
true,
"2026-04-05T13:00:00Z",
"2026-04-07T16:00:00Z",
),
(
"Data Science Fundamentals",
"Introduction to data analysis, visualization, and statistical modeling.",
"https://data-science.example.com",
110.0,
None,
true,
"2026-05-12T10:00:00Z",
"2026-05-14T15:00:00Z",
),
(
"IoT Innovation Expo",
"Showcase of the latest Internet of Things devices and solutions.",
"https://iot-expo.example.com",
50.0,
Some("Surabaya Expo Center".to_string()),
false,
"2026-06-20T09:00:00Z",
"2026-06-22T18:00:00Z",
),
];
for (
@@ -67,29 +154,34 @@ async fn main() -> Result<(), Box<dyn Error>> {
price,
location,
is_online,
start_date,
end_date,
start_date_str, // Renamed to avoid conflict
end_date_str, // Renamed to avoid conflict
) in events
{
let uuid = Uuid::new_v4().to_string(); // Generate new UUID
let event = EventsSchema {
id: Thing::from(("app_events", uuid.as_str())), // Use generated UUID
name: name.into(),
description: description.into(),
detail_link: detail_link.into(),
price,
location,
is_online,
is_deleted: false,
start_date: start_date.into(),
end_date: end_date.into(),
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
// Check if event already exists by name
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;
}
db.create::<Option<EventsSchema>>(("app_events", uuid.as_str())) // Use generated UUID
.content(event)
.await?;
let uuid = Uuid::new_v4(); // Generate a Uuid
let mut event_model: EventsActiveModel = Default::default();
event_model.id = Set(uuid);
event_model.name = Set(name.to_string());
event_model.description = Set(description.to_string());
event_model.detail_link = Set(detail_link.to_string());
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.insert(db).await?;
println!(
"✅ Inserted event: {} ({})",
@@ -100,4 +192,4 @@ async fn main() -> Result<(), Box<dyn Error>> {
println!("✅ All Events seeded");
Ok(())
}
}
+68 -41
View File
@@ -1,51 +1,78 @@
use imphnen_utils::{get_iso_date};
#![allow(clippy::all)]
use std::error::Error;
use surrealdb::opt::auth::Root;
use surrealdb::sql::Thing;
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 sea_orm::ActiveModelTrait;
use sea_orm::ActiveValue::Set;
use uuid::Uuid;
use sea_orm::ConnectionTrait;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(config).await?;
let db = &pg_conn.conn;
db.query("DELETE type::thing('app_gacha_items', $id)")
.bind(("id", "1"))
.await?;
db.query("DELETE type::thing('app_gacha_rolls', $id)")
.bind(("id", "test-gacha-roll-001"))
.await?;
let gacha_item_id = "1";
db.query("CREATE type::thing('app_gacha_items', $id) SET name = $name, image_url = $image_url, is_deleted = $is_deleted, created_at = $created_at, updated_at = $updated_at")
.bind(("id", gacha_item_id))
.bind(("name", "Test Gacha Item"))
.bind(("image_url", "https://example.com/gacha_item.png"))
.bind(("is_deleted", false))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.await?;
println!("Gacha Item seeded successfully!");
// 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 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 new_uuid = Uuid::new_v4();
let mut item_model: GachaItemActiveModel = Default::default();
item_model.id = Set(new_uuid);
item_model.item_code = Set("ITEM_TEST_1".to_string());
item_model.name = Set("Test Gacha Item".to_string());
item_model.description = Set("Test item for gacha".to_string());
item_model.rarity = Set("common".to_string());
item_model.type_ = Set("item".to_string());
item_model.category = Set("test".to_string());
item_model.value = Set(1);
item_model.weight = Set(1.0);
item_model.stock = Set(10);
item_model.is_limited = Set(false);
item_model.created_at = Set(chrono::Utc::now());
item_model.updated_at = Set(chrono::Utc::now());
item_model.insert(db).await?;
println!("Gacha Item seeded successfully!");
new_uuid
};
let gacha_roll_id = "test-gacha-roll-001";
db.query("CREATE type::thing('app_gacha_rolls', $id) SET item = $item, quantity = $quantity, weight = $weight, is_deleted = $is_deleted, created_at = $created_at, updated_at = $updated_at")
.bind(("id", gacha_roll_id))
.bind(("item", Thing::from(("app_gacha_items", gacha_item_id))))
.bind(("quantity", 10))
.bind(("weight", 1.0))
.bind(("is_deleted", false))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.await?;
// 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);
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 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?;
println!("✅ Gacha items and rolls seeded.");
Ok(())
}
-368
View File
@@ -1,368 +0,0 @@
use chrono::{DateTime, Utc};
use imphnen_hackathon::v1::hackathon::hackathon_schema::{
HackathonSchema, HackathonEventsSchema, HackathonTimelineSchema, HackathonSubmissionsSchema,
HackathonStatus, HackathonEventType, HackathonPhase, SubmissionStatus, Prize
};
use imphnen_utils::get_iso_date;
use std::error::Error;
use surrealdb::{opt::auth::Root, sql::Thing};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
// Sample hackathon data
let hackathons = vec![
(
"hackathon-001",
"AI Innovation Challenge 2025",
"Build the next generation of AI-powered applications that solve real-world problems.",
"2025-10-15T09:00:00Z",
"2025-10-17T18:00:00Z",
"2025-10-01T23:59:59Z",
Some(100),
HackathonStatus::RegistrationOpen,
Some("Artificial Intelligence & Machine Learning".to_string()),
Some("1. All code must be original\n2. Teams can have 2-5 members\n3. Projects must use AI/ML technologies".to_string()),
Some(vec![
Prize { position: 1, title: "Grand Prize".to_string(), description: Some("Winner gets full scholarship".to_string()), value: Some("$10,000".to_string()) },
Prize { position: 2, title: "Second Place".to_string(), description: Some("Runner-up prize".to_string()), value: Some("$5,000".to_string()) },
Prize { position: 3, title: "Third Place".to_string(), description: Some("Third place prize".to_string()), value: Some("$2,500".to_string()) },
]),
vec!["c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2".to_string()], // admin user
),
(
"hackathon-002",
"Green Tech Hackathon",
"Develop sustainable technology solutions for environmental challenges.",
"2025-11-20T10:00:00Z",
"2025-11-22T17:00:00Z",
"2025-11-05T23:59:59Z",
Some(75),
HackathonStatus::Draft,
Some("Sustainability & Green Technology".to_string()),
Some("Focus on renewable energy, waste reduction, and environmental monitoring.".to_string()),
Some(vec![
Prize { position: 1, title: "Eco Champion".to_string(), description: Some("Best environmental impact".to_string()), value: Some("$7,500".to_string()) },
Prize { position: 2, title: "Innovation Award".to_string(), description: Some("Most innovative solution".to_string()), value: Some("$3,500".to_string()) },
]),
vec!["c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2".to_string()],
),
];
// Sample hackathon events
let hackathon_events = vec![
(
"hackathon-001",
"event-001",
"Opening Ceremony",
Some("Welcome and kickoff event for the AI Innovation Challenge".to_string()),
HackathonEventType::Ceremony,
"2025-10-15T09:00:00Z",
"2025-10-15T10:00:00Z",
Some("Main Auditorium".to_string()),
None,
Some(150),
true,
),
(
"hackathon-001",
"event-002",
"AI Workshop: Getting Started",
Some("Introduction to AI frameworks and tools".to_string()),
HackathonEventType::Workshop,
"2025-10-15T14:00:00Z",
"2025-10-15T16:00:00Z",
None,
Some("https://zoom.us/meeting/ai-workshop".to_string()),
Some(80),
false,
),
(
"hackathon-001",
"event-003",
"Judging Session",
Some("Final project presentations and judging".to_string()),
HackathonEventType::Judging,
"2025-10-17T14:00:00Z",
"2025-10-17T17:00:00Z",
Some("Innovation Lab".to_string()),
None,
Some(100),
true,
),
];
// Sample hackathon timeline
let hackathon_timeline = vec![
(
"hackathon-001",
HackathonPhase::Registration,
"Registration Phase",
Some("Register your team and submit initial project ideas".to_string()),
"2025-10-01T00:00:00Z",
"2025-10-10T23:59:59Z",
true,
1,
),
(
"hackathon-001",
HackathonPhase::Ideation,
"Ideation & Planning",
Some("Brainstorm and plan your AI solution".to_string()),
"2025-10-11T00:00:00Z",
"2025-10-14T23:59:59Z",
false,
2,
),
(
"hackathon-001",
HackathonPhase::Development,
"Development Sprint",
Some("Build your AI-powered application".to_string()),
"2025-10-15T00:00:00Z",
"2025-10-16T23:59:59Z",
false,
3,
),
(
"hackathon-001",
HackathonPhase::Submission,
"Project Submission",
Some("Submit your final project and demo video".to_string()),
"2025-10-17T00:00:00Z",
"2025-10-17T12:00:00Z",
false,
4,
),
(
"hackathon-001",
HackathonPhase::Judging,
"Judging & Awards",
Some("Presentations and prize ceremony".to_string()),
"2025-10-17T13:00:00Z",
"2025-10-17T18:00:00Z",
false,
5,
),
];
// Sample hackathon submissions
let hackathon_submissions = vec![
(
"hackathon-001",
"team-dev-001",
"AI-Powered Health Monitor",
"A machine learning application that predicts health risks using wearable device data.",
Some("https://github.com/team-dev/ai-health-monitor".to_string()),
Some("https://demo.ai-health-monitor.com".to_string()),
None,
vec!["Python".to_string(), "TensorFlow".to_string(), "React".to_string()],
SubmissionStatus::Submitted,
"2025-10-17T11:30:00Z",
),
(
"hackathon-001",
"team-design-001",
"Smart City Traffic Optimizer",
"AI system that optimizes traffic flow using computer vision and predictive analytics.",
Some("https://github.com/team-design/smart-traffic".to_string()),
Some("https://demo.smart-traffic.com".to_string()),
Some("https://slides.smart-traffic.com/presentation".to_string()),
vec!["JavaScript".to_string(), "Node.js".to_string(), "OpenCV".to_string()],
SubmissionStatus::UnderReview,
"2025-10-17T10:45:00Z",
),
];
// Seed hackathons
for (
id,
name,
description,
start_date,
end_date,
registration_deadline,
max_participants,
status,
theme,
rules,
prizes,
organizers,
) in hackathons {
db.query("DELETE type::thing('app_hackathons', $id)")
.bind(("id", id))
.await?;
let hackathon = HackathonSchema {
id: Thing::from(("app_hackathons", id)),
name: name.into(),
description: description.into(),
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
registration_deadline: DateTime::parse_from_rfc3339(registration_deadline)?.with_timezone(&Utc),
max_participants,
status,
theme,
rules,
prizes,
previous_winners: None,
organizers,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonSchema>>(("app_hackathons", id))
.content(hackathon)
.await?;
println!("✅ Inserted hackathon: {name}");
}
// Seed hackathon events
for (
hackathon_id,
event_id,
title,
description,
event_type,
start_time,
end_time,
location,
virtual_link,
max_attendees,
is_mandatory,
) in hackathon_events {
db.query("DELETE type::thing('app_hackathon_events', $id)")
.bind(("id", event_id))
.await?;
let event = HackathonEventsSchema {
id: Thing::from(("app_hackathon_events", event_id)),
hackathon_id: Thing::from(("app_hackathons", hackathon_id)),
title: title.into(),
description,
event_type,
start_time: DateTime::parse_from_rfc3339(start_time)?.with_timezone(&Utc),
end_time: DateTime::parse_from_rfc3339(end_time)?.with_timezone(&Utc),
location,
virtual_link,
max_attendees,
is_mandatory,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonEventsSchema>>(("app_hackathon_events", event_id))
.content(event)
.await?;
println!("✅ Inserted hackathon event: {title}");
}
// Seed hackathon timeline
for (
hackathon_id,
phase,
title,
description,
start_date,
end_date,
is_active,
order,
) in hackathon_timeline {
let timeline_id = format!("timeline-{}-{}", hackathon_id, order);
db.query("DELETE type::thing('app_hackathon_timeline', $id)")
.bind(("id", timeline_id.clone()))
.await?;
let timeline = HackathonTimelineSchema {
id: Thing::from(("app_hackathon_timeline", timeline_id.as_str())),
hackathon_id: Thing::from(("app_hackathons", hackathon_id)),
phase,
title: title.into(),
description,
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
is_active,
order,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonTimelineSchema>>(("app_hackathon_timeline", timeline_id))
.content(timeline)
.await?;
println!("✅ Inserted hackathon timeline: {title}");
}
// Seed hackathon submissions
for (
hackathon_id,
team_id,
project_name,
description,
repository_url,
demo_url,
slides_url,
technologies,
submission_status,
submitted_at,
) in hackathon_submissions {
let submission_id = format!("submission-{}-{}", hackathon_id, team_id);
db.query("DELETE type::thing('app_hackathon_submissions', $id)")
.bind(("id", submission_id.clone()))
.await?;
let submission = HackathonSubmissionsSchema {
id: Thing::from(("app_hackathon_submissions", submission_id.as_str())),
hackathon_id: Thing::from(("app_hackathons", hackathon_id)),
judge_feedback: None,
team_id: Some(Thing::from(("app_teams", team_id))),
project_name: Some(project_name.into()),
description: Some(description.into()),
repository_url,
upload_file_url: None,
demo_url,
slides_url,
technologies: Some(technologies),
contact_instagram: None,
contact_twitter: None,
contact_linkedin: None,
contact_facebook: None,
contact_youtube: None,
contact_tiktok: None,
contact_other: None,
submission_status: Some(submission_status),
submitted_at: Some(DateTime::parse_from_rfc3339(submitted_at)?.with_timezone(&Utc)),
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonSubmissionsSchema>>(("app_hackathon_submissions", submission_id))
.content(submission)
.await?;
println!("✅ Inserted hackathon submission: {project_name}");
}
println!("✅ All Hackathons seeded");
Ok(())
}
+57 -79
View File
@@ -1,92 +1,70 @@
use imphnen_utils::{get_iso_date, hash_password};
#![allow(clippy::all)]
use imphnen_libs::hash_password;
use serde_json::json;
use std::error::Error;
use surrealdb::opt::auth::Root;
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]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(config).await?;
let db = &pg_conn.conn;
db.query("DELETE type::thing('app_mentors', $id)")
.bind(("id", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.await?;
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();
db.query("DELETE type::thing('app_users', $id)")
.bind(("id", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.await?;
// Find Mentor role
let role = RoleEntity::find()
.filter(RoleColumn::Name.eq("Mentor"))
.one(db)
.await?
.ok_or("Role 'Mentor' not found")?;
use surrealdb::sql::Thing;
db.query("CREATE type::thing('app_users', $id) SET fullname = $fullname, email = $email, password = $password, avatar = $avatar, phone_number = $phone_number, is_active = $is_active, is_deleted = $is_deleted, mentor_id = $mentor_id, gender = $gender, birthdate = $birthdate, role = $role, legal_name = $legal_name, domicile = $domicile, identity_document_url = $identity_document_url, phone_for_verification = $phone_for_verification, bio = $bio, last_education = $last_education, linkedin_url = $linkedin_url, github_url = $github_url, cv_url = $cv_url, portfolio_url = $portfolio_url, created_at = $created_at, updated_at = $updated_at")
.bind(("id", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.bind(("fullname", "Mentor User"))
.bind(("email", "mentor@example.com"))
.bind(("password", hash_password("password").unwrap()))
.bind(("avatar", Option::<String>::None))
.bind(("phone_number", "081234567890"))
.bind(("is_active", true))
.bind(("is_deleted", false))
.bind(("mentor_id", Option::<Thing>::None))
.bind(("gender", "male"))
.bind(("birthdate", "1990-05-15"))
.bind(("role", Thing::from(("app_roles", "3b9f8c4e-6a2d-4f8a-9a12-2d6f8b3c4e5a"))))
.bind(("legal_name", "Mentor User"))
.bind(("domicile", "Jakarta, Indonesia"))
// .bind(("identity_document_url", "https://example.com/ktp.jpg"))
.bind(("phone_for_verification", "081234567890"))
.bind(("bio", "Saya adalah mentor backend Rust dengan pengalaman 5 tahun dalam pengembangan aplikasi backend yang scalable dan performant."))
.bind(("last_education", "S1 Teknik Informatika"))
.bind(("linkedin_url", "https://linkedin.com/in/mentor"))
.bind(("github_url", "https://github.com/mentor"))
.bind(("cv_url", Option::<String>::None))
.bind(("portfolio_url", Option::<String>::None))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.await?;
// Insert user with Mentor role
let user_id = Uuid::new_v4();
let mut user_model: UsersActiveModel = Default::default();
user_model.id = Set(user_id);
user_model.email = Set("mentor@example.com".to_string());
user_model.password_hash = Set(hash_password("password").unwrap());
user_model.username = Set("mentor@example.com".to_string());
user_model.first_name = Set(Some("Mentor".to_string()));
user_model.last_name = Set(Some("User".to_string()));
user_model.avatar_url = Set(Some("https://example.com/avatar.jpg".to_string()));
user_model.is_active = Set(true);
user_model.is_verified = Set(true);
user_model.role_id = Set(Some(role.id));
user_model.created_at = Set(chrono::Utc::now());
user_model.updated_at = Set(chrono::Utc::now());
user_model.insert(db).await?;
db.query("CREATE type::thing('app_mentors', $id) SET user_id = $user_id, industries = $industries, expertise = $expertise, languages = $languages, current_company = $current_company, current_role = $current_role, years_of_experience = $years_of_experience, topics_of_interest = $topics_of_interest, preferred_mentee_level = $preferred_mentee_level, preferred_mentoring_formats = $preferred_mentoring_formats, availability_commitment = $availability_commitment, mentoring_rate = $mentoring_rate, status = $status, is_deleted = $is_deleted, created_at = $created_at, updated_at = $updated_at")
.bind(("id", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.bind(("user_id", Thing::from(("app_users", "e6f78d23-83bf-5c2b-bcd4-001345678901"))))
.bind(("industries", vec!["Software", "Education"]))
.bind(("expertise", vec!["Rust", "Microservices"]))
.bind(("languages", vec!["Indonesian", "English"]))
.bind(("current_company", "PT Contoh"))
.bind(("current_role", "Senior Backend Engineer"))
.bind(("years_of_experience", 5))
.bind(("topics_of_interest", vec!["Rust Programming", "Backend Development"]))
.bind(("preferred_mentee_level", vec!["beginner", "intermediate"]))
.bind(("preferred_mentoring_formats", vec!["online", "offline"]))
.bind(("availability_commitment", "2 jam per minggu untuk mentoring online dan offline"))
.bind(("mentoring_rate", json!({
"amount": 100000,
"currency": "IDR",
"per_duration": "hour"
})))
.bind(("status", "verified"))
.bind(("is_deleted", false))
.bind(("created_at", get_iso_date()))
.bind(("updated_at", get_iso_date()))
.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.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.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.mentoring_rate = Set(Some(100000.0));
mentor_model.status = Set(Some("verified".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!("Mentor created successfully!");
println!("Updating user with mentor_id...");
db.query("UPDATE type::thing('app_users', $id) SET mentor_id = $mentor_id")
.bind(("id", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.bind((
"mentor_id",
Thing::from(("app_mentors", "e6f78d23-83bf-5c2b-bcd4-001345678901")),
))
.await?;
println!("User updated with mentor_id successfully!");
println!("✅ Inserted mentor user: mentor@example.com");
println!("✅ Mentor user seeded");
+81 -77
View File
@@ -1,77 +1,81 @@
use imphnen_iam::PermissionsEnum;
use imphnen_utils::{get_iso_date};
use serde_json::json;
use std::error::Error;
use surrealdb::engine::any;
use surrealdb::opt::auth::Root;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
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,
] {
db.query("CREATE type::thing('app_permissions', $id) CONTENT $data")
.bind(("id", permission.id()))
.bind((
"data",
json!({
"name": permission.to_string(),
"is_deleted": false,
"created_at": get_iso_date(),
"updated_at": get_iso_date()
}),
))
.await?;
println!("✅ Inserted: {permission}");
}
println!("✅ All Permissions seeded");
Ok(())
}
#![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(())
}
+36 -32
View File
@@ -1,20 +1,15 @@
use imphnen_utils::{get_iso_date};
use serde_json::json;
use std::error::Error;
use surrealdb::engine::any;
use surrealdb::opt::auth::Root;
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
use imphnen_entities::seaorm::auth::roles::{RoleBuilder, Entity as RoleEntity};
use sea_orm::{ActiveModelTrait, ActiveValue::Set, EntityTrait};
use uuid::Uuid;
use chrono::Utc; // Added chrono
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(config).await?;
let db = &pg_conn.conn;
let roles = vec![
(
@@ -55,25 +50,34 @@ async fn main() -> Result<(), Box<dyn Error>> {
),
];
for (id, name, _created_at, _updated_at) in roles {
db.query("DELETE type::thing('app_roles', $id)")
.bind(("id", id))
.await?;
db.query("CREATE type::thing('app_roles', $id) CONTENT $data")
.bind(("id", id))
.bind((
"data",
json!({
"name": name,
"permissions": [],
"is_deleted": false,
"created_at": get_iso_date(),
"updated_at": get_iso_date(),
}),
))
.await?;
for (id, name, _created_at_str, _updated_at_str) in roles { // Renamed to avoid conflict
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())
.permissions(vec![])
.is_default(false)
.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.insert(db).await?;
println!("✅ Inserted role: {name}");
}
println!("✅ All Roles seeded");
Ok(())
}
}
+112 -115
View File
@@ -1,115 +1,112 @@
use imphnen_iam::{get_iso_date, make_thing, PermissionsEnum};
use std::error::Error;
use surrealdb::engine::any;
use surrealdb::opt::auth::Root;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
db.query("DEFINE INDEX user_email_index ON TABLE users COLUMNS email UNIQUE;")
.await?;
db.query("DEFINE INDEX role_name_idx ON TABLE roles COLUMNS name UNIQUE;")
.await?;
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,
PermissionsEnum::ManageAllTeams,
],
),
(
"60f1aeb7-dad2-4e06-bcb5-be1ba510c906",
vec![PermissionsEnum::ActivateUsers],
),
("6d4fea5d-4a08-4b8a-9782-f2ab2183dcf0", vec![]),
];
for (role_id, permissions) in roles_permissions {
let permission_refs: Vec<_> = permissions
.iter()
.map(|perm| make_thing("app_permissions", &perm.id()))
.collect();
db.query("UPDATE type::thing('app_roles', $role_id) SET permissions = $permissions, updated_at = $updated_at WHERE is_deleted = false")
.bind(("role_id", role_id))
.bind(("permissions", permission_refs))
.bind(("updated_at", get_iso_date()))
.await?;
println!("Permissions updated for role: {role_id}");
}
println!("✅ All roles permissions updated!");
Ok(())
}
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(())
}
-85
View File
@@ -1,85 +0,0 @@
use imphnen_iam::v1::teams::TeamsSchema;
use imphnen_utils::get_iso_date;
use std::error::Error;
use surrealdb::{opt::auth::Root, sql::Thing};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let teams = vec![
(
"team-dev-001",
"Development Team",
Some("Core development team for the platform".to_string()),
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2", // admin user
true,
Some(10),
Some(vec!["Rust".to_string(), "Backend".to_string()]),
Some("Remote".to_string()),
),
(
"team-design-001",
"Design Team",
Some("UI/UX design team".to_string()),
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2", // admin user
true,
Some(5),
Some(vec!["Figma".to_string(), "Design".to_string()]),
Some("Remote".to_string()),
),
(
"team-qa-001",
"Quality Assurance Team",
Some("Testing and quality assurance team".to_string()),
"c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2", // admin user
false,
Some(8),
Some(vec!["Testing".to_string(), "Automation".to_string()]),
Some("Remote".to_string()),
),
];
for (id, name, description, leader_id, is_open, max_members, skills_required, location) in teams {
db.query("DELETE type::thing('app_teams', $id)")
.bind(("id", id))
.await?;
let team = TeamsSchema {
id: Thing::from(("app_teams", id)),
name: name.into(),
description,
leader_id: Thing::from(("app_users", leader_id)),
is_open,
max_members,
skills_required,
location,
avatar: None,
website_url: None,
github_url: None,
is_active: true,
is_deleted: false,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
db.create::<Option<TeamsSchema>>(("app_teams", id))
.content(team)
.await?;
println!("✅ Inserted team: {name}");
}
println!("✅ All Teams seeded");
Ok(())
}
+58 -156
View File
@@ -1,174 +1,76 @@
use imphnen_cms::v1::landing::events::events_schema::EventsSchema;
use imphnen_cms::v1::landing::testimonials::testimonials_schema::TestimonialsSchema;
use imphnen_dimentorin::v1::mentors::mentors_schema::MentorSchema;
use imphnen_dimentorin::v1::mentors::mentors_dto::MentoringRate;
use imphnen_hackathon::v1::hackathon::hackathon_schema::{
HackathonSchema, HackathonEventsSchema, HackathonTimelineSchema,
HackathonStatus, HackathonEventType, HackathonPhase
};
use imphnen_utils::get_iso_date;
#![allow(clippy::all)]
use std::error::Error;
use surrealdb::{opt::auth::Root, sql::Thing};
use imphnen_libs::postgres::{PostgresConfig, PostgresConnection};
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 sea_orm::ActiveModelTrait;
use uuid::Uuid;
use serde_json::json;
use chrono::Utc;
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(config).await?;
let db = &pg_conn.conn;
// Seed Events - handle existing data
let event = EventsSchema {
id: Thing::from(("app_events", "1")),
name: "Test Event".to_string(),
description: "Test event description".to_string(),
detail_link: "https://example.com/event".to_string(),
price: 50.0,
is_online: true,
start_date: get_iso_date(),
end_date: get_iso_date(),
location: None,
is_deleted: false,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
match db.create::<Option<EventsSchema>>(("app_events", "1"))
.content(event)
.await {
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, skipping"),
Err(_) => println!("⚠️ Test event already exists or could not be inserted, skipping"),
};
// Seed Testimonials - handle existing data
let testimonial = TestimonialsSchema {
id: Thing::from(("app_testimonials", "1")),
user: Thing::from(("app_users", "c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2")),
role: "Student".to_string(),
content: "This is a great platform!".to_string(),
is_deleted: false,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
match db.create::<Option<TestimonialsSchema>>(("app_testimonials", "1"))
.content(testimonial)
.await {
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, skipping"),
};
// Seed Hackathon - handle existing data
let hackathon = HackathonSchema {
id: Thing::from(("app_hackathons", "1")),
name: "Test Hackathon".to_string(),
description: "Test hackathon description".to_string(),
start_date: Utc::now() + chrono::Duration::days(30),
end_date: Utc::now() + chrono::Duration::days(37),
registration_deadline: Utc::now() + chrono::Duration::days(25),
max_participants: Some(100),
status: HackathonStatus::Draft,
theme: Some("Technology".to_string()),
rules: Some("Follow the rules".to_string()),
prizes: Some(vec![]),
previous_winners: Some(vec![]),
organizers: vec!["c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2".to_string()],
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
match db.create::<Option<HackathonSchema>>(("app_hackathons", "1"))
.content(hackathon)
.await {
Ok(_) => println!("✅ Inserted test hackathon"),
Err(_) => println!("⚠️ Test hackathon already exists, skipping"),
};
// Seed Hackathon Event
let hackathon_event = HackathonEventsSchema {
id: Thing::from(("app_hackathon_events", "test-event-001")),
hackathon_id: Thing::from(("app_hackathons", "1")),
title: "Test Event".to_string(),
description: Some("Test hackathon event description".to_string()),
event_type: HackathonEventType::Workshop,
start_time: Utc::now() + chrono::Duration::days(30),
end_time: Utc::now() + chrono::Duration::days(30) + chrono::Duration::hours(6),
location: Some("Online".to_string()),
virtual_link: None,
max_attendees: Some(50),
is_mandatory: false,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
// Try to create hackathon event, skip if already exists
match db.create::<Option<HackathonEventsSchema>>(("app_hackathon_events", "test-event-001"))
.content(hackathon_event)
.await {
Ok(_) => println!("✅ Inserted test hackathon event"),
Err(_) => println!("⚠️ Test hackathon event already exists, skipping"),
};
// Seed Hackathon Timeline
let hackathon_timeline = HackathonTimelineSchema {
id: Thing::from(("app_hackathon_timeline", "test-timeline-001")),
hackathon_id: Thing::from(("app_hackathons", "1")),
phase: HackathonPhase::Registration,
title: "Test Timeline".to_string(),
description: Some("Test hackathon timeline description".to_string()),
start_date: Utc::now(),
end_date: Utc::now() + chrono::Duration::days(7),
is_active: true,
order: 1,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
// Try to create hackathon timeline, skip if already exists
match db.create::<Option<HackathonTimelineSchema>>(("app_hackathon_timeline", "test-timeline-001"))
.content(hackathon_timeline)
.await {
Ok(_) => println!("✅ Inserted test hackathon timeline"),
Err(_) => println!("⚠️ Test hackathon timeline already exists, skipping"),
Err(_) => println!("⚠️ Test testimonial already exists or could not be inserted, skipping"),
};
// Seed Mentor - handle existing data
let mentor = MentorSchema {
id: Thing::from(("app_mentors", "e6f78d23-83bf-5c2b-bcd4-001345678901")),
user_id: Some(Thing::from(("app_users", "e6f78d23-83bf-5c2b-bcd4-001345678901"))),
industries: vec!["Technology".to_string(), "Education".to_string()],
expertise: vec!["Software Development".to_string()],
languages: vec!["English".to_string(), "Indonesian".to_string()],
current_company: "Tech Corp".to_string(),
current_role: "Senior Engineer".to_string(),
years_of_experience: 5,
topics_of_interest: vec!["Rust".to_string(), "Web Development".to_string()],
preferred_mentee_level: vec!["Beginner".to_string()],
preferred_mentoring_formats: vec!["1:1".to_string(), "Group".to_string()],
availability_commitment: "Weekly".to_string(),
mentoring_rate: MentoringRate {
amount: 100,
currency: "IDR".to_string(),
per_duration: "hour".to_string(),
},
status: "active".to_string(),
is_deleted: false,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
match db.create::<Option<MentorSchema>>(("app_mentors", "e6f78d23-83bf-5c2b-bcd4-001345678901"))
.content(mentor)
.await {
Ok(_) => println!("✅ Inserted test mentor"),
Err(_) => println!("⚠️ Test mentor already exists, skipping"),
};
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");
println!("✅ All test data seeded successfully");
Ok(())
@@ -1,433 +0,0 @@
use chrono::{DateTime, Utc};
use imphnen_hackathon::v1::hackathon::hackathon_schema::{
HackathonSchema, HackathonTimelineSchema, HackathonSubmissionsSchema,
HackathonStatus, HackathonPhase, SubmissionStatus, Prize,
};
use imphnen_iam::{UsersSchema, v1::teams::TeamsSchema};
use imphnen_utils::{get_iso_date, hash_password};
use std::error::Error;
use surrealdb::{opt::auth::Root, sql::Thing};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
// Test users for submission testing
let test_users = vec![
(
"test-user-001",
"testuser1@example.com",
"Test User 1",
"5713cb37-dc02-4e87-8048-d7a41d352059", // User role
),
(
"test-user-002",
"testuser2@example.com",
"Test User 2",
"5713cb37-dc02-4e87-8048-d7a41d352059",
),
(
"test-user-003",
"testuser3@example.com",
"Test User 3",
"5713cb37-dc02-4e87-8048-d7a41d352059",
),
];
// Test teams for submission testing
let test_teams = vec![
(
"test-team-001",
"Test Team Alpha",
Some("Team for testing hackathon submissions".to_string()),
"test-user-001", // team leader
true,
Some(5),
Some(vec!["JavaScript".to_string(), "React".to_string()]),
Some("Remote".to_string()),
),
(
"test-team-002",
"Test Team Beta",
Some("Another team for testing submissions".to_string()),
"test-user-002",
true,
Some(4),
Some(vec!["Python".to_string(), "Django".to_string()]),
Some("Remote".to_string()),
),
];
// Test hackathon for submission testing
let test_hackathons = vec![
(
"test-hackathon-001",
"Test Hackathon 2025",
"Hackathon for testing submission functionality.",
"2025-12-01T09:00:00Z",
"2025-12-03T18:00:00Z",
"2025-11-25T23:59:59Z",
Some(50),
HackathonStatus::RegistrationOpen,
Some("Testing & Development".to_string()),
Some("1. Test all submission features\n2. Teams can have 2-5 members\n3. Submit by deadline".to_string()),
Some(vec![
Prize { position: 1, title: "Test Winner".to_string(), description: Some("Best test submission".to_string()), value: Some("$1,000".to_string()) },
Prize { position: 2, title: "Test Runner-up".to_string(), description: Some("Second best submission".to_string()), value: Some("$500".to_string()) },
]),
vec!["c3b1d6a8-8d4f-4b36-b789-2e532ec7a7b2".to_string()], // admin user
),
];
// Test hackathon timeline
let test_timeline = vec![
(
"test-hackathon-001",
HackathonPhase::Registration,
"Registration Phase",
Some("Register your team for the test hackathon".to_string()),
"2025-11-20T00:00:00Z",
"2025-11-25T23:59:59Z",
true,
1,
),
(
"test-hackathon-001",
HackathonPhase::Development,
"Development Phase",
Some("Build your test project".to_string()),
"2025-12-01T00:00:00Z",
"2025-12-02T23:59:59Z",
false,
2,
),
(
"test-hackathon-001",
HackathonPhase::Submission,
"Submission Phase",
Some("Submit your test project".to_string()),
"2025-12-03T00:00:00Z",
"2025-12-03T12:00:00Z",
false,
3,
),
];
// Test submissions
let test_submissions = vec![
(
"test-hackathon-001",
"test-team-001",
"Test Project Alpha",
"A comprehensive test project demonstrating all features.",
Some("https://github.com/test-team-alpha/test-project".to_string()),
Some("https://demo.test-project-alpha.com".to_string()),
Some("https://slides.test-project-alpha.com".to_string()),
vec!["JavaScript".to_string(), "React".to_string(), "Node.js".to_string()],
SubmissionStatus::Draft,
"2025-12-02T10:00:00Z",
),
(
"test-hackathon-001",
"test-team-002",
"Test Project Beta",
"Another test project with different technologies.",
Some("https://github.com/test-team-beta/test-project".to_string()),
Some("https://demo.test-project-beta.com".to_string()),
None,
vec!["Python".to_string(), "Django".to_string(), "PostgreSQL".to_string()],
SubmissionStatus::Submitted,
"2025-12-03T09:30:00Z",
),
];
// Seed test users
for (id, email, fullname, role_id) in test_users {
db.query("DELETE type::thing('app_users', $id)")
.bind(("id", id))
.await?;
let user = UsersSchema {
id: Thing::from(("app_users", id)),
fullname: fullname.into(),
legal_name: Some(format!("{} Legal Name", fullname)),
email: email.into(),
password: hash_password("password").unwrap(),
avatar: Some("https://example.com/avatar.jpg".into()),
phone_number: "081234567890".into(),
phone_for_verification: Some("081234567890".into()),
is_active: true,
is_deleted: false,
mentor_id: None,
gender: Some("male".into()),
birthdate: Some("1995-01-01".into()),
domicile: Some("Jakarta, Indonesia".into()),
bio: Some(format!("{} is a test user for hackathon submissions.", fullname)),
last_education: Some("S1 Computer Science".into()),
linkedin_url: Some("https://linkedin.com/in/testuser".into()),
github_url: Some("https://github.com/testuser".into()),
cv_url: Some("https://example.com/cv.pdf".into()),
portfolio_url: Some("https://example.com/portfolio".into()),
website_url: Some("https://example.com/website".into()),
twitter_url: Some("https://twitter.com/testuser".into()),
location: Some("Jakarta, Indonesia".into()),
skills: Some(vec!["JavaScript".to_string(), "Python".to_string()]),
experience: None,
education: None,
career_status: Some("Developer".into()),
role: Thing::from(("app_roles", role_id)),
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
db.create::<Option<UsersSchema>>(("app_users", id))
.content(user)
.await?;
println!("✅ Inserted test user: {fullname} ({email})");
}
// Seed test teams
for (id, name, description, leader_id, is_open, max_members, skills_required, location) in test_teams {
db.query("DELETE type::thing('app_teams', $id)")
.bind(("id", id))
.await?;
let team = TeamsSchema {
id: Thing::from(("app_teams", id)),
name: name.into(),
description,
leader_id: Thing::from(("app_users", leader_id)),
is_open,
max_members,
skills_required,
location,
avatar: None,
website_url: None,
github_url: None,
is_active: true,
is_deleted: false,
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
db.create::<Option<TeamsSchema>>(("app_teams", id))
.content(team)
.await?;
println!("✅ Inserted test team: {name}");
}
// Seed test hackathons
for (
id,
name,
description,
start_date,
end_date,
registration_deadline,
max_participants,
status,
theme,
rules,
prizes,
organizers,
) in test_hackathons {
db.query("DELETE type::thing('app_hackathons', $id)")
.bind(("id", id))
.await?;
let hackathon = HackathonSchema {
id: Thing::from(("app_hackathons", id)),
name: name.into(),
description: description.into(),
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
registration_deadline: DateTime::parse_from_rfc3339(registration_deadline)?.with_timezone(&Utc),
max_participants,
status: status.clone(),
theme: theme.clone(),
rules: rules.clone(),
prizes: prizes.clone(),
previous_winners: None,
organizers: organizers.clone(),
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonSchema>>(("app_hackathons", id))
.content(hackathon)
.await?;
println!("✅ Inserted test hackathon: {name}");
// Also create an alias canonical id 'test-hackathon' so tests referencing
// /v1/hackathons/test-hackathon/... can find a hackathon record.
if id != "test-hackathon" && id.starts_with("test-hackathon") {
let alias_id = "test-hackathon";
db.query("DELETE type::thing('app_hackathons', $id)")
.bind(("id", alias_id))
.await?;
let alias_hackathon = HackathonSchema {
id: Thing::from(("app_hackathons", alias_id)),
name: name.into(),
description: description.into(),
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
registration_deadline: DateTime::parse_from_rfc3339(registration_deadline)?.with_timezone(&Utc),
max_participants,
status: status.clone(),
theme: theme.clone(),
rules: rules.clone(),
prizes: prizes.clone(),
previous_winners: None,
organizers: organizers.clone(),
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonSchema>>(("app_hackathons", alias_id))
.content(alias_hackathon)
.await?;
println!("✅ Inserted test hackathon alias: {alias_id}");
}
}
// Seed test hackathon timeline
for (
hackathon_id,
phase,
title,
description,
start_date,
end_date,
is_active,
order,
) in test_timeline {
let timeline_id = format!("test-timeline-{}-{}", hackathon_id, order);
db.query("DELETE type::thing('app_hackathon_timeline', $id)")
.bind(("id", timeline_id.clone()))
.await?;
let timeline = HackathonTimelineSchema {
id: Thing::from(("app_hackathon_timeline", timeline_id.as_str())),
hackathon_id: Thing::from(("app_hackathons", hackathon_id)),
phase: phase.clone(),
title: title.into(),
description: description.clone(),
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
is_active,
order,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonTimelineSchema>>(("app_hackathon_timeline", timeline_id))
.content(timeline)
.await?;
println!("✅ Inserted test hackathon timeline: {title}");
// Also create alias timeline entries for the canonical test id 'test-hackathon'
if hackathon_id != "test-hackathon" && hackathon_id.starts_with("test-hackathon") {
let alias_hackathon_id = "test-hackathon";
let alias_timeline_id = format!("test-timeline-{}-{}", alias_hackathon_id, order);
db.query("DELETE type::thing('app_hackathon_timeline', $id)")
.bind(("id", alias_timeline_id.clone()))
.await?;
let alias_timeline = HackathonTimelineSchema {
id: Thing::from(("app_hackathon_timeline", alias_timeline_id.as_str())),
hackathon_id: Thing::from(("app_hackathons", alias_hackathon_id)),
phase: phase.clone(),
title: title.into(),
description: description.clone(),
start_date: DateTime::parse_from_rfc3339(start_date)?.with_timezone(&Utc),
end_date: DateTime::parse_from_rfc3339(end_date)?.with_timezone(&Utc),
is_active,
order,
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonTimelineSchema>>( ("app_hackathon_timeline", alias_timeline_id.clone()) )
.content(alias_timeline)
.await?;
println!("✅ Inserted test hackathon timeline alias: {alias_timeline_id}");
}
}
// Seed test hackathon submissions
for (
hackathon_id,
team_id,
project_name,
description,
repository_url,
demo_url,
slides_url,
technologies,
submission_status,
submitted_at,
) in test_submissions {
let submission_id = format!("test-submission-{}-{}", hackathon_id, team_id);
db.query("DELETE type::thing('app_hackathon_submissions', $id)")
.bind(("id", submission_id.clone()))
.await?;
let submission = HackathonSubmissionsSchema {
id: Thing::from(("app_hackathon_submissions", submission_id.as_str())),
hackathon_id: Thing::from(("app_hackathons", hackathon_id)),
team_id: Some(Thing::from(("app_teams", team_id))),
project_name: Some(project_name.into()),
description: Some(description.into()),
repository_url,
upload_file_url: None,
demo_url,
slides_url,
technologies: Some(technologies),
contact_instagram: None,
contact_twitter: None,
contact_linkedin: None,
contact_facebook: None,
contact_youtube: None,
contact_tiktok: None,
contact_other: None,
submission_status: Some(submission_status),
judge_feedback: None,
submitted_at: Some(DateTime::parse_from_rfc3339(submitted_at)?.with_timezone(&Utc)),
is_deleted: false,
created_at: Some(get_iso_date()),
updated_at: Some(get_iso_date()),
};
db.create::<Option<HackathonSubmissionsSchema>>(("app_hackathon_submissions", submission_id))
.content(submission)
.await?;
println!("✅ Inserted test hackathon submission: {project_name}");
}
println!("✅ All test submission data seeded");
Ok(())
}
+150 -96
View File
@@ -1,107 +1,161 @@
use imphnen_iam::UsersSchema;
use imphnen_utils::{get_iso_date, hash_password};
#![allow(clippy::all)]
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 std::error::Error;
use chrono::Utc;
use surrealdb::{opt::auth::Root, sql::Thing};
#[tokio::main]
async fn main() -> Result<(), Box<dyn Error>> {
let env = &imphnen_libs::environment::ENV;
use surrealdb::engine::any;
let db = any::connect(&env.surrealdb_url).await?;
db.signin(Root {
username: &env.surrealdb_username,
password: &env.surrealdb_password,
})
.await?;
db.use_ns(env.surrealdb_namespace.clone())
.use_db(env.surrealdb_dbname.clone())
.await?;
let postgres_config = PostgresConfig::from_env()?;
let pg_conn = PostgresConnection::new(postgres_config).await?;
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",
),
(
"testuser1-id",
"testuser1@example.com",
"Test User 1",
"5713cb37-dc02-4e87-8048-d7a41d352059",
),
(
"testuser2-id",
"testuser2@example.com",
"Test User 2",
"5713cb37-dc02-4e87-8048-d7a41d352059",
),
(
"testuser3-id",
"testuser3@example.com",
"Test User 3",
"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", // 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",
),
];
for (id, email, fullname, role_id) in users {
db.query("DELETE type::thing('app_users', $id)")
.bind(("id", id))
.await?;
let user = UsersSchema {
id: Thing::from(("app_users", id)),
fullname: fullname.into(),
legal_name: Some(format!("{} Legal Name", fullname)),
email: email.into(),
password: hash_password("password").unwrap(),
avatar: Some("https://example.com/avatar.jpg".into()),
phone_number: "081234567890".into(),
phone_for_verification: Some("081234567890".into()),
is_active: true,
is_deleted: false,
mentor_id: None,
gender: Some("male".into()),
birthdate: Some("1990-05-15".into()),
domicile: Some("Jakarta, Indonesia".into()),
// identity_document_url: None, // Sudah tidak dipakai, bisa dihapus dari schema jika tidak diperlukan
bio: Some(format!("{} adalah user dengan data pribadi lengkap untuk testing.", fullname)),
last_education: Some("S1 Teknik Informatika".into()),
linkedin_url: Some("https://linkedin.com/in/user".into()),
github_url: Some("https://github.com/user".into()),
cv_url: Some("https://example.com/cv.pdf".into()),
portfolio_url: Some("https://example.com/portfolio".into()),
website_url: Some("https://example.com/website".into()),
twitter_url: Some("https://twitter.com/user".into()),
location: Some("Jakarta, Indonesia".into()),
skills: Some(vec!["JavaScript".into(), "React".into(), "Node.js".into()]),
experience: None,
education: None,
career_status: Some("Senior Developer".into()),
role: Thing::from(("app_roles", role_id)),
created_at: get_iso_date(),
updated_at: get_iso_date(),
};
db.create::<Option<UsersSchema>>(("app_users", id))
.content(user)
.await?;
println!("✅ Inserted user: {fullname} ({email})");
}
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();
// 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?;
}
}
println!("✅ All Users seeded");
Ok(())
}
}
+33 -29
View File
@@ -1,29 +1,33 @@
use std::error::Error;
use std::process::Command;
fn run_seed(bin: &str) -> Result<(), Box<dyn Error>> {
println!("🔧 Seeding: {bin}");
let status = Command::new("cargo").args(["run", "--bin", bin]).status()?;
if !status.success() {
Err(format!("❌ Failed to run seed: {bin}").into())
} else {
Ok(())
}
}
fn main() -> Result<(), Box<dyn Error>> {
println!("🚀 Running all seeders...\n");
run_seed("seed_permissions")?;
run_seed("seed_roles")?;
run_seed("seed_roles_permissions")?;
run_seed("seed_users")?;
run_seed("seed_events")?;
run_seed("seed_hackathons")?;
run_seed("seed_gacha_rolls")?;
run_seed("seed_mentor_user")?;
run_seed("seed_test_data")?;
println!("\n✅ All seeding completed successfully.");
Ok(())
}
#![allow(clippy::all)]
use std::error::Error;
use std::process::Command;
fn run_seed(bin: &str) -> Result<(), Box<dyn Error>> {
println!("🔧 Seeding: {bin}");
#[cfg(target_os = "windows")]
let status = Command::new(format!("./target/release/{}.exe", bin)).status()?;
#[cfg(not(target_os = "windows"))]
let status = Command::new(format!("./target/release/{}", bin)).status()?;
if !status.success() {
Err(format!("❌ Failed to run seed: {bin}").into())
} else {
Ok(())
}
}
fn main() -> Result<(), Box<dyn Error>> {
println!("🚀 Running all seeders...\n");
run_seed("seed_permissions")?;
run_seed("seed_roles")?;
run_seed("seed_roles_permissions")?;
run_seed("seed_users")?;
run_seed("seed_events")?;
run_seed("seed_gacha_rolls")?;
run_seed("seed_mentor_user")?;
run_seed("seed_test_data")?;
println!("\n✅ All seeding completed successfully.");
Ok(())
}
+368
View File
@@ -0,0 +1,368 @@
//! 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);
}
}
+13 -12
View File
@@ -1,12 +1,13 @@
use imphnen_gateway::gateway_service;
use imphnen_libs::axum_init;
#[tokio::main]
async fn main() {
tracing_subscriber::fmt::init();
axum_init(|surrealdb_ws, surrealdb_mem| async {
gateway_service(surrealdb_ws, surrealdb_mem).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 {
// PostgreSQL is now the primary database - SurrealDB has been completely removed
gateway_service(postgres_conn).await
})
.await;
}