diff --git a/Cargo.lock b/Cargo.lock index d33111e..c7748fc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1927,6 +1927,7 @@ dependencies = [ "axum-test", "chrono", "env_logger", + "imphnen-cms", "imphnen-entities", "imphnen-gateway", "imphnen-iam", @@ -2038,6 +2039,7 @@ dependencies = [ "axum", "axum-test", "chrono", + "imphnen-cms", "imphnen-entities", "imphnen-gacha", "imphnen-iam", diff --git a/apply-env.cmd b/apply-env.cmd new file mode 100644 index 0000000..fa35431 --- /dev/null +++ b/apply-env.cmd @@ -0,0 +1,35 @@ +@echo off +setlocal + +:: Cek apakah file .env ada +if not exist ".env" ( + echo File .env tidak ditemukan di direktori saat ini. + exit /b 1 +) + +echo Memuat variabel dari .env... + +:: Baca file .env baris per baris +for /f "tokens=*" %%a in ('type ".env" ^| findstr /v "^$" ^| findstr /v "^#"') do ( + echo.%%a | findstr "=" >nul && ( + for /f "tokens=1,2 delims==" %%b in ("%%a") do ( + set "key=%%b" + set "value=%%c" + :: Trim whitespace + call :trimValue key value + echo Set variabel: %%b=%%c + setx %%b %%c >nul + ) + ) +) + +echo. +echo Semua variabel telah dimuat. +endlocal +goto :eof + +:: Fungsi trim (sederhana) +:trimValue +set "%1=%[%1]%" +set "%2=%[%2]%" +goto :eof \ No newline at end of file diff --git a/imphnen-backend/Cargo.toml b/imphnen-backend/Cargo.toml index 6d4ff86..d9def1f 100644 --- a/imphnen-backend/Cargo.toml +++ b/imphnen-backend/Cargo.toml @@ -9,6 +9,7 @@ imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" } imphnen-gateway = { version = "0.1.0", path = "../imphnen-gateway" } imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" } imphnen-iam = { version = "0.1.0", path = "../imphnen-iam" } +imphnen-cms = { version = "0.1.0", path = "../imphnen-cms" } axum.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/imphnen-backend/src/bin/seed_events.rs b/imphnen-backend/src/bin/seed_events.rs new file mode 100644 index 0000000..c59a160 --- /dev/null +++ b/imphnen-backend/src/bin/seed_events.rs @@ -0,0 +1,91 @@ +use imphnen_cms::v1::landing::events::events_schema::EventsSchema; +use imphnen_utils::{get_iso_date, Env}; +use std::error::Error; +use surrealdb::{engine::remote::ws::Ws, opt::auth::Root, sql::Thing, Surreal}; + +#[tokio::main] +async fn main() -> Result<(), Box> { + let env = Env::new(); + let db = Surreal::new::(env.surrealdb_url).await?; + db.signin(Root { + username: &env.surrealdb_username, + password: &env.surrealdb_password, + }) + .await?; + db.use_ns(env.surrealdb_namespace) + .use_db(env.surrealdb_dbname) + .await?; + + let events = vec![ + ( + "e1a2b3c4-5d6e-7f8g-9h0i-1j2k3l4m5n6o", + "Tech Conference 2025", + "Annual technology conference featuring the latest innovations in software development, AI, and cloud computing.", + "https://techconf2025.example.com", + 150.0, + Some("Jakarta Convention Center".to_string()), + false, + "2025-06-15T09:00:00Z", + "2025-06-17T18:00:00Z", + ), + ( + "f2b3c4d5-6e7f-8g9h-0i1j-2k3l4m5n6o7p", + "Online Web Development Workshop", + "Comprehensive workshop covering modern web development frameworks including React, Vue, and Angular.", + "https://webdev-workshop.example.com", + 75.0, + None, + true, + "2025-07-10T14:00:00Z", + "2025-07-10T17:00:00Z", + ), + ( + "g3c4d5e6-7f8g-9h0i-1j2k-3l4m5n6o7p8q", + "Startup Pitch Competition", + "Exciting competition where emerging startups present their innovative ideas to a panel of expert judges and investors.", + "https://startup-pitch.example.com", + 25.0, + Some("Innovation Hub Surabaya".to_string()), + false, + "2025-08-05T10:00:00Z", + "2025-08-05T16:00:00Z", + ), + ( + "h4d5e6f7-8g9h-0i1j-2k3l-4m5n6o7p8q9r", + "Digital Marketing Masterclass", + "Learn advanced digital marketing strategies, social media optimization, and data-driven marketing techniques.", + "https://digital-marketing.example.com", + 100.0, + None, + true, + "2025-09-20T13:00:00Z", + "2025-09-22T15:00:00Z", + ), + ]; + + for (id, name, description, detail_link, price, location, is_online, start_date, end_date) in events { + let event = EventsSchema { + id: Thing::from(("app_events", id)), + 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(), + }; + + db.create::>(("app_events", id)) + .content(event) + .await?; + + println!("āœ… Inserted event: {} ({})", name, if is_online { "Online" } else { "In-person" }); + } + + println!("āœ… All Events seeded"); + Ok(()) +} diff --git a/imphnen-backend/src/bin/seeder.rs b/imphnen-backend/src/bin/seeder.rs index f82af9c..116db22 100644 --- a/imphnen-backend/src/bin/seeder.rs +++ b/imphnen-backend/src/bin/seeder.rs @@ -19,6 +19,7 @@ fn main() -> Result<(), Box> { run_seed("seed_roles")?; run_seed("seed_roles_permissions")?; run_seed("seed_users")?; + run_seed("seed_events")?; println!("\nāœ… All seeding completed successfully."); Ok(()) diff --git a/imphnen-cms/src/lib.rs b/imphnen-cms/src/lib.rs index 1303f3d..590a6fc 100644 --- a/imphnen-cms/src/lib.rs +++ b/imphnen-cms/src/lib.rs @@ -1,3 +1,5 @@ +pub mod v1; + pub fn add(left: u64, right: u64) -> u64 { left + right } diff --git a/imphnen-cms/src/v1/landing/events/events_controller.rs b/imphnen-cms/src/v1/landing/events/events_controller.rs new file mode 100644 index 0000000..0e5929c --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/events_controller.rs @@ -0,0 +1,119 @@ +use super::{ + events_dto::{EventsCreateRequestDto, EventsDetailItemDto, EventsListItemDto, EventsUpdateRequestDto}, + events_service::EventsService, +}; +use imphnen_libs::{AppState, MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto, MessageResponseDto}; +use axum::extract::{Path, Query}; +use axum::response::IntoResponse; +use axum::{Extension, Json}; + +#[utoipa::path( + get, + security( + ("Bearer" = []) + ), + path = "/v1/events", + params( + ("page" = Option, Query, description = "Page number"), + ("per_page" = Option, Query, description = "Items per page"), + ("search" = Option, Query, description = "Search keyword"), + ("sort_by" = Option, Query, description = "Sort by field"), + ("order" = Option, Query, description = "Order ASC or DESC"), + ("filter" = Option, Query, description = "Filter value"), + ("filter_by" = Option, Query, description = "Field to filter by"), + ), + responses( + (status = 200, description = "Get event list", body = ResponseListSuccessDto>) + ), + tag = "Events" +)] +pub async fn get_event_list( + Extension(state): Extension, + Query(meta): Query, +) -> impl IntoResponse { + EventsService::get_event_list(&state, meta).await +} + +#[utoipa::path( + get, + security( + ("Bearer" = []) + ), + path = "/v1/events/detail/{id}", + params( + ("id" = String, Path, description = "Event ID") + ), + responses( + (status = 200, description = "Get event by ID", body = ResponseSuccessDto) + ), + tag = "Events" +)] +pub async fn get_event_by_id( + Extension(state): Extension, + Path(id): Path, +) -> impl IntoResponse { + EventsService::get_event_by_id(&state, id).await +} + +#[utoipa::path( + post, + security( + ("Bearer" = []) + ), + path = "/v1/events/create", + request_body = EventsCreateRequestDto, + responses( + (status = 201, description = "Create new event", body = MessageResponseDto) + ), + tag = "Events" +)] +pub async fn post_create_event( + Extension(state): Extension, + Json(payload): Json, +) -> impl IntoResponse { + EventsService::create_event(&state, payload).await +} + +#[utoipa::path( + patch, + security( + ("Bearer" = []) + ), + path = "/v1/events/update/{id}", + params( + ("id" = String, Path, description = "Event ID") + ), + request_body = EventsUpdateRequestDto, + responses( + (status = 200, description = "Update event", body = MessageResponseDto) + ), + tag = "Events" +)] +pub async fn patch_update_event( + Extension(state): Extension, + Path(id): Path, + Json(payload): Json, +) -> impl IntoResponse { + EventsService::update_event(&state, id, payload).await +} + +#[utoipa::path( + delete, + security( + ("Bearer" = []) + ), + path = "/v1/events/delete/{id}", + params( + ("id" = String, Path, description = "Event ID") + ), + responses( + (status = 200, description = "Soft delete event", body = MessageResponseDto) + ), + tag = "Events" +)] +pub async fn delete_event( + Extension(state): Extension, + Path(id): Path, +) -> impl IntoResponse { + EventsService::delete_event(&state, id).await +} diff --git a/imphnen-cms/src/v1/landing/events/events_dto.rs b/imphnen-cms/src/v1/landing/events/events_dto.rs new file mode 100644 index 0000000..d39e88e --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/events_dto.rs @@ -0,0 +1,121 @@ +use chrono::{DateTime, Utc}; +use lazy_static::lazy_static; +use regex::Regex; +use serde::{Deserialize, Serialize}; +use surrealdb::sql::Thing; +use utoipa::ToSchema; +use validator::Validate; + +// Lazy static regex for URL validation +lazy_static! { + static ref VALID_URL_REGEX: Regex = Regex::new(r"^https?://").unwrap(); +} + +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)] +pub struct EventsCreateRequestDto { + #[validate(length(min = 1, message = "Name is required"))] + pub name: String, + + #[validate(length(min = 1, message = "Description is required"))] + pub description: String, + + #[validate(regex( + path = "VALID_URL_REGEX", + message = "Detail link must be a valid URL" + ))] + pub detail_link: String, + + #[validate(range(min = 0, message = "Price cannot be negative"))] + pub price: f64, + + #[schema(example = "2025-09-20T13:00:00Z", value_type = String)] + pub end_date: DateTime, + + #[schema(example = "2025-09-20T13:00:00Z", value_type = String)] + pub start_date: DateTime, + + pub location: Option, + pub is_online: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)] +pub struct EventsUpdateRequestDto { + #[validate(length(min = 1, message = "Name is required"))] + pub name: String, + + #[schema(example = "2025-09-20T13:00:00Z", value_type = String)] + pub end_date: DateTime, + + #[schema(example = "2025-09-20T13:00:00Z", value_type = String)] + pub start_date: DateTime, + + pub price: f64, + pub is_online: bool, + pub description: String, + pub detail_link: String, + pub location: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] +pub struct EventsListItemDto { + pub id: String, + pub name: String, + pub description: String, + pub detail_link: String, + pub price: f64, + pub is_online: bool, + pub start_date: String, + pub end_date: String, + pub created_at: String, + pub location: Option, + pub is_deleted: bool, +} + +#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)] +pub struct EventsDetailItemDto { + pub id: String, + pub name: String, + pub description: String, + pub detail_link: String, + pub price: f64, + pub is_online: bool, + pub start_date: String, + pub end_date: String, + pub created_at: String, + pub updated_at: String, + pub location: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EventsQueryDto { + pub id: Thing, + pub name: String, + pub description: String, + pub detail_link: String, + pub price: f64, + pub is_online: bool, + pub is_deleted: bool, + pub start_date: String, + pub end_date: String, + pub created_at: String, + pub updated_at: String, + pub location: Option, +} + +impl EventsQueryDto { + pub fn from(self) -> EventsListItemDto { + EventsListItemDto { + id: self.id.id.to_raw(), + name: self.name, + description: self.description, + detail_link: self.detail_link, + price: self.price, + location: self.location, + is_online: self.is_online, + start_date: self.start_date, + end_date: self.end_date, + created_at: self.created_at, + is_deleted: self.is_deleted, + } + } +} diff --git a/imphnen-cms/src/v1/landing/events/events_repository.rs b/imphnen-cms/src/v1/landing/events/events_repository.rs new file mode 100644 index 0000000..ae59596 --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/events_repository.rs @@ -0,0 +1,115 @@ +use anyhow::{Result, bail}; +use imphnen_libs::{AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto}; +use imphnen_utils::{DetailQueryBuilder, ListQueryBuilder, get_id, get_iso_date}; + +use super::{events_dto::EventsQueryDto, events_schema::EventsSchema}; + +pub struct EventsRepository<'a> { + state: &'a AppState, +} + +impl<'a> EventsRepository<'a> { + pub fn new(state: &'a AppState) -> Self { + Self { state } + } + + // Get list of events with pagination and sorting by newest + pub async fn query_event_list( + &self, + meta: MetaRequestDto, + ) -> Result>> { + let query = ListQueryBuilder::new(&ResourceEnum::Events.to_string()) + .with_select_fields(vec!["*"]) + .with_pagination(meta.page, Some(10)) + .with_sorting(meta.sort_by.as_deref(), meta.order.as_deref()) + .build(); + let res: Vec = + self.state.surrealdb_ws.query(query).await?.take(0)?; + let data = ResponseListSuccessDto { + data: res, + meta: None, + }; + Ok(data) + } + + // Get event by ID + pub async fn query_event_by_id(&self, id: String) -> Result { + let db = &self.state.surrealdb_ws; + let builder = DetailQueryBuilder::new(ResourceEnum::Events.to_string()) + .with_id(&id) + .with_select_fields(vec!["*"]); + let sql = builder.build(); + let result: Option = + builder.apply_bindings(db.query(sql)).await?.take(0)?; + + match result { + Some(event) => { + if event.is_deleted { + bail!("Event not found"); + } + Ok(event) + } + None => bail!("Event not found"), + } + } + + // Create new event + pub async fn query_create_event(&self, data: EventsSchema) -> Result { + let db = &self.state.surrealdb_ws; + let record: Option = db + .create(ResourceEnum::Events.to_string()) + .content(data) + .await?; + + match record { + Some(_) => Ok("Success create event".into()), + None => bail!("Failed to create event"), + } + } + + // Update existing event + pub async fn query_update_event(&self, data: EventsSchema) -> Result { + let db = &self.state.surrealdb_ws; + + // Cek apakah event ada + let existing = self.query_event_by_id(data.id.id.to_raw()).await?; + if existing.is_deleted { + bail!("Event already deleted"); + } + + // Merge field tertentu jika diperlukan + let merged = EventsSchema { + created_at: existing.created_at, + updated_at: get_iso_date(), + ..data + }; + + let record_key = get_id(&merged.id)?; + let record: Option = db.update(record_key).merge(merged).await?; + + match record { + Some(_) => Ok("Success update event".into()), + None => bail!("Failed to update event"), + } + } + + // Soft delete event (mark is_deleted = true) + pub async fn query_delete_event(&self, id: String) -> Result { + let db = &self.state.surrealdb_ws; + let event = self.query_event_by_id(id).await?; + if event.is_deleted { + bail!("Event not found"); + } + + let record_key = get_id(&event.id)?; + let record: Option = db + .update(record_key) + .merge(serde_json::json!({ "is_deleted": true })) + .await?; + + match record { + Some(_) => Ok("Success delete event".into()), + None => bail!("Failed to delete event"), + } + } +} diff --git a/imphnen-cms/src/v1/landing/events/events_schema.rs b/imphnen-cms/src/v1/landing/events/events_schema.rs new file mode 100644 index 0000000..3f69b9d --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/events_schema.rs @@ -0,0 +1,100 @@ +use imphnen_libs::ResourceEnum; +use surrealdb::Uuid; +use surrealdb::sql::Thing; +use serde::{Deserialize, Serialize}; +use imphnen_utils::{get_iso_date, make_thing}; + +use super::events_dto::{EventsCreateRequestDto, EventsQueryDto, EventsUpdateRequestDto}; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct EventsSchema { + pub id: Thing, + pub price: f64, + pub is_online: bool, + pub is_deleted: bool, + pub name: String, + pub end_date: String, + pub start_date: String, + pub created_at: String, + pub updated_at: String, + pub description: String, + pub detail_link: String, + pub location: Option, +} + +impl Default for EventsSchema { + fn default() -> Self { + Self { + id: make_thing( + &ResourceEnum::Events.to_string(), + &Uuid::new_v4().to_string(), + ), + name: String::new(), + description: String::new(), + detail_link: String::new(), + price: 0.0, + location: None, + is_online: false, + is_deleted: false, + start_date: String::new(), + end_date: String::new(), + created_at: get_iso_date(), + updated_at: get_iso_date(), + } + } +} + +impl EventsSchema { + pub fn from(dto: EventsQueryDto) -> Self { + Self { + id: dto.id, + name: dto.name, + description: dto.description, + detail_link: dto.detail_link, + price: dto.price, + location: dto.location, + is_online: dto.is_online, + is_deleted: false, + start_date: dto.start_date, + end_date: dto.end_date, + created_at: dto.created_at, + updated_at: dto.updated_at, + } + } + + pub fn create(payload: EventsCreateRequestDto) -> Self { + Self { + id: make_thing( + &ResourceEnum::Events.to_string(), + &Uuid::new_v4().to_string(), + ), + name: payload.name, + description: payload.description, + detail_link: payload.detail_link, + price: payload.price, + location: payload.location, + is_online: payload.is_online, + is_deleted: false, + end_date: payload.end_date.to_string(), + start_date: payload.start_date.to_string(), + created_at: get_iso_date(), + updated_at: get_iso_date(), + } + } + + pub fn update(payload: EventsUpdateRequestDto, id: String) -> Self { + Self { + id: make_thing(&ResourceEnum::Events.to_string(), &id), + name: payload.name, + price: payload.price, + location: payload.location, + is_online: payload.is_online, + description: payload.description, + detail_link: payload.detail_link, + end_date: payload.end_date.to_string(), + start_date: payload.start_date.to_string(), + updated_at: get_iso_date(), + ..Default::default() + } + } +} diff --git a/imphnen-cms/src/v1/landing/events/events_service.rs b/imphnen-cms/src/v1/landing/events/events_service.rs new file mode 100644 index 0000000..71af4e8 --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/events_service.rs @@ -0,0 +1,86 @@ +use super::{ + events_dto::{EventsCreateRequestDto, EventsDetailItemDto, EventsListItemDto, EventsQueryDto, EventsUpdateRequestDto}, + events_repository::EventsRepository, + events_schema::EventsSchema, +}; +use imphnen_libs::{AppState, MetaRequestDto, ResponseListSuccessDto, ResponseSuccessDto}; +use imphnen_utils::{common_response, success_list_response, success_response, validate_request}; +use axum::{http::StatusCode, response::Response}; + +pub struct EventsService; + +impl EventsService { + pub async fn get_event_list(state: &AppState, meta: MetaRequestDto) -> Response { + let repo = EventsRepository::new(state); + match repo.query_event_list(meta).await { + Ok(data) => { + let items: Vec = data.data + .into_iter() + .filter(|e| !e.is_deleted) + .map(EventsQueryDto::from) + .collect(); + let response = ResponseListSuccessDto { + data: items, + meta: data.meta, + }; + success_list_response(response) + } + Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()), + } + } + + pub async fn get_event_by_id(state: &AppState, id: String) -> Response { + let repo = EventsRepository::new(state); + match repo.query_event_by_id(id).await { + Ok(event) if !event.is_deleted => success_response(ResponseSuccessDto { + data: EventsDetailItemDto { + id: event.id.id.to_raw(), + name: event.name, + description: event.description, + detail_link: event.detail_link, + price: event.price, + is_online: event.is_online, + start_date: event.start_date, + end_date: event.end_date, + created_at: event.created_at, + updated_at: event.updated_at, + location: event.location, + }, + }), + Ok(_) => common_response(StatusCode::NOT_FOUND, "Event not found"), + Err(e) => common_response(StatusCode::NOT_FOUND, &e.to_string()), + } + } + + pub async fn create_event(state: &AppState, payload: EventsCreateRequestDto) -> Response { + if let Err((status, message)) = validate_request(&payload) { + return common_response(status, &message); + } + let repo = EventsRepository::new(state); + let schema = EventsSchema::create(payload); + match repo.query_create_event(schema).await { + Ok(msg) => common_response(StatusCode::CREATED, &msg), + Err(e) => common_response(StatusCode::INTERNAL_SERVER_ERROR, &e.to_string()), + } + } + + pub async fn update_event(state: &AppState, id: String, payload: EventsUpdateRequestDto) -> Response { + if let Err((status, message)) = validate_request(&payload) { + return common_response(status, &message); + } + let repo = EventsRepository::new(state); + let schema = EventsSchema::update(payload, id); + match repo.query_update_event(schema).await { + Ok(msg) => common_response(StatusCode::OK, &msg), + Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()), + } + } + + pub async fn delete_event(state: &AppState, id: String) -> Response { + let repo = EventsRepository::new(state); + match repo.query_delete_event(id).await { + Ok(msg) => common_response(StatusCode::OK, &msg), + Err(e) => common_response(StatusCode::BAD_REQUEST, &e.to_string()), + } + } +} \ No newline at end of file diff --git a/imphnen-cms/src/v1/landing/events/mod.rs b/imphnen-cms/src/v1/landing/events/mod.rs new file mode 100644 index 0000000..9247bc4 --- /dev/null +++ b/imphnen-cms/src/v1/landing/events/mod.rs @@ -0,0 +1,17 @@ +pub mod events_dto; +pub mod events_schema; +pub mod events_repository; +pub mod events_service; +pub mod events_controller; + +use axum::{routing::{delete, get, patch, post}, Router}; +pub use events_controller::*; + +pub fn events_public_routes() -> Router { + Router::new() + .route("/events", get(events_controller::get_event_list)) + .route("/events/detail/{id}", get(events_controller::get_event_by_id)) + .route("/events/create", post(events_controller::post_create_event)) + .route("/events/update/{id}", patch(events_controller::patch_update_event)) + .route("/events/delete/{id}", delete(events_controller::delete_event)) +} diff --git a/imphnen-cms/src/v1/landing/mod.rs b/imphnen-cms/src/v1/landing/mod.rs new file mode 100644 index 0000000..6226263 --- /dev/null +++ b/imphnen-cms/src/v1/landing/mod.rs @@ -0,0 +1 @@ +pub mod events; \ No newline at end of file diff --git a/imphnen-cms/src/v1/mod.rs b/imphnen-cms/src/v1/mod.rs new file mode 100644 index 0000000..5947388 --- /dev/null +++ b/imphnen-cms/src/v1/mod.rs @@ -0,0 +1 @@ +pub mod landing; \ No newline at end of file diff --git a/imphnen-gateway/Cargo.toml b/imphnen-gateway/Cargo.toml index 9701f9a..c907ac5 100644 --- a/imphnen-gateway/Cargo.toml +++ b/imphnen-gateway/Cargo.toml @@ -10,6 +10,7 @@ imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" } imphnen-gacha = { version = "0.1.0", path = "../imphnen-gacha" } imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" } imphnen-middleware = { version = "0.1.0", path = "../imphnen-middleware" } +imphnen-cms = { version = "0.1.0", path = "../imphnen-cms" } axum.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/imphnen-gateway/src/docs.rs b/imphnen-gateway/src/docs.rs index 5f22ff3..bd0a0f0 100644 --- a/imphnen-gateway/src/docs.rs +++ b/imphnen-gateway/src/docs.rs @@ -6,6 +6,8 @@ use utoipa::{ Modify, OpenApi, }; use imphnen_gacha::{gacha_claims, gacha_items, gacha_rolls, GachaClaimItemDto, GachaClaimRequestDto, GachaItemDto, GachaItemRequestDto, GachaRollItemDto, GachaRollRequestDto}; +use imphnen_cms::v1::landing::events::{events_controller, events_dto::{EventsDetailItemDto, EventsListItemDto}}; + #[derive(OpenApi)] #[openapi( @@ -45,6 +47,11 @@ use imphnen_gacha::{gacha_claims, gacha_items, gacha_rolls, GachaClaimItemDto, G gacha_rolls::get_detail_gacha_roll, gacha_rolls::post_create_gacha_roll, gacha_rolls::post_execute_gacha_roll, + events_controller::get_event_list, + events_controller::get_event_by_id, + events_controller::post_create_event, + events_controller::patch_update_event, + events_controller::delete_event, ), components( schemas( @@ -83,7 +90,12 @@ use imphnen_gacha::{gacha_claims, gacha_items, gacha_rolls, GachaClaimItemDto, G ResponseListSuccessDto>, ResponseSuccessDto, ResponseListSuccessDto>, - ResponseSuccessDto + ResponseSuccessDto, + ResponseListSuccessDto>, + ResponseSuccessDto, + MessageResponseDto, + MessageResponseDto, + MessageResponseDto, ) ), info( diff --git a/imphnen-gateway/src/lib.rs b/imphnen-gateway/src/lib.rs index 095d782..9978de0 100644 --- a/imphnen-gateway/src/lib.rs +++ b/imphnen-gateway/src/lib.rs @@ -1,6 +1,7 @@ use axum::{ Extension, Router, middleware::from_fn, response::Redirect, routing::get, }; +use imphnen_cms::v1::landing::events::events_public_routes; use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient}; use imphnen_gacha::gacha_router; use imphnen_iam::{iam_protected_routes, iam_public_routes}; @@ -20,6 +21,7 @@ pub async fn gateway_service( }; let public_routes = iam_public_routes(); + let events_public_routes = events_public_routes(); let protected_routes = Router::new() .merge(iam_protected_routes()) @@ -28,7 +30,7 @@ pub async fn gateway_service( Router::new() .route("/", get(Redirect::to("/docs"))) - .nest("/v1", public_routes.merge(protected_routes)) + .nest("/v1", public_routes.merge(protected_routes).merge(events_public_routes)) .merge(SwaggerUi::new("/docs").url("/openapi.json", docs_router())) .layer(cors_middleware()) .layer(Extension(state)) diff --git a/imphnen-libs/src/surrealdb/resource.rs b/imphnen-libs/src/surrealdb/resource.rs index 567feaa..870840f 100644 --- a/imphnen-libs/src/surrealdb/resource.rs +++ b/imphnen-libs/src/surrealdb/resource.rs @@ -12,6 +12,7 @@ pub enum ResourceEnum { Roles, Permissions, RolesPermissions, + Events, } impl fmt::Display for ResourceEnum { @@ -27,6 +28,7 @@ impl fmt::Display for ResourceEnum { ResourceEnum::GachaClaims => "app_gacha_claims", ResourceEnum::GachaRolls => "app_gacha_rolls", ResourceEnum::GachaCredits => "app_gacha_credits", + ResourceEnum::Events => "app_events", }; write!(f, "{}", str) }