Compare commits
3
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d2c333dbd6 | ||
|
|
054f0f63ff | ||
|
|
eb05ea5994 |
Generated
+52
@@ -1871,6 +1871,30 @@ dependencies = [
|
|||||||
"validator",
|
"validator",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-cms-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-dimentorin-service"
|
name = "imphnen-dimentorin-service"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -1941,6 +1965,7 @@ dependencies = [
|
|||||||
"imphnen-entities",
|
"imphnen-entities",
|
||||||
"imphnen-iam-service",
|
"imphnen-iam-service",
|
||||||
"imphnen-libs",
|
"imphnen-libs",
|
||||||
|
"imphnen-middleware-service",
|
||||||
"imphnen-utils",
|
"imphnen-utils",
|
||||||
"lazy_static",
|
"lazy_static",
|
||||||
"rand 0.9.0",
|
"rand 0.9.0",
|
||||||
@@ -1994,6 +2019,33 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "imphnen-middleware-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"anyhow",
|
||||||
|
"axum",
|
||||||
|
"axum-test",
|
||||||
|
"chrono",
|
||||||
|
"futures",
|
||||||
|
"imphnen-entities",
|
||||||
|
"imphnen-iam-service",
|
||||||
|
"imphnen-libs",
|
||||||
|
"imphnen-utils",
|
||||||
|
"lazy_static",
|
||||||
|
"rand 0.9.0",
|
||||||
|
"regex",
|
||||||
|
"serde",
|
||||||
|
"serde_json",
|
||||||
|
"surrealdb",
|
||||||
|
"tokio",
|
||||||
|
"tower",
|
||||||
|
"tower-http",
|
||||||
|
"utoipa",
|
||||||
|
"utoipa-swagger-ui",
|
||||||
|
"validator",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "imphnen-utils"
|
name = "imphnen-utils"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
|
|||||||
@@ -1,78 +1,107 @@
|
|||||||
# Axum SurrealDB Boilerplate
|
# IMPHNEN Backend Service
|
||||||
|
|
||||||
## Features
|
<p align="center">
|
||||||
|
<img src="docs/logo.svg" alt="IMPHNEN">
|
||||||
|
</p>
|
||||||
|
|
||||||
- **Authentication Ready**: Preconfigured authentication and middleware for secure API access.
|
This repository serves as the **monorepo** for all backend services of IMPHNEN. It encompasses several main services:
|
||||||
- **Database Integration**: SurrealDB seamlessly integrated as an Axum Extension.
|
|
||||||
- **CORS Handling**: Fine-tuned CORS management with Tower HTTP `CorsLayer`.
|
|
||||||
- **API Documentation**: Fully documented with OpenAPI and Swagger UI.
|
|
||||||
- **Optimized for Performance**: Asynchronous, lightweight, and scalable architecture.
|
|
||||||
|
|
||||||
## Prerequisites
|
1. **Core Service** - Provides fundamental functionalities and shared resources for other services.
|
||||||
|
2. **IAM Service** - Handles identity and access management across IMPHNEN applications.
|
||||||
|
3. **CMS Service** - Supports the cms services by IMPHNEN [Landing Page website](https://imphnen.dev/).
|
||||||
|
4. **Gacha Service** - Supports the gacha services by IMPHNEN [Gacha website](https://gacha.imphnen.dev/).
|
||||||
|
5. **Dimentorin Service** - Supports the mentoring services by IMPHNEN [Dimentorin website](https://dimentorin.imphnen.dev/).
|
||||||
|
6. **Gateway Service** - Acts as the API gateway, routing requests to appropriate services.
|
||||||
|
|
||||||
- **Rust**: Install Rust from [rust-lang.org](https://www.rust-lang.org/).
|
## How to Install
|
||||||
- **Database**: Set up a SurrealDB instance and configure connection details.
|
|
||||||
- **Docker**: Required for containerized deployment, install from [docker.com](https://www.docker.com/).
|
|
||||||
- **Nix (Optional)**: For reproducible builds, install from [nixos.org](https://nixos.org/).
|
|
||||||
|
|
||||||
## Getting Started
|
1. **Clone the repository**:
|
||||||
|
|
||||||
1. **Clone the Repository**:
|
```sh
|
||||||
|
git clone https://github.com/IMPHNEN/imphnen-backend-service.git
|
||||||
|
cd imphnen-backend-service
|
||||||
|
```
|
||||||
|
|
||||||
- `git clone https://github.com/maulanasdqn/axum-surrealdb-boilerplate`
|
2. **Set up the environment**:
|
||||||
|
|
||||||
2. **Set Up Environment Variables**:
|
- Copy the example environment files:
|
||||||
|
|
||||||
- Copy `.env.example` and rename it to `.env`
|
```sh
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
- **Windows**: Run the script: `./apply-env.ps1`
|
if you use windows based system
|
||||||
- **Unix-based systems (Linux, macOS, BSD)**: Run the script: `./apply-env.sh`
|
|
||||||
|
|
||||||
3. **Install Dependencies**:
|
```sh
|
||||||
|
./apply-env.ps1
|
||||||
|
```
|
||||||
|
|
||||||
- `cargo install .`
|
if you use unix based system
|
||||||
|
|
||||||
4. **Setup Database**:
|
```sh
|
||||||
|
sh apply-env.sh
|
||||||
|
```
|
||||||
|
|
||||||
- Install the surrealDB
|
- Modify the `.env` files with your specific configuration settings.
|
||||||
- **Windows**: `iwr https://windows.surrealdb.com -useb | iex`
|
|
||||||
- **Unix-based systems (Linux, macOS, BSD)**: `curl -sSf https://install.surrealdb.com | sh`
|
|
||||||
- Start the database `surreal start --user root --pass root`
|
|
||||||
|
|
||||||
5. **Start the Server**:
|
3. **Install dependencies**:
|
||||||
|
|
||||||
- Install Cargo Watch `cargo install cargo-watch`
|
Ensure you have [Rust](https://www.rust-lang.org/) installed. Then, run:
|
||||||
- Run it with cargo watch `cargo watch -x run`
|
|
||||||
|
|
||||||
The API will be available at `http://localhost:3000/docs`.
|
```sh
|
||||||
|
cargo build
|
||||||
|
```
|
||||||
|
|
||||||
## Docker
|
## How to Run
|
||||||
|
|
||||||
1. **Build the Docker Image**:
|
### Development
|
||||||
|
|
||||||
2. **Run the Docker Container**:
|
To run the services in development mode:
|
||||||
|
|
||||||
The API will be accessible at `http://localhost:3000/docs`.
|
1. **Start the database and other dependencies** using Docker Compose:
|
||||||
|
|
||||||
## Using Nix as Builder (Optional)
|
```sh
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
1. **Install Nix**:
|
2. **Run the desired service**. For example, to run the Core Service:
|
||||||
|
|
||||||
2. **Enter Nix Shell or Use Nix Flakes**:
|
```sh
|
||||||
|
cargo run -p imphnen-core-service --bin api
|
||||||
|
```
|
||||||
|
|
||||||
3. **Build the Project**:
|
### Production
|
||||||
|
|
||||||
4. **Run the Server**:
|
For production deployment:
|
||||||
|
|
||||||
## Contributing
|
1. **Build the Docker image**:
|
||||||
|
|
||||||
Contributions are welcome! Fork the repository and create a pull request with your improvements.
|
```sh
|
||||||
|
docker build -t imphnen-backend-service .
|
||||||
|
```
|
||||||
|
|
||||||
## License
|
2. **Run the Docker container**:
|
||||||
|
|
||||||
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
|
```sh
|
||||||
|
docker run -d --env-file .env -p 8080:8080 imphnen-backend-service
|
||||||
|
```
|
||||||
|
|
||||||
## Acknowledgements
|
Adjust the port and environment variables as needed.
|
||||||
|
|
||||||
- [Axum](https://github.com/tokio-rs/axum)
|
## How to Contribute
|
||||||
- [SurrealDB](https://github.com/surrealdb/surrealdb)
|
|
||||||
|
1. **Fork the repository** and clone it locally.
|
||||||
|
2. **Create a new branch** for your feature or fix:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
git checkout -b feat/your-feature-name
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **Make your changes**, commit them, and push to your forked repository.
|
||||||
|
4. **Create a pull request** to the `develop` branch of this repository.
|
||||||
|
|
||||||
|
If you encounter any issues or have questions, feel free to create a new issue in the repository.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
_Note: For detailed API documentation, please refer to our [API Docs](https://api.imphnen.dev/docs)._
|
||||||
|
|||||||
@@ -6,3 +6,11 @@ services:
|
|||||||
ports:
|
ports:
|
||||||
- "${PORT}:${PORT}"
|
- "${PORT}:${PORT}"
|
||||||
env_file: ".env"
|
env_file: ".env"
|
||||||
|
depends_on:
|
||||||
|
- surrealdb
|
||||||
|
|
||||||
|
surrealdb:
|
||||||
|
image: surrealdb/surrealdb:latest
|
||||||
|
command: start --log trace --user root --pass root
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 351 KiB |
@@ -0,0 +1,24 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-cms-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
axum.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
utoipa.workspace = true
|
||||||
|
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
|
||||||
|
anyhow.workspace = true
|
||||||
|
tower-http.workspace = true
|
||||||
|
utoipa-swagger-ui.workspace = true
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
pub fn add(left: u64, right: u64) -> u64 {
|
||||||
|
left + right
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn it_works() {
|
||||||
|
let result = add(2, 2);
|
||||||
|
assert_eq!(result, 4);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,9 +4,10 @@ version = "0.1.0"
|
|||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
|
||||||
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
|
imphnen-gateway-service = { version = "0.1.0", path = "../imphnen-gateway-service" }
|
||||||
axum.workspace = true
|
axum.workspace = true
|
||||||
serde.workspace = true
|
serde.workspace = true
|
||||||
serde_json.workspace = true
|
serde_json.workspace = true
|
||||||
@@ -22,4 +23,3 @@ chrono.workspace = true
|
|||||||
anyhow.workspace = true
|
anyhow.workspace = true
|
||||||
tower-http.workspace = true
|
tower-http.workspace = true
|
||||||
utoipa-swagger-ui.workspace = true
|
utoipa-swagger-ui.workspace = true
|
||||||
imphnen-gateway-service = { version = "0.1.0", path = "../imphnen-gateway-service" }
|
|
||||||
|
|||||||
@@ -1,93 +0,0 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use surrealdb::sql::Thing;
|
|
||||||
use utoipa::ToSchema;
|
|
||||||
use validator::Validate;
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
||||||
pub struct GachaItemRequestDto {
|
|
||||||
#[validate(length(min = 1, message = "Item name must not be empty"))]
|
|
||||||
pub name: String,
|
|
||||||
#[validate(length(min = 1, message = "Image URL must not be empty"))]
|
|
||||||
pub image_url: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
||||||
pub struct GachaClaimRequestDto {
|
|
||||||
#[validate(length(min = 1, message = "User ID must not be empty"))]
|
|
||||||
pub user_id: String,
|
|
||||||
|
|
||||||
#[validate(length(min = 1, message = "Item ID must not be empty"))]
|
|
||||||
pub item_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
|
||||||
pub struct GachaRollRequestDto {
|
|
||||||
#[validate(length(min = 1, message = "Item ID must not be empty"))]
|
|
||||||
pub item_id: String,
|
|
||||||
|
|
||||||
#[validate(range(min = 1, message = "Weight must be greater than zero"))]
|
|
||||||
pub weight: f32,
|
|
||||||
|
|
||||||
#[validate(range(min = 1, message = "Quantity must be at least 1"))]
|
|
||||||
pub quantity: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
||||||
pub struct GachaItemDto {
|
|
||||||
pub id: String,
|
|
||||||
pub name: String,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaItemDtoRaw {
|
|
||||||
pub id: Thing,
|
|
||||||
pub name: String,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
||||||
pub struct GachaClaimDto {
|
|
||||||
pub id: String,
|
|
||||||
pub user: String,
|
|
||||||
pub item: String,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaClaimDtoRaw {
|
|
||||||
pub id: Thing,
|
|
||||||
pub user: Thing,
|
|
||||||
pub item: Thing,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
|
||||||
pub struct GachaRollDto {
|
|
||||||
pub id: String,
|
|
||||||
pub item: String,
|
|
||||||
pub weight: String,
|
|
||||||
pub quantity: i32,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaRollDtoRaw {
|
|
||||||
pub id: Thing,
|
|
||||||
pub item: Thing,
|
|
||||||
pub weight: String,
|
|
||||||
pub quantity: i32,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
use crate::{make_thing, ResourceEnum};
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
use surrealdb::{sql::Thing, Uuid};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaItemSchema {
|
|
||||||
pub id: Thing,
|
|
||||||
pub name: String,
|
|
||||||
pub image_url: String,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GachaItemSchema {
|
|
||||||
fn default() -> Self {
|
|
||||||
GachaItemSchema {
|
|
||||||
id: make_thing(
|
|
||||||
&ResourceEnum::GachaItems.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
name: String::new(),
|
|
||||||
image_url: String::new(),
|
|
||||||
is_deleted: false,
|
|
||||||
created_at: None,
|
|
||||||
updated_at: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaClaimSchema {
|
|
||||||
pub id: Thing,
|
|
||||||
pub user: Thing,
|
|
||||||
pub item: Thing,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GachaClaimSchema {
|
|
||||||
fn default() -> Self {
|
|
||||||
GachaClaimSchema {
|
|
||||||
id: make_thing(
|
|
||||||
&ResourceEnum::GachaClaims.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
user: make_thing(
|
|
||||||
&ResourceEnum::Users.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
item: make_thing(
|
|
||||||
&ResourceEnum::GachaItems.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
is_deleted: false,
|
|
||||||
created_at: None,
|
|
||||||
updated_at: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize)]
|
|
||||||
pub struct GachaRollSchema {
|
|
||||||
pub id: Thing,
|
|
||||||
pub item: Thing,
|
|
||||||
pub weight: f32,
|
|
||||||
pub quantity: i32,
|
|
||||||
pub is_deleted: bool,
|
|
||||||
pub created_at: Option<String>,
|
|
||||||
pub updated_at: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Default for GachaRollSchema {
|
|
||||||
fn default() -> Self {
|
|
||||||
GachaRollSchema {
|
|
||||||
id: make_thing(
|
|
||||||
&ResourceEnum::GachaRolls.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
item: make_thing(
|
|
||||||
&ResourceEnum::GachaItems.to_string(),
|
|
||||||
&Uuid::new_v4().to_string(),
|
|
||||||
),
|
|
||||||
weight: 0.2,
|
|
||||||
quantity: 2,
|
|
||||||
is_deleted: false,
|
|
||||||
created_at: None,
|
|
||||||
updated_at: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
pub mod gacha_dto;
|
|
||||||
pub mod gacha_repository;
|
|
||||||
pub mod gacha_schema;
|
|
||||||
|
|
||||||
pub use gacha_repository::*;
|
|
||||||
pub use gacha_schema::*;
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
|
pub struct GachaClaimRequestDto {
|
||||||
|
#[validate(length(min = 1, message = "User ID must not be empty"))]
|
||||||
|
pub user_id: String,
|
||||||
|
|
||||||
|
#[validate(length(min = 1, message = "Item ID must not be empty"))]
|
||||||
|
pub item_id: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
|
pub struct GachaClaimDto {
|
||||||
|
pub id: String,
|
||||||
|
pub user: String,
|
||||||
|
pub item: String,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaClaimDtoRaw {
|
||||||
|
pub id: Thing,
|
||||||
|
pub user: Thing,
|
||||||
|
pub item: Thing,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
use super::GachaClaimSchema;
|
||||||
|
use crate::{AppState, ResourceEnum};
|
||||||
|
use anyhow::{Result, bail};
|
||||||
|
|
||||||
|
pub struct GachaClaimRepository<'a> {
|
||||||
|
state: &'a AppState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GachaClaimRepository<'a> {
|
||||||
|
pub fn new(state: &'a AppState) -> Self {
|
||||||
|
Self { state }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_gacha_claim_by_id(
|
||||||
|
&self,
|
||||||
|
id: String,
|
||||||
|
) -> Result<GachaClaimSchema> {
|
||||||
|
let db = &self.state.surrealdb_ws;
|
||||||
|
let result: Option<GachaClaimSchema> = db
|
||||||
|
.select((ResourceEnum::GachaClaims.to_string(), id.clone()))
|
||||||
|
.await?;
|
||||||
|
match result {
|
||||||
|
Some(claim) if !claim.is_deleted => Ok(claim),
|
||||||
|
_ => bail!("Gacha Claim not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_create_gacha_claim(
|
||||||
|
&self,
|
||||||
|
data: GachaClaimSchema,
|
||||||
|
) -> Result<String> {
|
||||||
|
let db = &self.state.surrealdb_ws;
|
||||||
|
let record: Option<GachaClaimSchema> = db
|
||||||
|
.create(ResourceEnum::GachaClaims.to_string())
|
||||||
|
.content(data)
|
||||||
|
.await?;
|
||||||
|
match record {
|
||||||
|
Some(_) => Ok("Success create Gacha Claim".into()),
|
||||||
|
None => bail!("Failed to create Gacha Claim"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
use crate::{ResourceEnum, make_thing};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::{Uuid, sql::Thing};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaClaimSchema {
|
||||||
|
pub id: Thing,
|
||||||
|
pub user: Thing,
|
||||||
|
pub item: Thing,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GachaClaimSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
GachaClaimSchema {
|
||||||
|
id: make_thing(
|
||||||
|
&ResourceEnum::GachaClaims.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
user: make_thing(
|
||||||
|
&ResourceEnum::Users.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
item: make_thing(
|
||||||
|
&ResourceEnum::GachaItems.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
is_deleted: false,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod gacha_claim_dto;
|
||||||
|
pub mod gacha_claim_repository;
|
||||||
|
pub mod gacha_claim_schema;
|
||||||
|
|
||||||
|
pub use gacha_claim_dto::*;
|
||||||
|
pub use gacha_claim_repository::*;
|
||||||
|
pub use gacha_claim_schema::*;
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
|
pub struct GachaItemRequestDto {
|
||||||
|
#[validate(length(min = 1, message = "Item name must not be empty"))]
|
||||||
|
pub name: String,
|
||||||
|
#[validate(length(min = 1, message = "Image URL must not be empty"))]
|
||||||
|
pub image_url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
|
pub struct GachaItemDto {
|
||||||
|
pub id: String,
|
||||||
|
pub name: String,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaItemDtoRaw {
|
||||||
|
pub id: Thing,
|
||||||
|
pub name: String,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
+3
-58
@@ -1,15 +1,15 @@
|
|||||||
use super::{GachaClaimSchema, GachaItemSchema, GachaRollSchema};
|
use super::GachaItemSchema;
|
||||||
use crate::{
|
use crate::{
|
||||||
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
|
AppState, MetaRequestDto, ResourceEnum, ResponseListSuccessDto, get_id,
|
||||||
make_thing, query_list_with_meta,
|
make_thing, query_list_with_meta,
|
||||||
};
|
};
|
||||||
use anyhow::{Result, bail};
|
use anyhow::{Result, bail};
|
||||||
|
|
||||||
pub struct GachaRepository<'a> {
|
pub struct GachaItemRepository<'a> {
|
||||||
state: &'a AppState,
|
state: &'a AppState,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> GachaRepository<'a> {
|
impl<'a> GachaItemRepository<'a> {
|
||||||
pub fn new(state: &'a AppState) -> Self {
|
pub fn new(state: &'a AppState) -> Self {
|
||||||
Self { state }
|
Self { state }
|
||||||
}
|
}
|
||||||
@@ -97,59 +97,4 @@ impl<'a> GachaRepository<'a> {
|
|||||||
None => bail!("Failed to delete Gacha Item"),
|
None => bail!("Failed to delete Gacha Item"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn query_gacha_claim_by_id(
|
|
||||||
&self,
|
|
||||||
id: String,
|
|
||||||
) -> Result<GachaClaimSchema> {
|
|
||||||
let db = &self.state.surrealdb_ws;
|
|
||||||
let result: Option<GachaClaimSchema> = db
|
|
||||||
.select((ResourceEnum::GachaClaims.to_string(), id.clone()))
|
|
||||||
.await?;
|
|
||||||
match result {
|
|
||||||
Some(claim) if !claim.is_deleted => Ok(claim),
|
|
||||||
_ => bail!("Gacha Claim not found"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn query_create_gacha_claim(
|
|
||||||
&self,
|
|
||||||
data: GachaClaimSchema,
|
|
||||||
) -> Result<String> {
|
|
||||||
let db = &self.state.surrealdb_ws;
|
|
||||||
let record: Option<GachaClaimSchema> = db
|
|
||||||
.create(ResourceEnum::GachaClaims.to_string())
|
|
||||||
.content(data)
|
|
||||||
.await?;
|
|
||||||
match record {
|
|
||||||
Some(_) => Ok("Success create Gacha Claim".into()),
|
|
||||||
None => bail!("Failed to create Gacha Claim"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn query_gacha_roll_by_id(&self, id: String) -> Result<GachaRollSchema> {
|
|
||||||
let db = &self.state.surrealdb_ws;
|
|
||||||
let result: Option<GachaRollSchema> = db
|
|
||||||
.select((ResourceEnum::GachaRolls.to_string(), id.clone()))
|
|
||||||
.await?;
|
|
||||||
match result {
|
|
||||||
Some(roll) if !roll.is_deleted => Ok(roll),
|
|
||||||
_ => bail!("Gacha Roll not found"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub async fn query_create_gacha_roll(
|
|
||||||
&self,
|
|
||||||
data: GachaRollSchema,
|
|
||||||
) -> Result<String> {
|
|
||||||
let db = &self.state.surrealdb_ws;
|
|
||||||
let record: Option<GachaRollSchema> = db
|
|
||||||
.create(ResourceEnum::GachaRolls.to_string())
|
|
||||||
.content(data)
|
|
||||||
.await?;
|
|
||||||
match record {
|
|
||||||
Some(_) => Ok("Success create Gacha Roll".into()),
|
|
||||||
None => bail!("Failed to create Gacha Roll"),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
use crate::{ResourceEnum, make_thing};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::{Uuid, sql::Thing};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaItemSchema {
|
||||||
|
pub id: Thing,
|
||||||
|
pub name: String,
|
||||||
|
pub image_url: String,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GachaItemSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
GachaItemSchema {
|
||||||
|
id: make_thing(
|
||||||
|
&ResourceEnum::GachaItems.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
name: String::new(),
|
||||||
|
image_url: String::new(),
|
||||||
|
is_deleted: false,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod gacha_item_dto;
|
||||||
|
pub mod gacha_item_repository;
|
||||||
|
pub mod gacha_item_schema;
|
||||||
|
|
||||||
|
pub use gacha_item_dto::*;
|
||||||
|
pub use gacha_item_repository::*;
|
||||||
|
pub use gacha_item_schema::*;
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::sql::Thing;
|
||||||
|
use utoipa::ToSchema;
|
||||||
|
use validator::Validate;
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema, Validate)]
|
||||||
|
pub struct GachaRollRequestDto {
|
||||||
|
#[validate(length(min = 1, message = "Item ID must not be empty"))]
|
||||||
|
pub item_id: String,
|
||||||
|
|
||||||
|
#[validate(range(min = 1, message = "Weight must be greater than zero"))]
|
||||||
|
pub weight: f32,
|
||||||
|
|
||||||
|
#[validate(range(min = 1, message = "Quantity must be at least 1"))]
|
||||||
|
pub quantity: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize, ToSchema)]
|
||||||
|
pub struct GachaRollDto {
|
||||||
|
pub id: String,
|
||||||
|
pub item: String,
|
||||||
|
pub weight: String,
|
||||||
|
pub quantity: i32,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaRollDtoRaw {
|
||||||
|
pub id: Thing,
|
||||||
|
pub item: Thing,
|
||||||
|
pub weight: String,
|
||||||
|
pub quantity: i32,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
use super::GachaRollSchema;
|
||||||
|
use crate::{AppState, ResourceEnum};
|
||||||
|
use anyhow::{Result, bail};
|
||||||
|
|
||||||
|
pub struct GachaRollRepository<'a> {
|
||||||
|
state: &'a AppState,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> GachaRollRepository<'a> {
|
||||||
|
pub fn new(state: &'a AppState) -> Self {
|
||||||
|
Self { state }
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_gacha_roll_by_id(&self, id: String) -> Result<GachaRollSchema> {
|
||||||
|
let db = &self.state.surrealdb_ws;
|
||||||
|
let result: Option<GachaRollSchema> = db
|
||||||
|
.select((ResourceEnum::GachaRolls.to_string(), id.clone()))
|
||||||
|
.await?;
|
||||||
|
match result {
|
||||||
|
Some(roll) if !roll.is_deleted => Ok(roll),
|
||||||
|
_ => bail!("Gacha Roll not found"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn query_create_gacha_roll(
|
||||||
|
&self,
|
||||||
|
data: GachaRollSchema,
|
||||||
|
) -> Result<String> {
|
||||||
|
let db = &self.state.surrealdb_ws;
|
||||||
|
let record: Option<GachaRollSchema> = db
|
||||||
|
.create(ResourceEnum::GachaRolls.to_string())
|
||||||
|
.content(data)
|
||||||
|
.await?;
|
||||||
|
match record {
|
||||||
|
Some(_) => Ok("Success create Gacha Roll".into()),
|
||||||
|
None => bail!("Failed to create Gacha Roll"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
use crate::{ResourceEnum, make_thing};
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
use surrealdb::{Uuid, sql::Thing};
|
||||||
|
|
||||||
|
#[derive(Clone, Debug, Serialize, Deserialize)]
|
||||||
|
pub struct GachaRollSchema {
|
||||||
|
pub id: Thing,
|
||||||
|
pub item: Thing,
|
||||||
|
pub weight: f32,
|
||||||
|
pub quantity: i32,
|
||||||
|
pub is_deleted: bool,
|
||||||
|
pub created_at: Option<String>,
|
||||||
|
pub updated_at: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for GachaRollSchema {
|
||||||
|
fn default() -> Self {
|
||||||
|
GachaRollSchema {
|
||||||
|
id: make_thing(
|
||||||
|
&ResourceEnum::GachaRolls.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
item: make_thing(
|
||||||
|
&ResourceEnum::GachaItems.to_string(),
|
||||||
|
&Uuid::new_v4().to_string(),
|
||||||
|
),
|
||||||
|
weight: 0.2,
|
||||||
|
quantity: 2,
|
||||||
|
is_deleted: false,
|
||||||
|
created_at: None,
|
||||||
|
updated_at: None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod gacha_roll_dto;
|
||||||
|
pub mod gacha_roll_repository;
|
||||||
|
pub mod gacha_roll_schema;
|
||||||
|
|
||||||
|
pub use gacha_roll_dto::*;
|
||||||
|
pub use gacha_roll_repository::*;
|
||||||
|
pub use gacha_roll_schema::*;
|
||||||
@@ -1,2 +1,7 @@
|
|||||||
pub mod gacha;
|
pub mod gacha_claim;
|
||||||
pub use gacha::*;
|
pub mod gacha_item;
|
||||||
|
pub mod gacha_roll;
|
||||||
|
|
||||||
|
pub use gacha_claim::*;
|
||||||
|
pub use gacha_item::*;
|
||||||
|
pub use gacha_roll::*;
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ edition = "2024"
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
||||||
|
imphnen-middleware-service = { version = "0.1.0", path = "../imphnen-middleware-service" }
|
||||||
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
|||||||
@@ -5,10 +5,8 @@ use imphnen_entities::{AppState, SurrealMemClient, SurrealWsClient};
|
|||||||
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
|
use imphnen_iam_service::{iam_protected_routes, iam_public_routes};
|
||||||
|
|
||||||
pub mod docs;
|
pub mod docs;
|
||||||
pub mod middleware;
|
|
||||||
|
|
||||||
pub use docs::*;
|
pub use docs::*;
|
||||||
pub use middleware::*;
|
use imphnen_middleware_service::{auth_middleware, cors_middleware};
|
||||||
use utoipa_swagger_ui::SwaggerUi;
|
use utoipa_swagger_ui::SwaggerUi;
|
||||||
|
|
||||||
pub async fn gateway_service(
|
pub async fn gateway_service(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ use std::fmt;
|
|||||||
pub enum RolesEnum {
|
pub enum RolesEnum {
|
||||||
Admin,
|
Admin,
|
||||||
User,
|
User,
|
||||||
|
Student,
|
||||||
Staf,
|
Staf,
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -12,6 +13,7 @@ impl fmt::Display for RolesEnum {
|
|||||||
let roles_str = match self {
|
let roles_str = match self {
|
||||||
RolesEnum::Admin => "Admin",
|
RolesEnum::Admin => "Admin",
|
||||||
RolesEnum::User => "User",
|
RolesEnum::User => "User",
|
||||||
|
RolesEnum::Student => "Student",
|
||||||
RolesEnum::Staf => "Staf",
|
RolesEnum::Staf => "Staf",
|
||||||
};
|
};
|
||||||
write!(f, "{}", roles_str)
|
write!(f, "{}", roles_str)
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[package]
|
||||||
|
name = "imphnen-middleware-service"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
imphnen-iam-service = { version = "0.1.0", path = "../imphnen-iam-service" }
|
||||||
|
imphnen-entities = { version = "0.1.0", path = "../imphnen-entities" }
|
||||||
|
imphnen-libs = { version = "0.1.0", path = "../imphnen-libs" }
|
||||||
|
imphnen-utils = { version = "0.1.0", path = "../imphnen-utils" }
|
||||||
|
axum.workspace = true
|
||||||
|
serde.workspace = true
|
||||||
|
serde_json.workspace = true
|
||||||
|
utoipa.workspace = true
|
||||||
|
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
|
||||||
|
anyhow.workspace = true
|
||||||
|
tower-http.workspace = true
|
||||||
|
utoipa-swagger-ui.workspace = true
|
||||||
|
futures = "0.3.31"
|
||||||
|
tower = "0.5.2"
|
||||||
+2
-31
@@ -1,15 +1,11 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
Extension,
|
Extension, extract::Request, http::StatusCode, middleware::Next,
|
||||||
extract::Request,
|
|
||||||
http::{HeaderValue, Method, StatusCode, header},
|
|
||||||
middleware::Next,
|
|
||||||
response::Response,
|
response::Response,
|
||||||
};
|
};
|
||||||
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
|
use imphnen_iam_service::{UsersItemDtoRaw, UsersRepository};
|
||||||
use imphnen_libs::{AppState, Env};
|
use imphnen_libs::AppState;
|
||||||
use imphnen_utils::{common_response, extract_email};
|
use imphnen_utils::{common_response, extract_email};
|
||||||
use std::convert::Infallible;
|
use std::convert::Infallible;
|
||||||
use tower_http::cors::CorsLayer;
|
|
||||||
|
|
||||||
pub async fn auth_middleware(
|
pub async fn auth_middleware(
|
||||||
Extension(state): Extension<AppState>,
|
Extension(state): Extension<AppState>,
|
||||||
@@ -46,28 +42,3 @@ pub async fn auth_middleware(
|
|||||||
req.extensions_mut().insert(user.unwrap());
|
req.extensions_mut().insert(user.unwrap());
|
||||||
Ok(next.run(req).await)
|
Ok(next.run(req).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn cors_middleware() -> CorsLayer {
|
|
||||||
let env = Env::new();
|
|
||||||
let cors_origins = match env.rust_env.as_str() {
|
|
||||||
"development" => vec!["http://localhost:3000"],
|
|
||||||
"production" => {
|
|
||||||
vec!["https://gacha.imphnen.dev", "https://imphnen.dev"]
|
|
||||||
}
|
|
||||||
_ => vec![
|
|
||||||
"http://localhost:3000",
|
|
||||||
"https://gacha.imphnen.dev",
|
|
||||||
"https://imphnen.dev",
|
|
||||||
],
|
|
||||||
};
|
|
||||||
let allowed_origins: Vec<HeaderValue> = cors_origins
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
CorsLayer::new()
|
|
||||||
.allow_origin(allowed_origins)
|
|
||||||
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
|
||||||
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
|
|
||||||
.allow_credentials(true)
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
use axum::http::{HeaderValue, Method, header};
|
||||||
|
use imphnen_libs::Env;
|
||||||
|
use tower_http::cors::CorsLayer;
|
||||||
|
|
||||||
|
pub fn cors_middleware() -> CorsLayer {
|
||||||
|
let env = Env::new();
|
||||||
|
let cors_origins = match env.rust_env.as_str() {
|
||||||
|
"development" => vec!["http://localhost:3000"],
|
||||||
|
"production" => {
|
||||||
|
vec![
|
||||||
|
"https://gacha.imphnen.dev",
|
||||||
|
"https://imphnen.dev",
|
||||||
|
"https://dimentorin.imphnen.dev",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
_ => vec![
|
||||||
|
"http://localhost:3000",
|
||||||
|
"https://gacha.imphnen.dev",
|
||||||
|
"https://imphnen.dev",
|
||||||
|
"https://dimentorin.imphnen.dev",
|
||||||
|
],
|
||||||
|
};
|
||||||
|
let allowed_origins: Vec<HeaderValue> = cors_origins
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|origin| origin.parse::<HeaderValue>().ok())
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
CorsLayer::new()
|
||||||
|
.allow_origin(allowed_origins)
|
||||||
|
.allow_methods([Method::GET, Method::POST, Method::PUT, Method::DELETE])
|
||||||
|
.allow_headers([header::AUTHORIZATION, header::CONTENT_TYPE])
|
||||||
|
.allow_credentials(true)
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
pub mod auth_middleware;
|
||||||
|
pub mod cors_middleware;
|
||||||
|
pub mod permissions_middleware;
|
||||||
|
|
||||||
|
pub use auth_middleware::*;
|
||||||
|
pub use cors_middleware::*;
|
||||||
|
pub use permissions_middleware::*;
|
||||||
@@ -0,0 +1,102 @@
|
|||||||
|
use axum::{
|
||||||
|
body::Body,
|
||||||
|
http::{Request, Response, StatusCode},
|
||||||
|
};
|
||||||
|
use futures::future::BoxFuture;
|
||||||
|
use imphnen_iam_service::{AuthRepository, PermissionsEnum};
|
||||||
|
use imphnen_libs::AppState;
|
||||||
|
use imphnen_utils::{common_response, extract_email};
|
||||||
|
use std::task::{Context, Poll};
|
||||||
|
use tower::{Layer, Service};
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PermissionsMiddlewareLayer {
|
||||||
|
app_state: AppState,
|
||||||
|
permissions: Vec<PermissionsEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PermissionsMiddlewareLayer {
|
||||||
|
pub fn new(app_state: AppState, permissions: Vec<PermissionsEnum>) -> Self {
|
||||||
|
Self {
|
||||||
|
app_state,
|
||||||
|
permissions,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Layer<S> for PermissionsMiddlewareLayer {
|
||||||
|
type Service = PermissionsMiddleware<S>;
|
||||||
|
fn layer(&self, inner: S) -> Self::Service {
|
||||||
|
PermissionsMiddleware {
|
||||||
|
inner,
|
||||||
|
app_state: self.app_state.clone(),
|
||||||
|
permissions: self.permissions.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub struct PermissionsMiddleware<S> {
|
||||||
|
inner: S,
|
||||||
|
app_state: AppState,
|
||||||
|
permissions: Vec<PermissionsEnum>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S> Service<Request<Body>> for PermissionsMiddleware<S>
|
||||||
|
where
|
||||||
|
S: Service<Request<Body>, Response = Response<Body>> + Clone + Send + 'static,
|
||||||
|
S::Future: Send + 'static,
|
||||||
|
{
|
||||||
|
type Response = S::Response;
|
||||||
|
type Error = S::Error;
|
||||||
|
type Future = BoxFuture<'static, Result<Self::Response, Self::Error>>;
|
||||||
|
|
||||||
|
fn poll_ready(&mut self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
|
||||||
|
self.inner.poll_ready(cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn call(&mut self, req: Request<Body>) -> Self::Future {
|
||||||
|
let mut inner = self.inner.clone();
|
||||||
|
let app_state = self.app_state.clone();
|
||||||
|
let permissions = self.permissions.clone();
|
||||||
|
|
||||||
|
Box::pin(async move {
|
||||||
|
let headers = req.headers();
|
||||||
|
let email = match extract_email(headers) {
|
||||||
|
Some(email) => email,
|
||||||
|
None => {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
"Invalid or missing authorization token",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let auth_repo = AuthRepository::new(&app_state);
|
||||||
|
let user = match auth_repo.query_get_stored_user(email).await {
|
||||||
|
Ok(user) => user,
|
||||||
|
Err(_) => {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::UNAUTHORIZED,
|
||||||
|
"User session expired or not found",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let user_permissions: Vec<String> =
|
||||||
|
user.role.permissions.into_iter().map(|p| p.name).collect();
|
||||||
|
let allowed = permissions
|
||||||
|
.iter()
|
||||||
|
.all(|p| user_permissions.contains(&p.to_string()));
|
||||||
|
|
||||||
|
if !allowed {
|
||||||
|
return Ok(common_response(
|
||||||
|
StatusCode::FORBIDDEN,
|
||||||
|
"You don't have the required permissions",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
inner.call(req).await
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user