removing rbac cos its not needed at all
This commit is contained in:
1
Cargo.lock
generated
1
Cargo.lock
generated
@@ -5071,6 +5071,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"serial_test",
|
"serial_test",
|
||||||
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ sea-orm = { version = "1.1", features = [
|
|||||||
"macros",
|
"macros",
|
||||||
] }
|
] }
|
||||||
chrono = { version = "0.4" }
|
chrono = { version = "0.4" }
|
||||||
|
time = { version = "0.3" }
|
||||||
validator = { version = "0.20" }
|
validator = { version = "0.20" }
|
||||||
uuid = { version = "1.6", features = ["v4"] }
|
uuid = { version = "1.6", features = ["v4"] }
|
||||||
include_dir = { version = "0.7" }
|
include_dir = { version = "0.7" }
|
||||||
|
|||||||
@@ -93,7 +93,14 @@ database:
|
|||||||
auth:
|
auth:
|
||||||
# JWT authentication
|
# JWT authentication
|
||||||
jwt:
|
jwt:
|
||||||
|
location:
|
||||||
|
- from: Cookie
|
||||||
|
name: auth_token
|
||||||
|
- from: Bearer
|
||||||
# Secret key for token generation and verification
|
# Secret key for token generation and verification
|
||||||
secret: A6ECni63rt2Jb00tX9Hf
|
secret: A6ECni63rt2Jb00tX9Hf
|
||||||
# Token expiration time in seconds
|
# Token expiration time in seconds
|
||||||
expiration: 604800 # 7 days
|
expiration: 604800 # 7 days
|
||||||
|
|
||||||
|
settings:
|
||||||
|
admin_email: {{ get_env(name="ADMIN_EMAIL", default="admin@example.com") }}
|
||||||
|
|||||||
@@ -90,7 +90,14 @@ database:
|
|||||||
auth:
|
auth:
|
||||||
# JWT authentication
|
# JWT authentication
|
||||||
jwt:
|
jwt:
|
||||||
|
location:
|
||||||
|
- from: Cookie
|
||||||
|
name: auth_token
|
||||||
|
- from: Bearer
|
||||||
# Secret key for token generation and verification
|
# Secret key for token generation and verification
|
||||||
secret: 0yWwoflcGiAhonIzhQyQ
|
secret: 0yWwoflcGiAhonIzhQyQ
|
||||||
# Token expiration time in seconds
|
# Token expiration time in seconds
|
||||||
expiration: 604800 # 7 days
|
expiration: 604800 # 7 days
|
||||||
|
|
||||||
|
settings:
|
||||||
|
admin_email: admin@example.com
|
||||||
|
|||||||
@@ -10,6 +10,8 @@ mod m20260517_000005_audio_albums;
|
|||||||
mod m20260517_000006_audio_tracks;
|
mod m20260517_000006_audio_tracks;
|
||||||
mod m20260517_000007_audio_tags;
|
mod m20260517_000007_audio_tags;
|
||||||
mod m20260517_000008_audio_track_tags;
|
mod m20260517_000008_audio_track_tags;
|
||||||
|
mod m20260517_000009_simple_constraints;
|
||||||
|
mod m20260517_000010_drop_user_roles;
|
||||||
|
|
||||||
pub struct Migrator;
|
pub struct Migrator;
|
||||||
|
|
||||||
@@ -26,6 +28,8 @@ impl MigratorTrait for Migrator {
|
|||||||
Box::new(m20260517_000006_audio_tracks::Migration),
|
Box::new(m20260517_000006_audio_tracks::Migration),
|
||||||
Box::new(m20260517_000007_audio_tags::Migration),
|
Box::new(m20260517_000007_audio_tags::Migration),
|
||||||
Box::new(m20260517_000008_audio_track_tags::Migration),
|
Box::new(m20260517_000008_audio_track_tags::Migration),
|
||||||
|
Box::new(m20260517_000009_simple_constraints::Migration),
|
||||||
|
Box::new(m20260517_000010_drop_user_roles::Migration),
|
||||||
// inject-above (do not remove this comment)
|
// inject-above (do not remove this comment)
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
60
migration/src/m20260517_000009_simple_constraints.rs
Normal file
60
migration/src/m20260517_000009_simple_constraints.rs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
use sea_orm_migration::{prelude::*, sea_orm::ConnectionTrait};
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum AudioTrackTags {
|
||||||
|
Table,
|
||||||
|
TagId,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
if matches!(
|
||||||
|
m.get_database_backend(),
|
||||||
|
sea_orm_migration::sea_orm::DatabaseBackend::Postgres
|
||||||
|
) {
|
||||||
|
m.get_connection()
|
||||||
|
.execute_unprepared(
|
||||||
|
"ALTER TABLE users \
|
||||||
|
ADD CONSTRAINT chk_users_theme \
|
||||||
|
CHECK (theme IN ('light', 'dark'))",
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
m.create_index(
|
||||||
|
Index::create()
|
||||||
|
.name("idx-audio_track_tags-tag_id")
|
||||||
|
.table(AudioTrackTags::Table)
|
||||||
|
.col(AudioTrackTags::TagId)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
m.drop_index(
|
||||||
|
Index::drop()
|
||||||
|
.name("idx-audio_track_tags-tag_id")
|
||||||
|
.table(AudioTrackTags::Table)
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
if matches!(
|
||||||
|
m.get_database_backend(),
|
||||||
|
sea_orm_migration::sea_orm::DatabaseBackend::Postgres
|
||||||
|
) {
|
||||||
|
m.get_connection()
|
||||||
|
.execute_unprepared("ALTER TABLE users DROP CONSTRAINT chk_users_theme")
|
||||||
|
.await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
28
migration/src/m20260517_000010_drop_user_roles.rs
Normal file
28
migration/src/m20260517_000010_drop_user_roles.rs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
use sea_orm_migration::prelude::*;
|
||||||
|
|
||||||
|
#[derive(DeriveMigrationName)]
|
||||||
|
pub struct Migration;
|
||||||
|
|
||||||
|
#[derive(DeriveIden)]
|
||||||
|
enum UserRoles {
|
||||||
|
Table,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait::async_trait]
|
||||||
|
impl MigrationTrait for Migration {
|
||||||
|
async fn up(&self, m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
m.drop_table(
|
||||||
|
Table::drop()
|
||||||
|
.table(UserRoles::Table)
|
||||||
|
.if_exists()
|
||||||
|
.cascade()
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn down(&self, _m: &SchemaManager) -> Result<(), DbErr> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -52,6 +52,7 @@ impl Hooks for App {
|
|||||||
fn routes(_ctx: &AppContext) -> AppRoutes {
|
fn routes(_ctx: &AppContext) -> AppRoutes {
|
||||||
AppRoutes::with_default_routes() // controller routes below
|
AppRoutes::with_default_routes() // controller routes below
|
||||||
.add_route(controllers::auth::routes())
|
.add_route(controllers::auth::routes())
|
||||||
|
.add_route(controllers::admin::routes())
|
||||||
}
|
}
|
||||||
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
|
async fn connect_workers(ctx: &AppContext, queue: &Queue) -> Result<()> {
|
||||||
queue.register(DownloadWorker::build(ctx)).await?;
|
queue.register(DownloadWorker::build(ctx)).await?;
|
||||||
|
|||||||
57
src/controllers/admin.rs
Normal file
57
src/controllers/admin.rs
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
use crate::models::{
|
||||||
|
_entities::{audio_albums, audio_tracks, audit_logs, blog_articles, users},
|
||||||
|
users as users_model,
|
||||||
|
};
|
||||||
|
use loco_rs::prelude::*;
|
||||||
|
use sea_orm::{EntityTrait, PaginatorTrait};
|
||||||
|
use serde::Serialize;
|
||||||
|
|
||||||
|
#[derive(Debug, Serialize)]
|
||||||
|
struct DashboardResponse {
|
||||||
|
users: u64,
|
||||||
|
blog_articles: u64,
|
||||||
|
audio_albums: u64,
|
||||||
|
audio_tracks: u64,
|
||||||
|
audit_logs: u64,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn admin_email(ctx: &AppContext) -> Option<&str> {
|
||||||
|
ctx.config
|
||||||
|
.settings
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|settings| settings.get("admin_email"))
|
||||||
|
.and_then(|email| email.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_admin(ctx: &AppContext, user: &users::Model) -> bool {
|
||||||
|
admin_email(ctx).is_some_and(|email| user.email.eq_ignore_ascii_case(email))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn current_admin(auth: auth::JWT, ctx: &AppContext) -> Result<users::Model> {
|
||||||
|
let user = users_model::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
|
||||||
|
|
||||||
|
if !is_admin(ctx, &user) {
|
||||||
|
return unauthorized("admin only");
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn dashboard(auth: auth::JWT, State(ctx): State<AppContext>) -> Result<Response> {
|
||||||
|
current_admin(auth, &ctx).await?;
|
||||||
|
|
||||||
|
format::json(DashboardResponse {
|
||||||
|
users: users::Entity::find().count(&ctx.db).await?,
|
||||||
|
blog_articles: blog_articles::Entity::find().count(&ctx.db).await?,
|
||||||
|
audio_albums: audio_albums::Entity::find().count(&ctx.db).await?,
|
||||||
|
audio_tracks: audio_tracks::Entity::find().count(&ctx.db).await?,
|
||||||
|
audit_logs: audit_logs::Entity::find().count(&ctx.db).await?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn routes() -> Routes {
|
||||||
|
Routes::new()
|
||||||
|
.prefix("/api/admin")
|
||||||
|
.add("/dashboard", get(dashboard))
|
||||||
|
}
|
||||||
@@ -6,12 +6,15 @@ use crate::{
|
|||||||
},
|
},
|
||||||
views::auth::{CurrentResponse, LoginResponse},
|
views::auth::{CurrentResponse, LoginResponse},
|
||||||
};
|
};
|
||||||
|
use axum_extra::extract::cookie::{Cookie, SameSite};
|
||||||
use loco_rs::prelude::*;
|
use loco_rs::prelude::*;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::sync::OnceLock;
|
use std::sync::OnceLock;
|
||||||
|
use time::Duration as TimeDuration;
|
||||||
|
|
||||||
pub static EMAIL_DOMAIN_RE: OnceLock<Regex> = OnceLock::new();
|
pub static EMAIL_DOMAIN_RE: OnceLock<Regex> = OnceLock::new();
|
||||||
|
const AUTH_COOKIE: &str = "auth_token";
|
||||||
|
|
||||||
fn get_allow_email_domain_re() -> &'static Regex {
|
fn get_allow_email_domain_re() -> &'static Regex {
|
||||||
EMAIL_DOMAIN_RE.get_or_init(|| {
|
EMAIL_DOMAIN_RE.get_or_init(|| {
|
||||||
@@ -19,6 +22,36 @@ fn get_allow_email_domain_re() -> &'static Regex {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn admin_email(ctx: &AppContext) -> Option<&str> {
|
||||||
|
ctx.config
|
||||||
|
.settings
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|settings| settings.get("admin_email"))
|
||||||
|
.and_then(|email| email.as_str())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_admin(ctx: &AppContext, user: &users::Model) -> bool {
|
||||||
|
admin_email(ctx).is_some_and(|email| user.email.eq_ignore_ascii_case(email))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn auth_cookie(token: &str, max_age_seconds: u64) -> Cookie<'static> {
|
||||||
|
Cookie::build((AUTH_COOKIE, token.to_string()))
|
||||||
|
.path("/")
|
||||||
|
.http_only(true)
|
||||||
|
.same_site(SameSite::Lax)
|
||||||
|
.max_age(TimeDuration::seconds(max_age_seconds as i64))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clear_auth_cookie() -> Cookie<'static> {
|
||||||
|
Cookie::build((AUTH_COOKIE, ""))
|
||||||
|
.path("/")
|
||||||
|
.http_only(true)
|
||||||
|
.same_site(SameSite::Lax)
|
||||||
|
.max_age(TimeDuration::seconds(0))
|
||||||
|
.build()
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize)]
|
#[derive(Debug, Deserialize, Serialize)]
|
||||||
pub struct ForgotParams {
|
pub struct ForgotParams {
|
||||||
pub email: String,
|
pub email: String,
|
||||||
@@ -155,13 +188,20 @@ async fn login(State(ctx): State<AppContext>, Json(params): Json<LoginParams>) -
|
|||||||
.generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
|
.generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
|
||||||
.or_else(|_| unauthorized("unauthorized!"))?;
|
.or_else(|_| unauthorized("unauthorized!"))?;
|
||||||
|
|
||||||
format::json(LoginResponse::new(&user, &token))
|
format::render()
|
||||||
|
.cookies(&[auth_cookie(&token, jwt_secret.expiration)])?
|
||||||
|
.json(LoginResponse::new(&user, &token, is_admin(&ctx, &user)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
async fn current(auth: auth::JWT, State(ctx): State<AppContext>) -> Result<Response> {
|
async fn current(auth: auth::JWT, State(ctx): State<AppContext>) -> Result<Response> {
|
||||||
let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
|
let user = users::Model::find_by_pid(&ctx.db, &auth.claims.pid).await?;
|
||||||
format::json(CurrentResponse::new(&user))
|
format::json(CurrentResponse::new(&user, is_admin(&ctx, &user)))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[debug_handler]
|
||||||
|
async fn logout() -> Result<Response> {
|
||||||
|
format::render().cookies(&[clear_auth_cookie()])?.json(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Magic link authentication provides a secure and passwordless way to log in to the application.
|
/// Magic link authentication provides a secure and passwordless way to log in to the application.
|
||||||
@@ -223,7 +263,9 @@ async fn magic_link_verify(
|
|||||||
.generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
|
.generate_jwt(&jwt_secret.secret, jwt_secret.expiration)
|
||||||
.or_else(|_| unauthorized("unauthorized!"))?;
|
.or_else(|_| unauthorized("unauthorized!"))?;
|
||||||
|
|
||||||
format::json(LoginResponse::new(&user, &token))
|
format::render()
|
||||||
|
.cookies(&[auth_cookie(&token, jwt_secret.expiration)])?
|
||||||
|
.json(LoginResponse::new(&user, &token, is_admin(&ctx, &user)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[debug_handler]
|
#[debug_handler]
|
||||||
@@ -264,6 +306,7 @@ pub fn routes() -> Routes {
|
|||||||
.add("/register", post(register))
|
.add("/register", post(register))
|
||||||
.add("/verify/{token}", get(verify))
|
.add("/verify/{token}", get(verify))
|
||||||
.add("/login", post(login))
|
.add("/login", post(login))
|
||||||
|
.add("/logout", post(logout))
|
||||||
.add("/forgot", post(forgot))
|
.add("/forgot", post(forgot))
|
||||||
.add("/reset", post(reset))
|
.add("/reset", post(reset))
|
||||||
.add("/current", get(current))
|
.add("/current", get(current))
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
|
pub mod admin;
|
||||||
pub mod auth;
|
pub mod auth;
|
||||||
|
|||||||
@@ -8,5 +8,4 @@ pub mod audio_track_tags;
|
|||||||
pub mod audio_tracks;
|
pub mod audio_tracks;
|
||||||
pub mod audit_logs;
|
pub mod audit_logs;
|
||||||
pub mod blog_articles;
|
pub mod blog_articles;
|
||||||
pub mod user_roles;
|
|
||||||
pub mod users;
|
pub mod users;
|
||||||
|
|||||||
@@ -6,5 +6,4 @@ pub use super::audio_track_tags::Entity as AudioTrackTags;
|
|||||||
pub use super::audio_tracks::Entity as AudioTracks;
|
pub use super::audio_tracks::Entity as AudioTracks;
|
||||||
pub use super::audit_logs::Entity as AuditLogs;
|
pub use super::audit_logs::Entity as AuditLogs;
|
||||||
pub use super::blog_articles::Entity as BlogArticles;
|
pub use super::blog_articles::Entity as BlogArticles;
|
||||||
pub use super::user_roles::Entity as UserRoles;
|
|
||||||
pub use super::users::Entity as Users;
|
pub use super::users::Entity as Users;
|
||||||
|
|||||||
@@ -1,35 +0,0 @@
|
|||||||
//! `SeaORM` Entity, @generated by sea-orm-codegen 1.1.20
|
|
||||||
|
|
||||||
use sea_orm::entity::prelude::*;
|
|
||||||
use serde::{Deserialize, Serialize};
|
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, DeriveEntityModel, Eq, Serialize, Deserialize)]
|
|
||||||
#[sea_orm(table_name = "user_roles")]
|
|
||||||
pub struct Model {
|
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
|
||||||
pub user_id: i32,
|
|
||||||
#[sea_orm(primary_key, auto_increment = false)]
|
|
||||||
pub role: String,
|
|
||||||
pub assigned_by: Option<i32>,
|
|
||||||
pub assigned_at: DateTimeWithTimeZone,
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Copy, Clone, Debug, EnumIter, DeriveRelation)]
|
|
||||||
pub enum Relation {
|
|
||||||
#[sea_orm(
|
|
||||||
belongs_to = "super::users::Entity",
|
|
||||||
from = "Column::AssignedBy",
|
|
||||||
to = "super::users::Column::Id",
|
|
||||||
on_update = "NoAction",
|
|
||||||
on_delete = "SetNull"
|
|
||||||
)]
|
|
||||||
Users2,
|
|
||||||
#[sea_orm(
|
|
||||||
belongs_to = "super::users::Entity",
|
|
||||||
from = "Column::UserId",
|
|
||||||
to = "super::users::Column::Id",
|
|
||||||
on_update = "Cascade",
|
|
||||||
on_delete = "Cascade"
|
|
||||||
)]
|
|
||||||
Users1,
|
|
||||||
}
|
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
pub mod _entities;
|
pub mod _entities;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
pub mod user_roles;
|
|
||||||
pub mod audio_tags;
|
pub mod audio_tags;
|
||||||
pub mod audio_tracks;
|
pub mod audio_tracks;
|
||||||
pub mod audio_track_tags;
|
pub mod audio_track_tags;
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
use sea_orm::entity::prelude::*;
|
|
||||||
pub use super::_entities::user_roles::{ActiveModel, Model, Entity};
|
|
||||||
pub type UserRoles = Entity;
|
|
||||||
|
|
||||||
#[async_trait::async_trait]
|
|
||||||
impl ActiveModelBehavior for ActiveModel {
|
|
||||||
async fn before_save<C>(self, _db: &C, _insert: bool) -> std::result::Result<Self, DbErr>
|
|
||||||
where
|
|
||||||
C: ConnectionTrait,
|
|
||||||
{
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// implement your read-oriented logic here
|
|
||||||
impl Model {}
|
|
||||||
|
|
||||||
// implement your write-oriented logic here
|
|
||||||
impl ActiveModel {}
|
|
||||||
|
|
||||||
// implement your custom finders, selectors oriented logic here
|
|
||||||
impl Entity {}
|
|
||||||
@@ -8,16 +8,18 @@ pub struct LoginResponse {
|
|||||||
pub pid: String,
|
pub pid: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub is_verified: bool,
|
pub is_verified: bool,
|
||||||
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl LoginResponse {
|
impl LoginResponse {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(user: &users::Model, token: &String) -> Self {
|
pub fn new(user: &users::Model, token: &String, is_admin: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
token: token.to_string(),
|
token: token.to_string(),
|
||||||
pid: user.pid.to_string(),
|
pid: user.pid.to_string(),
|
||||||
name: user.name.clone(),
|
name: user.name.clone(),
|
||||||
is_verified: user.email_verified_at.is_some(),
|
is_verified: user.email_verified_at.is_some(),
|
||||||
|
is_admin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -27,15 +29,17 @@ pub struct CurrentResponse {
|
|||||||
pub pid: String,
|
pub pid: String,
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub email: String,
|
pub email: String,
|
||||||
|
pub is_admin: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CurrentResponse {
|
impl CurrentResponse {
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new(user: &users::Model) -> Self {
|
pub fn new(user: &users::Model, is_admin: bool) -> Self {
|
||||||
Self {
|
Self {
|
||||||
pid: user.pid.to_string(),
|
pid: user.pid.to_string(),
|
||||||
name: user.name.clone(),
|
name: user.name.clone(),
|
||||||
email: user.email.clone(),
|
email: user.email.clone(),
|
||||||
|
is_admin,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user