add: safety_content_warning, master_pass env var
This commit is contained in:
parent
a6a289c594
commit
15b417c6d6
9 changed files with 150 additions and 29 deletions
46
Cargo.lock
generated
46
Cargo.lock
generated
|
@ -571,7 +571,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
checksum = "778e2ac28f6c47af28e4907f13ffd1e1ddbd400980a9abd7c8df189bf578a5ad"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -623,7 +623,7 @@ dependencies = [
|
||||||
"tetratto-core",
|
"tetratto-core",
|
||||||
"tetratto-shared",
|
"tetratto-shared",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.9.2",
|
"toml 0.9.4",
|
||||||
"tower-http",
|
"tower-http",
|
||||||
"tracing",
|
"tracing",
|
||||||
"tracing-subscriber",
|
"tracing-subscriber",
|
||||||
|
@ -997,7 +997,7 @@ dependencies = [
|
||||||
"libc",
|
"libc",
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"system-configuration",
|
"system-configuration",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
|
@ -1951,7 +1951,7 @@ dependencies = [
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"ryu",
|
"ryu",
|
||||||
"sha1_smol",
|
"sha1_smol",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"url",
|
"url",
|
||||||
|
@ -2082,7 +2082,7 @@ dependencies = [
|
||||||
"errno",
|
"errno",
|
||||||
"libc",
|
"libc",
|
||||||
"linux-raw-sys",
|
"linux-raw-sys",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2205,9 +2205,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "serde_json"
|
name = "serde_json"
|
||||||
version = "1.0.141"
|
version = "1.0.142"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "30b9eff21ebe718216c6ec64e1d9ac57087aad11efc64e32002bce4a0d4c03d3"
|
checksum = "030fedb782600dcbd6f02d479bf0d817ac3bb40d644745b769d6a96bc3afc5a7"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"itoa",
|
"itoa",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
@ -2397,6 +2397,16 @@ dependencies = [
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "socket2"
|
||||||
|
version = "0.6.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "233504af464074f9d066d7b5416c5f9b894a5862a6506e306f7b816cdd6f1807"
|
||||||
|
dependencies = [
|
||||||
|
"libc",
|
||||||
|
"windows-sys 0.59.0",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "stable_deref_trait"
|
name = "stable_deref_trait"
|
||||||
version = "1.2.0"
|
version = "1.2.0"
|
||||||
|
@ -2513,7 +2523,7 @@ dependencies = [
|
||||||
"getrandom 0.3.3",
|
"getrandom 0.3.3",
|
||||||
"once_cell",
|
"once_cell",
|
||||||
"rustix",
|
"rustix",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2571,7 +2581,7 @@ dependencies = [
|
||||||
"tetratto-l10n",
|
"tetratto-l10n",
|
||||||
"tetratto-shared",
|
"tetratto-shared",
|
||||||
"tokio",
|
"tokio",
|
||||||
"toml 0.9.2",
|
"toml 0.9.4",
|
||||||
"totp-rs",
|
"totp-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -2583,7 +2593,7 @@ checksum = "d96f5e41633c757e3519efb47c9b85d00d14322c1961360e126d0ecc0ea79b86"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"pathbufd",
|
"pathbufd",
|
||||||
"serde",
|
"serde",
|
||||||
"toml 0.9.2",
|
"toml 0.9.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2711,9 +2721,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tokio"
|
name = "tokio"
|
||||||
version = "1.46.1"
|
version = "1.47.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "0cc3a2344dafbe23a245241fe8b09735b521110d30fcefbbd5feb1797ca35d17"
|
checksum = "89e49afdadebb872d3145a5638b59eb0691ea23e46ca484037cfab3b76b95038"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"backtrace",
|
"backtrace",
|
||||||
"bytes",
|
"bytes",
|
||||||
|
@ -2723,9 +2733,9 @@ dependencies = [
|
||||||
"parking_lot",
|
"parking_lot",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"slab",
|
"slab",
|
||||||
"socket2",
|
"socket2 0.6.0",
|
||||||
"tokio-macros",
|
"tokio-macros",
|
||||||
"windows-sys 0.52.0",
|
"windows-sys 0.59.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -2769,7 +2779,7 @@ dependencies = [
|
||||||
"postgres-protocol",
|
"postgres-protocol",
|
||||||
"postgres-types",
|
"postgres-types",
|
||||||
"rand 0.9.1",
|
"rand 0.9.1",
|
||||||
"socket2",
|
"socket2 0.5.10",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-util",
|
"tokio-util",
|
||||||
"whoami",
|
"whoami",
|
||||||
|
@ -2824,9 +2834,9 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "toml"
|
name = "toml"
|
||||||
version = "0.9.2"
|
version = "0.9.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ed0aee96c12fa71097902e0bb061a5e1ebd766a6636bb605ba401c45c1650eac"
|
checksum = "41ae868b5a0f67631c14589f7e250c1ea2c574ee5ba21c6c8dd4b1485705a5a1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"indexmap",
|
"indexmap",
|
||||||
"serde",
|
"serde",
|
||||||
|
@ -3385,7 +3395,7 @@ version = "0.1.9"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.52.0",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
|
|
@ -10,7 +10,7 @@ homepage = "https://fluffle.cc"
|
||||||
[dependencies]
|
[dependencies]
|
||||||
tetratto-core = "12.0.2"
|
tetratto-core = "12.0.2"
|
||||||
tetratto-shared = "12.0.6"
|
tetratto-shared = "12.0.6"
|
||||||
tokio = { version = "1.46.1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1.47.1", features = ["macros", "rt-multi-thread"] }
|
||||||
pathbufd = "0.1.4"
|
pathbufd = "0.1.4"
|
||||||
serde = { version = "1.0.219", features = ["derive"] }
|
serde = { version = "1.0.219", features = ["derive"] }
|
||||||
tera = "1.20.0"
|
tera = "1.20.0"
|
||||||
|
@ -27,7 +27,7 @@ axum-extra = { version = "0.10.1", features = ["cookie"] }
|
||||||
nanoneo = "0.2.0"
|
nanoneo = "0.2.0"
|
||||||
dotenv = "0.15.0"
|
dotenv = "0.15.0"
|
||||||
glob = "0.3.2"
|
glob = "0.3.2"
|
||||||
serde_json = "1.0.141"
|
serde_json = "1.0.142"
|
||||||
toml = "0.9.2"
|
toml = "0.9.4"
|
||||||
serde_valid = { version = "1.0.5", features = ["toml"] }
|
serde_valid = { version = "1.0.5", features = ["toml"] }
|
||||||
regex = "1.11.1"
|
regex = "1.11.1"
|
||||||
|
|
|
@ -38,6 +38,8 @@ Once you've built the binary, it'll be located at (from the root `fluffle/` dire
|
||||||
|
|
||||||
All templates are compiled with [nanoneo](https://trisua.com/t/nanoneo), so it's recommended that you familiarize yourself with that syntax.
|
All templates are compiled with [nanoneo](https://trisua.com/t/nanoneo), so it's recommended that you familiarize yourself with that syntax.
|
||||||
|
|
||||||
|
You can set a master password to be able to freely edit all entries by using the `MASTER_PASS` environment variable.
|
||||||
|
|
||||||
## Attribution
|
## Attribution
|
||||||
|
|
||||||
Fluffle is licensed under the AGPL-3.0 license. Tetratto is also licensed under the AGPL-3.0 license.
|
Fluffle is licensed under the AGPL-3.0 license. Tetratto is also licensed under the AGPL-3.0 license.
|
||||||
|
|
BIN
app/public/fluffle_bunny.webp
Normal file
BIN
app/public/fluffle_bunny.webp
Normal file
Binary file not shown.
After Width: | Height: | Size: 22 KiB |
|
@ -301,7 +301,7 @@ input {
|
||||||
border-left: solid 0px transparent;
|
border-left: solid 0px transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
input:focus {
|
input:not([type="checkbox"]):focus {
|
||||||
outline: solid 2px var(--color-primary);
|
outline: solid 2px var(--color-primary);
|
||||||
box-shadow: 0 0 0 4px oklch(87% 0.065 274.039 / 25%);
|
box-shadow: 0 0 0 4px oklch(87% 0.065 274.039 / 25%);
|
||||||
background: var(--color-super-raised);
|
background: var(--color-super-raised);
|
||||||
|
@ -316,6 +316,10 @@ input.surface {
|
||||||
background: var(--color-surface);
|
background: var(--color-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="checkbox"] {
|
||||||
|
height: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
/* typo */
|
/* typo */
|
||||||
p {
|
p {
|
||||||
margin-bottom: var(--pad-4);
|
margin-bottom: var(--pad-4);
|
||||||
|
|
|
@ -61,6 +61,14 @@
|
||||||
(button
|
(button
|
||||||
("class" "button camo fade no_fill")
|
("class" "button camo fade no_fill")
|
||||||
("title" "Toggle high-contrast")
|
("title" "Toggle high-contrast")
|
||||||
|
("id" "toggle_high_contrast_button")
|
||||||
("onclick" "toggle_metadata_css(event)")
|
("onclick" "toggle_metadata_css(event)")
|
||||||
(text "{{ icon \"contrast\" }}"))
|
(text "{{ icon \"contrast\" }}"))
|
||||||
|
|
||||||
|
(text "{% if \"EntryHighContrast\" in flags -%}")
|
||||||
|
(script
|
||||||
|
(text "setTimeout(() => {
|
||||||
|
toggle_metadata_css({ target: document.getElementById(\"toggle_high_contrast_button\") });
|
||||||
|
}, 150);"))
|
||||||
|
(text "{%- endif %}")
|
||||||
(text "{% endblock %}")
|
(text "{% endblock %}")
|
||||||
|
|
54
app/templates_src/warning.lisp
Normal file
54
app/templates_src/warning.lisp
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
(text "{% extends \"root.lisp\" %} {% block head %}")
|
||||||
|
(text "{% if not metadata.page_title -%}")
|
||||||
|
(title
|
||||||
|
(text "{{ entry.slug }}"))
|
||||||
|
(text "{%- endif %} {{ metadata_head|safe }}")
|
||||||
|
|
||||||
|
(text "{% if not metadata.share_title -%}")
|
||||||
|
(meta ("property" "og:title") ("content" "{{ entry.slug }}"))
|
||||||
|
(meta ("property" "twitter:title") ("content" "{{ entry.slug }}"))
|
||||||
|
(text "{%- endif %}")
|
||||||
|
|
||||||
|
(text "{% if metadata.page_icon|length == 0 -%}")
|
||||||
|
(link ("rel" "icon") ("href" "/public/favicon.svg"))
|
||||||
|
(text "{%- endif %}")
|
||||||
|
|
||||||
|
(text "{% endblock %} {% block body %}")
|
||||||
|
(div
|
||||||
|
("class" "card container flex flex-col gap-1")
|
||||||
|
("id" "content_rect")
|
||||||
|
(p ("class" "fade") (text "Content warning:"))
|
||||||
|
(div (text "{{ metadata.safety_content_warning|markdown|safe }}"))
|
||||||
|
(hr)
|
||||||
|
(div
|
||||||
|
("class" "flex flex-col gap-4")
|
||||||
|
(label
|
||||||
|
("class" "flex flex-row gap-2 items-center")
|
||||||
|
("for" "open_in_high_contrast")
|
||||||
|
(input
|
||||||
|
("type" "checkbox")
|
||||||
|
("id" "open_in_high_contrast")
|
||||||
|
("name" "open_in_high_contrast"))
|
||||||
|
(span (text "Open in high contrast")))
|
||||||
|
(div
|
||||||
|
("class" "flex gap-2")
|
||||||
|
(button
|
||||||
|
("class" "button surface green")
|
||||||
|
("onclick" "accept()")
|
||||||
|
(text "Continue"))
|
||||||
|
(button
|
||||||
|
("class" "button surface red")
|
||||||
|
("onclick" "window.history.back()")
|
||||||
|
(text "Cancel")))))
|
||||||
|
|
||||||
|
(script
|
||||||
|
(text "const QFLAGS = [\"AcceptWarning\"];
|
||||||
|
function accept() {
|
||||||
|
if (document.getElementById(\"open_in_high_contrast\").checked) {
|
||||||
|
QFLAGS.push(\"EntryHighContrast\");
|
||||||
|
}
|
||||||
|
|
||||||
|
document.cookie = `Atto-QFlags=\"${JSON.stringify(QFLAGS)}\"; path=/`;
|
||||||
|
window.location.reload();
|
||||||
|
}"))
|
||||||
|
(text "{% endblock %}")
|
19
src/model.rs
19
src/model.rs
|
@ -342,6 +342,7 @@ pub struct EntryMetadata {
|
||||||
pub content_font_weight: u32,
|
pub content_font_weight: u32,
|
||||||
/// The text size of elements (separated by space).
|
/// The text size of elements (separated by space).
|
||||||
#[serde(default, alias = "CONTENT_TEXT_SIZE")]
|
#[serde(default, alias = "CONTENT_TEXT_SIZE")]
|
||||||
|
#[validate(max_length = 128)]
|
||||||
pub content_text_size: String,
|
pub content_text_size: String,
|
||||||
/// The text size of elements by element tag.
|
/// The text size of elements by element tag.
|
||||||
///
|
///
|
||||||
|
@ -357,13 +358,19 @@ pub struct EntryMetadata {
|
||||||
pub content_text_align: TextAlignment,
|
pub content_text_align: TextAlignment,
|
||||||
/// The base text color.
|
/// The base text color.
|
||||||
#[serde(default, alias = "CONTENT_TEXT_COLOR")]
|
#[serde(default, alias = "CONTENT_TEXT_COLOR")]
|
||||||
|
#[validate(max_length = 128)]
|
||||||
pub content_text_color: String,
|
pub content_text_color: String,
|
||||||
/// The color of links.
|
/// The color of links.
|
||||||
#[serde(default, alias = "CONTENT_LINK_COLOR")]
|
#[serde(default, alias = "CONTENT_LINK_COLOR")]
|
||||||
|
#[validate(max_length = 128)]
|
||||||
pub content_link_color: String,
|
pub content_link_color: String,
|
||||||
/// If paragraph elements have a margin below them.
|
/// If paragraph elements have a margin below them.
|
||||||
#[serde(default, alias = "CONTENT_DISABLE_PARAGRAPH_MARGIN")]
|
#[serde(default, alias = "CONTENT_DISABLE_PARAGRAPH_MARGIN")]
|
||||||
pub content_disable_paragraph_margin: bool,
|
pub content_disable_paragraph_margin: bool,
|
||||||
|
/// The content warning shown before viewing this entry.
|
||||||
|
#[serde(default, alias = "SAFETY_CONTENT_WARNING")]
|
||||||
|
#[validate(max_length = 512)]
|
||||||
|
pub safety_content_warning: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
macro_rules! metadata_css {
|
macro_rules! metadata_css {
|
||||||
|
@ -684,3 +691,15 @@ impl EntryMetadata {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Flags that can be provided through the "Atto-QFlags" cookie to customize the
|
||||||
|
/// resulting page.
|
||||||
|
#[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
|
||||||
|
pub enum QuickFlag {
|
||||||
|
/// If the entry's warning is automatically skipped.
|
||||||
|
AcceptWarning,
|
||||||
|
/// If the entry is automatically set to high-contrast mode.
|
||||||
|
EntryHighContrast,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub type QuickFlags = Vec<QuickFlag>;
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::env::var;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
State,
|
State,
|
||||||
model::{Entry, EntryMetadata},
|
model::{Entry, EntryMetadata, QuickFlag, QuickFlags},
|
||||||
};
|
};
|
||||||
use axum::{
|
use axum::{
|
||||||
Extension, Json, Router,
|
Extension, Json, Router,
|
||||||
|
@ -126,11 +126,20 @@ pub struct ViewQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn view_request(
|
async fn view_request(
|
||||||
|
jar: CookieJar,
|
||||||
Extension(data): Extension<State>,
|
Extension(data): Extension<State>,
|
||||||
Path(mut slug): Path<String>,
|
Path(mut slug): Path<String>,
|
||||||
Query(props): Query<ViewQuery>,
|
Query(props): Query<ViewQuery>,
|
||||||
) -> impl IntoResponse {
|
) -> impl IntoResponse {
|
||||||
let (ref data, ref tera, ref build_code) = *data.read().await;
|
let (ref data, ref tera, ref build_code) = *data.read().await;
|
||||||
|
let qflags: QuickFlags = match jar.get("Atto-QFlags") {
|
||||||
|
Some(x) => match serde_json::from_str(&x.value_trimmed()) {
|
||||||
|
Ok(x) => x,
|
||||||
|
Err(_) => QuickFlags::default(),
|
||||||
|
},
|
||||||
|
None => QuickFlags::default(),
|
||||||
|
};
|
||||||
|
|
||||||
slug = slug.to_lowercase();
|
slug = slug.to_lowercase();
|
||||||
|
|
||||||
let entry = match data
|
let entry = match data
|
||||||
|
@ -222,8 +231,15 @@ async fn view_request(
|
||||||
ctx.insert("metadata_head", &metadata.head_tags());
|
ctx.insert("metadata_head", &metadata.head_tags());
|
||||||
ctx.insert("metadata_css", &metadata.css());
|
ctx.insert("metadata_css", &metadata.css());
|
||||||
ctx.insert("password", &props.key);
|
ctx.insert("password", &props.key);
|
||||||
|
ctx.insert("flags", &qflags);
|
||||||
|
|
||||||
Html(tera.render("view.lisp", &ctx).unwrap())
|
if metadata.safety_content_warning.is_empty() | qflags.contains(&QuickFlag::AcceptWarning) {
|
||||||
|
// regular view
|
||||||
|
Html(tera.render("view.lisp", &ctx).unwrap())
|
||||||
|
} else {
|
||||||
|
// warning
|
||||||
|
Html(tera.render("warning.lisp", &ctx).unwrap())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn editor_request(
|
async fn editor_request(
|
||||||
|
@ -614,12 +630,20 @@ async fn edit_request(
|
||||||
let using_modify_code = edit_code == entry.modify_code;
|
let using_modify_code = edit_code == entry.modify_code;
|
||||||
|
|
||||||
// check edit code
|
// check edit code
|
||||||
if edit_code
|
let mut using_master = false;
|
||||||
!= *if using_modify_code {
|
if let Ok(master_pass) = var("MASTER_PASS") {
|
||||||
&entry.modify_code
|
if req.edit_code == master_pass {
|
||||||
} else {
|
using_master = true;
|
||||||
&entry.edit_code
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !using_master
|
||||||
|
&& edit_code
|
||||||
|
!= *if using_modify_code {
|
||||||
|
&entry.modify_code
|
||||||
|
} else {
|
||||||
|
&entry.edit_code
|
||||||
|
}
|
||||||
{
|
{
|
||||||
return Json(Error::NotAllowed.into());
|
return Json(Error::NotAllowed.into());
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue