Display aliases

This commit is contained in:
traxys 2023-08-29 21:29:48 +02:00
parent 5cd7f26717
commit 1785d25bc6
4 changed files with 138 additions and 3 deletions

1
Cargo.lock generated
View file

@ -1272,6 +1272,7 @@ dependencies = [
"color-eyre", "color-eyre",
"cookie", "cookie",
"envious", "envious",
"futures-util",
"jwt-simple", "jwt-simple",
"once_cell", "once_cell",
"openidconnect", "openidconnect",

View file

@ -11,6 +11,7 @@ base64 = "0.21.2"
color-eyre = "0.6.2" color-eyre = "0.6.2"
cookie = "0.17.0" cookie = "0.17.0"
envious = "0.2.2" envious = "0.2.2"
futures-util = "0.3.28"
jwt-simple = "0.11.6" jwt-simple = "0.11.6"
once_cell = "1.18.0" once_cell = "1.18.0"
openidconnect = "3.3.0" openidconnect = "3.3.0"

View file

@ -17,6 +17,7 @@ use axum_extra::extract::{cookie::Cookie, CookieJar};
use base64::{engine::general_purpose, engine::Engine}; use base64::{engine::general_purpose, engine::Engine};
use color_eyre::eyre; use color_eyre::eyre;
use cookie::{time::OffsetDateTime, SameSite}; use cookie::{time::OffsetDateTime, SameSite};
use futures_util::TryStreamExt;
use jwt_simple::prelude::*; use jwt_simple::prelude::*;
use once_cell::sync::Lazy; use once_cell::sync::Lazy;
use openidconnect::{ use openidconnect::{
@ -500,6 +501,17 @@ struct HomeQuery {
user_error: Option<UserError>, user_error: Option<UserError>,
} }
struct AliasPart {
mail: String,
recipient: String,
}
#[derive(Serialize)]
struct Alias {
mail: String,
recipients: Vec<String>,
}
async fn home( async fn home(
state: State<Arc<AppState>>, state: State<Arc<AppState>>,
User(user): User, User(user): User,
@ -513,9 +525,39 @@ async fn home(
.fetch_all(&state.db) .fetch_all(&state.db)
.await?; .await?;
let aliases = sqlx::query_as!(
Mail,
"SELECT mail FROM emails WHERE id = $1 AND alias = true",
user
)
.fetch_all(&state.db)
.await?;
let mut alias_stream = sqlx::query_as!(
AliasPart,
r#"
SELECT alias_recipient.mail,recipient
FROM emails,alias_recipient
WHERE id = $1 AND alias = true AND emails.mail = alias_recipient.mail
"#,
user
)
.fetch(&state.db);
let mut aliases: HashMap<_, _> = aliases.into_iter().map(|a| (a.mail, Vec::new())).collect();
while let Some(alias) = alias_stream.try_next().await? {
aliases.get_mut(&alias.mail).unwrap().push(alias.recipient);
}
let aliases: Vec<_> = aliases
.into_iter()
.map(|(mail, recipients)| Alias { mail, recipients })
.collect();
let mut context = tera::Context::new(); let mut context = tera::Context::new();
context.insert("mails", &mails); context.insert("mails", &mails);
context.insert("mail_domain", &state.mail_domain); context.insert("mail_domain", &state.mail_domain);
context.insert("aliases", &aliases);
if let Some(err) = query.user_error { if let Some(err) = query.user_error {
tracing::info!("User error: {err:?}"); tracing::info!("User error: {err:?}");
context.insert("user_error", &err.to_string()); context.insert("user_error", &err.to_string());

View file

@ -81,13 +81,104 @@
</div> </div>
</div> </div>
</div> </div>
<h2 class="title is-2 mt-2">Aliases</h2>
<ul class="list-group">
{% for alias in aliases %}
<li class="list-group-item d-flex flex-column">
<div class="d-flex justify-content-between align-items-center">
{{ alias.mail }}
<button type="button"
class="btn btn-danger"
data-bs-toggle="modal"
data-bs-target="#aliasDelete{{ loop.index }}">Delete</button>
<div class="modal fade"
tabindex="-1"
id="aliasDelete{{ loop.index }}"
aria-labelledby="aliasDeleteLabel{{ loop.index }}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="aliasDeleteLabel{{ loop.index }}">Delete alias '{{ alias.mail }}'</h1>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-footer">
<form action="/alias/delete" method="post">
<input type="hidden" name="mail" value="{{ alias.mail }}" />
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit" class="btn btn-danger">Delete</button>
</form>
</div>
</div>
</div>
</div>
</div>
<ul class="list-group mt-1">
{% set alias_idx = loop.index %}
{% for recpt in alias.recipients %}<li class="list-group-item">{{ recpt }}</li>{% endfor %}
</ul>
<button type="button"
class="btn btn-primary mt-1 w-25"
data-bs-toggle="modal"
data-bs-target="#aliasRecptAdd{{ loop.index }}">Add Recipient</button>
<div class="modal fade"
tabindex="-1"
id="aliasRecptAdd{{ loop.index }}"
aria-labelledby="aliasRecptAddLabel{{ loop.index }}">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<h1 class="modal-title fs-5" id="aliasRecptAddLabel{{ loop.index }}">Add new recipient</h1>
<button type="button"
class="btn-close"
data-bs-dismiss="modal"
aria-label="Close"></button>
</div>
<div class="modal-body">
<form action="/alias/recipient/add"
method="post"
id="aliasRecptAddForm{{ loop.index }}">
<div class="form-floating mb-3">
<input type="email"
class="form-control"
id="floatingAddAliasRecpt{{ loop.index }}"
placeholder="mail@example.com"
name="recipient">
<label for="floatingAddAliasRecpt">Email address</label>
</div>
</form>
</div>
<div class="modal-footer">
<input type="hidden" name="alias" value="{{ alias.mail }}" />
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancel</button>
<button type="submit"
class="btn btn-primary"
form="aliasRecptAddForm{{ loop.index }}">Add</button>
</div>
</div>
</div>
</div>
</li>
{% endfor %}
</ul>
</div> </div>
<script> <script>
const addModal = document.getElementById('mailAdd') const addModal = document.getElementById('mailAdd');
const addInput = document.getElementById('floatingAddMail') const addInput = document.getElementById('floatingAddMail');
addModal.addEventListener('shown.bs.modal', () => { addModal.addEventListener('shown.bs.modal', () => {
addInput.focus() addInput.focus()
}) });
{% for idx in range(start = 1, end=aliases | length + 1) %}
const addRecptModal{{ idx }} = document.getElementById('aliasRecptAdd{{ idx }}');
const addRecptInput{{ idx }} = document.getElementById('floatingAddAliasRecpt{{ idx }}');
addRecptModal{{ idx }}.addEventListener('shown.bs.modal', () => {
addRecptInput{{ idx }}.focus()
});
{% endfor %}
</script> </script>
{% endblock content %} {% endblock content %}