admin/roles: view, archive and restore
This commit is contained in:
parent
8fa2fb7ddc
commit
d778380d8b
10 changed files with 363 additions and 2 deletions
|
|
@ -34,6 +34,7 @@ pub enum Page {
|
|||
AdminUsersNew(AdminUsersNew),
|
||||
AdminRolesList(AdminRolesList),
|
||||
AdminRolesNew(AdminRolesNew),
|
||||
AdminRolesView(AdminRolesView),
|
||||
}
|
||||
|
||||
impl Page {
|
||||
|
|
@ -64,6 +65,7 @@ impl Page {
|
|||
Page::AdminUsersNew(_) => "pages/admin/users/new",
|
||||
Page::AdminRolesList(_) => "pages/admin/roles/list",
|
||||
Page::AdminRolesNew(_) => "pages/admin/roles/new",
|
||||
Page::AdminRolesView(_) => "pages/admin/roles/view",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -94,6 +96,7 @@ impl Page {
|
|||
Page::AdminUsersNew(_) => "New user",
|
||||
Page::AdminRolesList(_) => "Roles",
|
||||
Page::AdminRolesNew(_) => "New role",
|
||||
Page::AdminRolesView(_) => "Role info",
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -126,6 +129,7 @@ impl Page {
|
|||
Page::AdminUsersNew(_) => Some(AdminMenu::Users.into()),
|
||||
Page::AdminRolesList(_) => Some(AdminMenu::Roles.into()),
|
||||
Page::AdminRolesNew(_) => Some(AdminMenu::Roles.into()),
|
||||
Page::AdminRolesView(_) => Some(AdminMenu::Roles.into()),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -156,6 +160,7 @@ impl Page {
|
|||
Page::AdminUsersNew(new) => Box::new(new),
|
||||
Page::AdminRolesList(list) => Box::new(list),
|
||||
Page::AdminRolesNew(new) => Box::new(new),
|
||||
Page::AdminRolesView(view) => Box::new(view),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,6 +40,8 @@ pub fn routes() -> Vec<Route> {
|
|||
admin_roles_list,
|
||||
admin_roles_new,
|
||||
admin_roles_new_form,
|
||||
admin_roles_view,
|
||||
admin_roles_archive,
|
||||
]
|
||||
}
|
||||
|
||||
|
|
@ -143,4 +145,13 @@ pub mod content {
|
|||
pub struct AdminRolesNew {
|
||||
pub user: JwtClaims,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
#[serde(crate = "rocket::serde")]
|
||||
#[derive(Clone)]
|
||||
pub struct AdminRolesView {
|
||||
pub user: JwtClaims,
|
||||
pub role: Role,
|
||||
pub jwt_duration: i64,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
use crate::routes::prelude::*;
|
||||
use crate::tokens::JWT_DURATION_MINUTES;
|
||||
use rocket::{get, post};
|
||||
use roles::Role;
|
||||
use std::str::FromStr;
|
||||
|
|
@ -71,9 +72,107 @@ pub async fn admin_roles_new_form(
|
|||
|
||||
transaction.commit().await?;
|
||||
|
||||
let id = RocketRoleID(name);
|
||||
Ok(Flash::new(
|
||||
Redirect::to(uri!(admin_roles_list)),
|
||||
Redirect::to(uri!(admin_roles_view(id))),
|
||||
FlashKind::Success,
|
||||
"Role has been created.",
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/admin/roles/<id>")]
|
||||
pub async fn admin_roles_view(
|
||||
admin: JwtAdmin,
|
||||
mut db: Connection<Database>,
|
||||
id: RocketRoleID,
|
||||
flash: Option<FlashMessage<'_>>,
|
||||
) -> Result<Template> {
|
||||
let role_id = id.0;
|
||||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
let role = Role::get_by_name(&mut transaction, &role_id)
|
||||
.await?
|
||||
.ok_or_else(|| Error::not_found(role_id.to_string()))?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
let page = Page::AdminRolesView(super::content::AdminRolesView {
|
||||
user: admin.0,
|
||||
role,
|
||||
jwt_duration: JWT_DURATION_MINUTES,
|
||||
});
|
||||
|
||||
Ok(flash
|
||||
.map(|flash| Page::with_flash(page.clone(), flash))
|
||||
.unwrap_or_else(|| page.into()))
|
||||
}
|
||||
|
||||
#[derive(Debug, FromForm)]
|
||||
pub struct ArchiveRoleForm {
|
||||
pub archive: Option<bool>,
|
||||
pub restore: Option<bool>,
|
||||
}
|
||||
|
||||
#[post("/admin/roles/<id>/archive", data = "<form>")]
|
||||
pub async fn admin_roles_archive(
|
||||
_admin: JwtAdmin,
|
||||
mut db: Connection<Database>,
|
||||
id: RocketRoleID,
|
||||
form: Form<ArchiveRoleForm>,
|
||||
) -> Result<Flash<Redirect>> {
|
||||
let (redirect, flash_kind, flash_message) = match (form.archive, form.restore) {
|
||||
(Some(true), _) => {
|
||||
// Archive role
|
||||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
// Get role
|
||||
let role = Role::get_by_name(&mut transaction, &id.0)
|
||||
.await?
|
||||
.ok_or_else(|| Error::not_found("Could not find role"))?;
|
||||
|
||||
// Set new status
|
||||
role.set_archive_status(&mut transaction, true).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
(
|
||||
Redirect::to(uri!(admin_roles_list)),
|
||||
FlashKind::Success,
|
||||
"Role has been archived.",
|
||||
)
|
||||
}
|
||||
(_, Some(true)) => {
|
||||
// Restore role
|
||||
|
||||
let mut transaction = db.begin().await?;
|
||||
|
||||
// Get role
|
||||
let role = Role::get_by_name(&mut transaction, &id.0)
|
||||
.await?
|
||||
.ok_or_else(|| Error::not_found("Could not find role"))?;
|
||||
|
||||
// Set new status
|
||||
role.set_archive_status(&mut transaction, false).await?;
|
||||
|
||||
transaction.commit().await?;
|
||||
|
||||
(
|
||||
Redirect::to(uri!(admin_roles_view(id))),
|
||||
FlashKind::Success,
|
||||
"Role has been restored.",
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
// Nothing to do
|
||||
(
|
||||
Redirect::to(uri!(admin_roles_view(id))),
|
||||
FlashKind::Warning,
|
||||
"Nothing to do.",
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
Ok(Flash::new(redirect, flash_kind, flash_message))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
class="form-control"
|
||||
required>
|
||||
<small class="form-hint">
|
||||
This name identifies the role inside the system. It is not public.<br>
|
||||
This name is for internal usage. It is not public and cannot be changed.<br>
|
||||
Allowed characters: <code>[a-z][0-9]-</code>
|
||||
</small>
|
||||
</div>
|
||||
|
|
|
|||
145
crates/ezidam/templates/pages/admin/roles/view.html.tera
Normal file
145
crates/ezidam/templates/pages/admin/roles/view.html.tera
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
{% extends "shell" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="page-header d-print-none">
|
||||
<div class="container-xl">
|
||||
<div class="row align-items-center">
|
||||
<div class="col">
|
||||
<div class="page-pretitle">
|
||||
Admin dashboard
|
||||
</div>
|
||||
<h2 class="page-title">
|
||||
Roles
|
||||
</h2>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Page body -->
|
||||
<div class="page-body">
|
||||
<div class="container-xl">
|
||||
|
||||
{% if flash %}
|
||||
<div class="alert alert-{{flash.0}}" role="alert">
|
||||
<h4 class="alert-title">{{ flash.1 | safe }}</h4>
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
{% if role.is_archived %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Archived role</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<p>This role is archived.</p>
|
||||
|
||||
<form action="{{ role.name }}/archive" method="post">
|
||||
<button type="submit" name="restore" value="true"
|
||||
class="btn btn-primary">
|
||||
Restore role
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="card-title">Role information</h3>
|
||||
</div>
|
||||
<form action="{{ role.name }}/info" method="post">
|
||||
<div class="card-body">
|
||||
<!-- Name -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label" for="name">Name</label>
|
||||
<div>
|
||||
<input id="name" value="{{ role.name }}" type="text"
|
||||
class="form-control cursor-not-allowed" readonly>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Label -->
|
||||
<div class="mb-3">
|
||||
<label class="form-label required" for="label">Label</label>
|
||||
<div>
|
||||
<input name="label" value="{{ role.label }}" id="label" type="text"
|
||||
placeholder="Enter label"
|
||||
class="form-control"
|
||||
required>
|
||||
<small class="form-hint">
|
||||
The label is shown to the users.
|
||||
</small>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-footer text-end">
|
||||
<div class="d-flex">
|
||||
|
||||
<a class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#modal-archive">
|
||||
Archive
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary ms-auto">Save</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Archive modal -->
|
||||
<div class="modal modal-blur" tabindex="-1" id="modal-archive">
|
||||
<div class="modal-dialog modal-sm modal-dialog-centered" role="document">
|
||||
<div class="modal-content">
|
||||
|
||||
<div class="modal-status bg-danger"></div>
|
||||
|
||||
<div class="modal-body text-center py-4">
|
||||
|
||||
<div class="text-danger mb-2">
|
||||
{% include "icons/alert-triangle-large" %}
|
||||
</div>
|
||||
|
||||
<h3>Do you want to archive this role?</h3>
|
||||
<div class="mt-2">This role will not be able to be used by users.</div>
|
||||
<div class="mt-2">
|
||||
This action can take up to {{ jwt_duration }} minutes to be effective for all users.
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<div class="w-100">
|
||||
<div class="row">
|
||||
|
||||
<div class="col">
|
||||
<a href="#" class="btn w-100" data-bs-dismiss="modal">Cancel</a>
|
||||
</div>
|
||||
|
||||
<div class="col">
|
||||
<form action="{{ role.name }}/archive" method="post">
|
||||
<button type="submit" name="archive" value="true"
|
||||
class="btn btn-danger w-100">
|
||||
Archive role
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
{% block libs_js %}
|
||||
{% endblock lib_js %}
|
||||
|
||||
{% block additional_js %}
|
||||
{% endblock additional_js %}
|
||||
Loading…
Add table
Add a link
Reference in a new issue