feat: Basic domain admin page
This commit is contained in:
parent
38700ee4cb
commit
ae5d466e99
5 changed files with 92 additions and 2 deletions
|
|
@ -54,7 +54,10 @@ pub trait DatabaseInterface: Clone + Send + Sync + 'static {
|
||||||
&mut self,
|
&mut self,
|
||||||
domain: &str,
|
domain: &str,
|
||||||
) -> impl Future<Output = Result<Result<(), DomainCreationError>, BoxedError>>;
|
) -> impl Future<Output = Result<Result<(), DomainCreationError>, BoxedError>>;
|
||||||
fn get_domain(&self, domain: &str) -> impl Future<Output = Result<Option<Domain>, BoxedError>>;
|
fn get_domain(
|
||||||
|
&self,
|
||||||
|
domain: &str,
|
||||||
|
) -> impl Future<Output = Result<Option<Domain>, BoxedError>> + Send;
|
||||||
|
|
||||||
fn get_user(
|
fn get_user(
|
||||||
&self,
|
&self,
|
||||||
|
|
|
||||||
54
src/http/domain.rs
Normal file
54
src/http/domain.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
||||||
|
use axum::extract::{Path, State};
|
||||||
|
use axum::response::{Html, IntoResponse, Response};
|
||||||
|
use axum_extra::extract::cookie::CookieJar;
|
||||||
|
use http::StatusCode;
|
||||||
|
use minijinja::context;
|
||||||
|
|
||||||
|
use crate::db::{DatabaseInterface, Operation};
|
||||||
|
use crate::http::HttpState;
|
||||||
|
use crate::http::login::login_page;
|
||||||
|
|
||||||
|
pub async fn get_domain<D: DatabaseInterface>(
|
||||||
|
State(state): State<HttpState<D>>,
|
||||||
|
cookies: CookieJar,
|
||||||
|
Path(domain): Path<String>,
|
||||||
|
) -> Response {
|
||||||
|
let Some(session) = state.sessions.get_session(&cookies) else {
|
||||||
|
return login_page(State(state), None).await.into_response();
|
||||||
|
};
|
||||||
|
|
||||||
|
let domain = match state.db.get_domain(&domain).await {
|
||||||
|
Ok(Some(domain)) => domain,
|
||||||
|
Ok(None) => return format!("Domain not found: {domain}").into_response(),
|
||||||
|
Err(e) => {
|
||||||
|
return format!("Database error: {e}").into_response();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let domain_users = match state.db.list_domain_users(Some(domain.name.clone())).await {
|
||||||
|
Ok(users) => users,
|
||||||
|
Err(e) => {
|
||||||
|
return format!("Database error: {e}").into_response();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Redundant because someone who can see the domain admin page for the moment
|
||||||
|
// always can create accounts.
|
||||||
|
let op = Operation::CreateUser(domain.name.clone());
|
||||||
|
let can_create_user = session.user.can_perform(&op);
|
||||||
|
|
||||||
|
let ctx = context! {
|
||||||
|
can_create_user,
|
||||||
|
domain,
|
||||||
|
user => session.user,
|
||||||
|
users => domain_users,
|
||||||
|
};
|
||||||
|
|
||||||
|
let page = state
|
||||||
|
.templates
|
||||||
|
.get_template("domain.html")
|
||||||
|
.unwrap()
|
||||||
|
.render(ctx)
|
||||||
|
.unwrap();
|
||||||
|
(StatusCode::OK, Html(page)).into_response()
|
||||||
|
}
|
||||||
|
|
@ -13,6 +13,7 @@ use crate::db::{Database, DatabaseInterface};
|
||||||
use crate::listener::{Listener, ListenerKind};
|
use crate::listener::{Listener, ListenerKind};
|
||||||
use crate::stream::{AbstractSocketAddr, AbstractStreamKind};
|
use crate::stream::{AbstractSocketAddr, AbstractStreamKind};
|
||||||
|
|
||||||
|
mod domain;
|
||||||
mod home;
|
mod home;
|
||||||
mod login;
|
mod login;
|
||||||
mod logout;
|
mod logout;
|
||||||
|
|
@ -99,6 +100,8 @@ pub async fn http_listen<D: DatabaseInterface>(listener: Listener, db: Database<
|
||||||
.route("/login", get(home::home))
|
.route("/login", get(home::home))
|
||||||
.route("/login", post(login::post_login))
|
.route("/login", post(login::post_login))
|
||||||
.route("/logout", get(logout::logout))
|
.route("/logout", get(logout::logout))
|
||||||
|
.route("/domain/{domain}", get(domain::get_domain))
|
||||||
|
//.route("/domain", post(domain::create_domain))
|
||||||
.with_state(HttpState::new(db));
|
.with_state(HttpState::new(db));
|
||||||
|
|
||||||
axum::serve(listener, app).await.unwrap();
|
axum::serve(listener, app).await.unwrap();
|
||||||
|
|
|
||||||
30
templates/domain.html
Normal file
30
templates/domain.html
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
{% extends 'base.html' %}
|
||||||
|
{% block main %}
|
||||||
|
<main id="center">
|
||||||
|
<img src="/assets/img/logo.png" id="logo">
|
||||||
|
<p style="text-align: center;">You are logged in as {{ user.username }}</p>
|
||||||
|
<div id="login-line">
|
||||||
|
<a href="/logout" id="submit-login" value="Logout">Logout</a>
|
||||||
|
</div>
|
||||||
|
{% if can_create_user %}
|
||||||
|
<div>
|
||||||
|
<h2>Create user</h2>
|
||||||
|
<form action="/user" method="POST">
|
||||||
|
<input type="hidden" name="domainname" value="{{ domain.name }}">
|
||||||
|
<input type="text" name="username" placeholder="username">
|
||||||
|
<input type="password" name="password" placeholder="password">
|
||||||
|
<button type="submit" class="button is-info">Create</button>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
<div>
|
||||||
|
<h2>Users on {{ domain.name }}</h2>
|
||||||
|
<ul>
|
||||||
|
{% for user in users %}
|
||||||
|
<li>{{ user.mail }}</li>
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
@ -9,7 +9,7 @@
|
||||||
{% if can_create_domain %}
|
{% if can_create_domain %}
|
||||||
<div>
|
<div>
|
||||||
<h2>Create domain</h2>
|
<h2>Create domain</h2>
|
||||||
<form action="/domains" method="POST">
|
<form action="/domain" method="POST">
|
||||||
<input type="text" name="domainname" placeholder="example.com">
|
<input type="text" name="domainname" placeholder="example.com">
|
||||||
<button type="submit" class="button is-info">Create</button>
|
<button type="submit" class="button is-info">Create</button>
|
||||||
</form>
|
</form>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue