Skip to content

Commit

Permalink
v0.1.8
Browse files Browse the repository at this point in the history
  • Loading branch information
mdecimus committed Jul 7, 2024
1 parent 9f0b9ff commit fd39bb3
Show file tree
Hide file tree
Showing 16 changed files with 836 additions and 19 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,16 @@

All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).

## [0.1.8] - 2024-07-07

## Added
- Restore deleted emails (Enterprise edition only).
- Option to purge accounts.

### Changed

### Fixed

## [0.1.7] - 2024-07-01

## Added
Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ homepage = "https://stalw.art"
keywords = ["web", "admin", "email", "mail", "server"]
categories = ["email"]
license = "AGPL-3.0-only OR LicenseRef-SEL"
version = "0.1.7"
version = "0.1.8"
edition = "2021"
resolver = "2"

Expand Down
30 changes: 30 additions & 0 deletions src/components/icon.rs
Original file line number Diff line number Diff line change
Expand Up @@ -577,3 +577,33 @@ pub fn IconSquare2x2(
</SvgWrapper>
}
}

#[component]
pub fn IconArrowUTurnLeft(
#[prop(optional)] size: Option<usize>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
view! {
<SvgWrapper size attrs>
<path
stroke-linecap="round"
stroke-linejoin="round"
d="M9 15 3 9m0 0 6-6M3 9h12a6 6 0 0 1 0 12h-3"
></path>
</SvgWrapper>
}
}

#[component]
pub fn IconThreeDots(
#[prop(optional)] size: Option<usize>,
#[prop(attrs)] attrs: Vec<(&'static str, Attribute)>,
) -> impl IntoView {
view! {
<SvgWrapper size attrs>
<circle cx="12" cy="12" r="1"></circle>
<circle cx="19" cy="12" r="1"></circle>
<circle cx="5" cy="12" r="1"></circle>
</SvgWrapper>
}
}
15 changes: 14 additions & 1 deletion src/core/oauth.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ pub struct AuthToken {
pub username: Arc<String>,
pub is_valid: bool,
pub is_admin: bool,
pub is_enterprise: bool,
}

#[derive(Debug, Clone, Serialize, Deserialize)]
Expand All @@ -39,6 +40,8 @@ pub enum OAuthCodeRequest {
pub struct OAuthCodeResponse {
pub code: String,
pub is_admin: bool,
#[serde(default)]
pub is_enterprise: bool,
}

#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
Expand Down Expand Up @@ -90,6 +93,7 @@ pub enum AuthenticationResult<T> {
pub struct AuthenticationResponse {
pub grant: OAuthGrant,
pub is_admin: bool,
pub is_enterprise: bool,
}

pub async fn oauth_authenticate(
Expand All @@ -104,6 +108,7 @@ pub async fn oauth_authenticate(
AuthenticationResult::Error(err) => return AuthenticationResult::Error(err),
};
let is_admin = response.is_admin;
let is_enterprise = response.is_enterprise;
match HttpRequest::post(format!("{base_url}/auth/token"))
.with_raw_body(
serde_urlencoded::to_string([
Expand All @@ -120,7 +125,11 @@ pub async fn oauth_authenticate(
serde_json::from_slice::<OAuthResponse>(response.as_slice()).map_err(Into::into)
}) {
Ok(OAuthResponse::Granted(grant)) => {
AuthenticationResult::Success(AuthenticationResponse { grant, is_admin })
AuthenticationResult::Success(AuthenticationResponse {
grant,
is_admin,
is_enterprise,
})
}
Ok(OAuthResponse::Error { error }) => AuthenticationResult::Error(
Alert::error("OAuth failure")
Expand Down Expand Up @@ -223,6 +232,10 @@ impl AuthToken {
pub fn is_admin(&self) -> bool {
self.is_admin && self.is_logged_in()
}

pub fn is_enterprise(&self) -> bool {
self.is_enterprise
}
}

impl AsRef<AuthToken> for AuthToken {
Expand Down
6 changes: 6 additions & 0 deletions src/core/schema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ pub struct Field {
pub placeholder: Value<&'static str>,
pub display: Vec<Eval>,
pub readonly: bool,
pub enterprise: bool,
}

#[derive(Clone, Default, Debug)]
Expand Down Expand Up @@ -597,6 +598,11 @@ impl Builder<(Schemas, Schema), Field> {
self
}

pub fn enterprise_feature(mut self) -> Self {
self.item.enterprise = true;
self
}

pub fn typ(mut self, typ_: Type<&'static str, &'static str>) -> Self {
self.item.typ_ = match typ_ {
Type::Select {
Expand Down
8 changes: 8 additions & 0 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ use pages::{
mfa::ManageMfa,
},
config::edit::DEFAULT_SETTINGS_URL,
enterprise::undelete::UndeleteList,
manage::spam::{SpamTest, SpamTrain},
};

Expand Down Expand Up @@ -137,6 +138,7 @@ pub fn App() -> impl IntoView {

let is_logged_in = create_memo(move |_| auth_token.get().is_logged_in());
let is_admin = create_memo(move |_| auth_token.get().is_admin());
let is_enterprise = create_memo(move |_| auth_token.get().is_enterprise());

view! {
<Router>
Expand Down Expand Up @@ -241,6 +243,12 @@ pub fn App() -> impl IntoView {
redirect_path="/login"
condition=move || is_admin.get()
/>
<ProtectedRoute
path="/undelete/:id"
view=UndeleteList
redirect_path="/login"
condition=move || is_admin.get() && is_enterprise.get()
/>
</ProtectedRoute>
<ProtectedRoute
path="/settings"
Expand Down
5 changes: 2 additions & 3 deletions src/pages/account/app_password.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,8 @@ use serde::{Deserialize, Serialize};
use crate::{
components::{
form::{
button::Button,
input::InputText,
Form, FormButtonBar, FormElement, FormItem, FormSection,
button::Button, input::InputText, Form, FormButtonBar, FormElement, FormItem,
FormSection,
},
icon::{IconAdd, IconTrash},
list::{
Expand Down
9 changes: 7 additions & 2 deletions src/pages/config/edit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,7 @@ pub fn SettingsEdit() -> impl IntoView {
};
let schema = current_schema.get();
let sections = schema.form.sections.iter().cloned();
let is_enterprise = auth.get().is_enterprise();
data.set(
FormData::from_settings(schema.clone(), settings)
.with_external_sources(external_sources),
Expand All @@ -358,7 +359,8 @@ pub fn SettingsEdit() -> impl IntoView {
.iter()
.cloned()
.map(|field| {
let is_disabled = field.readonly && !is_create;
let is_disabled = (field.readonly && !is_create)
|| (!is_enterprise && field.enterprise);
let field_label = field.label_form;
let help = field.help;
let field_ = field.clone();
Expand Down Expand Up @@ -444,7 +446,10 @@ pub fn SettingsEdit() -> impl IntoView {
}
Type::Duration => {
view! {
<InputDuration element=FormElement::new(field.id, data)/>
<InputDuration
element=FormElement::new(field.id, data)
disabled=is_disabled
/>
}
.into_view()
}
Expand Down
12 changes: 11 additions & 1 deletion src/pages/config/schema/storage.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ impl Builder<Schemas, ()> {
.default("30d")
.typ(Type::Duration)
.build()
.new_field("enterprise.undelete-period")
.label("Un-delete period 💎")
.help(concat!(
"How long to keep deleted emails before they are permanently ",
"removed from the system. (Enterprise feature)"
))
.default("false")
.typ(Type::Duration)
.enterprise_feature()
.build()
.new_form_section()
.title("Data Store")
.fields([
Expand All @@ -163,7 +173,7 @@ impl Builder<Schemas, ()> {
.build()
.new_form_section()
.title("Blob Store")
.fields(["storage.blob"])
.fields(["storage.blob", "enterprise.undelete-period"])
.build()
.new_form_section()
.title("Full Text Index Store")
Expand Down
Loading

0 comments on commit fd39bb3

Please sign in to comment.