diff --git a/README.md b/README.md index 263ddb3..feb5413 100644 --- a/README.md +++ b/README.md @@ -77,7 +77,9 @@ of the notes even if it tried to. | `SIZE_LIMIT` | `1 KiB` | Max size for body. Accepted values according to [byte-unit](https://docs.rs/byte-unit/).
`512 MiB` is the maximum allowed.
The frontend will show that number including the ~35% encoding overhead. | | `MAX_VIEWS` | `100` | Maximal number of views. | | `MAX_EXPIRATION` | `360` | Maximal expiration in minutes. | -| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will be one view only. | +| `DEFAULT_EXPIRE` | `60` | Default expiration in minutes. Default value is used in a simple mode or when advanced mode with mode switch enabled (per default, see `DISABLE_MODE_SWITCH`) and views are limited. | +| `DEFAULT_VIEWS` | `0` | Default views. Default value is used in the advanced mode with expiration defined or when advanced mode is disabled (`ALLOW_ADVANCED` set to false). | +| `ALLOW_ADVANCED` | `true` | Allow custom configuration. If set to `false` all notes will have infinite views unless `DEFAULT_VIEWS` is set to non zerro vale and will expire after default expiration (set in `DEFAULT_EXPIRE`). | | `ALLOW_FILES` | `true` | Allow uploading files. If set to `false`, users will only be allowed to create text notes. | | `ID_LENGTH` | `32` | Set the size of the note `id` in bytes. By default this is `32` bytes. This is useful for reducing link size. _This setting does not affect encryption strength_. | | `VERBOSITY` | `warn` | Verbosity level for the backend. [Possible values](https://docs.rs/env_logger/latest/env_logger/#enabling-logging) are: `error`, `warn`, `info`, `debug`, `trace` | @@ -88,6 +90,8 @@ of the notes even if it tried to. | `THEME_NEW_NOTE_NOTICE` | `true` | Show the message about how notes are stored in the memory and may be evicted after creating a new note. Defaults to `true`. | | `IMPRINT_URL` | `""` | Custom url for an Imprint hosted somewhere else. Must be publicly reachable. Takes precedence above `IMPRINT_HTML`. | | `IMPRINT_HTML` | `""` | Alternative to `IMPRINT_URL`, this can be used to specify the HTML code to show on `/imprint`. Only `IMPRINT_HTML` or `IMPRINT_URL` should be specified, not both. | +| `DISABLE_MODE_SWITCH` | `false` | Disables mode switch in the advanced mode. This makes both views and expiration fields editable and allows to define both limits at the same time. | + ## Deployment > ℹ️ `https` is required otherwise browsers will not support the cryptographic functions. diff --git a/package.json b/package.json index de22f74..d293e95 100644 --- a/package.json +++ b/package.json @@ -17,5 +17,5 @@ "npm-run-all": "^4.1.5", "shelljs": "^0.8.5" }, - "packageManager": "pnpm@10.3.0" + "packageManager": "pnpm@10.10.0" } diff --git a/packages/backend/src/config.rs b/packages/backend/src/config.rs index 318b66f..68e1410 100644 --- a/packages/backend/src/config.rs +++ b/packages/backend/src/config.rs @@ -26,6 +26,14 @@ pub static ref MAX_EXPIRATION: u32 = std::env::var("MAX_EXPIRATION") .unwrap_or("360".to_string()) // 6 hours in minutes .parse() .unwrap(); +pub static ref DEFAULT_EXPIRE: u32 = std::env::var("DEFAULT_EXPIRE") + .unwrap_or("60".to_string()) + .parse() + .unwrap(); +pub static ref DEFAULT_VIEWS: u32 = std::env::var("DEFAULT_VIEWS") + .unwrap_or("0".to_string()) + .parse() + .unwrap(); pub static ref ALLOW_ADVANCED: bool = std::env::var("ALLOW_ADVANCED") .unwrap_or("true".to_string()) .parse() @@ -46,7 +54,11 @@ pub static ref IMPRINT_HTML: String = std::env::var("IMPRINT_HTML") .unwrap_or("".to_string()) .parse() .unwrap(); -} +pub static ref DISABLE_MODE_SWITCH: bool = std::env::var("DISABLE_MODE_SWITCH") + .unwrap_or("false".to_string()) + .parse() + .unwrap(); +} // THEME lazy_static! { diff --git a/packages/backend/src/note/routes.rs b/packages/backend/src/note/routes.rs index bff47ef..e4519a5 100644 --- a/packages/backend/src/note/routes.rs +++ b/packages/backend/src/note/routes.rs @@ -53,15 +53,18 @@ pub async fn create(Json(mut n): Json) -> Response { .into_response(); } if !*config::ALLOW_ADVANCED { - n.views = Some(1); - n.expiration = None; + // Default value for views is 0 which means unlimited. + // This works together with the ALLOW_ADVANCED default value wich is true. + // If ALLOW_ADVANCED is false, we need to set the default value for views + // to value 1 to keep the old behavior. + n.views = Some(*config::DEFAULT_VIEWS); + n.expiration = Some(*config::DEFAULT_EXPIRE); } match n.views { Some(v) => { if v > *config::MAX_VIEWS || v < 1 { return (StatusCode::BAD_REQUEST, "Invalid views").into_response(); } - n.expiration = None; // views overrides expiration } _ => {} } @@ -75,6 +78,18 @@ pub async fn create(Json(mut n): Json) -> Response { } _ => {} } + + // Set default views + if n.views == None && *config::DEFAULT_VIEWS > 0 { + n.views = Some(*config::DEFAULT_VIEWS); + } + + // Set default expiration + if n.expiration == None { + let expiration = now() + (*config::DEFAULT_EXPIRE * 60); + n.expiration = Some(expiration); // Use default expiration + } + match store::set(&id.clone(), &n.clone()) { Ok(_) => (StatusCode::OK, Json(CreateResponse { id })).into_response(), Err(e) => (StatusCode::INTERNAL_SERVER_ERROR, e.to_string()).into_response(), diff --git a/packages/backend/src/status/mod.rs b/packages/backend/src/status/mod.rs index fcec5d2..6bffd49 100644 --- a/packages/backend/src/status/mod.rs +++ b/packages/backend/src/status/mod.rs @@ -10,10 +10,12 @@ pub struct Status { pub max_size: u32, pub max_views: u32, pub max_expiration: u32, + pub default_expire: u32, pub allow_advanced: bool, pub allow_files: bool, pub imprint_url: String, pub imprint_html: String, + pub disable_mode_switch: bool, // Theme pub theme_image: String, pub theme_text: String, @@ -28,10 +30,12 @@ pub async fn get_status() -> (StatusCode, Json) { max_size: *config::LIMIT as u32, max_views: *config::MAX_VIEWS, max_expiration: *config::MAX_EXPIRATION, + default_expire: *config::DEFAULT_EXPIRE, allow_advanced: *config::ALLOW_ADVANCED, allow_files: *config::ALLOW_FILES, imprint_url: config::IMPRINT_URL.to_string(), imprint_html: config::IMPRINT_HTML.to_string(), + disable_mode_switch: *config::DISABLE_MODE_SWITCH, theme_new_note_notice: *config::THEME_NEW_NOTE_NOTICE, theme_image: config::THEME_IMAGE.to_string(), theme_text: config::THEME_TEXT.to_string(), diff --git a/packages/backend/src/store.rs b/packages/backend/src/store.rs index bebf2d2..c80b958 100644 --- a/packages/backend/src/store.rs +++ b/packages/backend/src/store.rs @@ -39,7 +39,11 @@ pub fn set(id: &String, note: &Note) -> Result<(), &'static str> { conn.expire(id, seconds as i64) .map_err(|_| "Unable to set expiration on notion")? } - None => {} + None => { + let seconds = 60 * 60; // Hardcoded 1 hour + conn.expire(id, seconds as i64) + .map_err(|_| "Unable to set expiration on notion")? + } }; Ok(()) } diff --git a/packages/cli/src/shared/api.ts b/packages/cli/src/shared/api.ts index 6f8f010..d9029e0 100644 --- a/packages/cli/src/shared/api.ts +++ b/packages/cli/src/shared/api.ts @@ -111,10 +111,12 @@ export type Status = { max_size: number max_views: number max_expiration: number + default_expiration: number allow_advanced: boolean allow_files: boolean imprint_url: string imprint_html: string + disable_mode_switch: boolean theme_image: string theme_text: string theme_favicon: string diff --git a/packages/frontend/src/lib/ui/AdvancedParameters.svelte b/packages/frontend/src/lib/ui/AdvancedParameters.svelte index ba755d8..cd52871 100644 --- a/packages/frontend/src/lib/ui/AdvancedParameters.svelte +++ b/packages/frontend/src/lib/ui/AdvancedParameters.svelte @@ -23,6 +23,8 @@ $effect(() => { if (!hasCustomPassword) customPassword = null }) + + let disableModSwitch = $status && $status?.disable_mode_switch
@@ -32,28 +34,31 @@ type="number" label={$t('common.views', { values: { n: 0 } })} bind:value={note.views} - disabled={timeExpiration} + disabled={timeExpiration && !disableModSwitch} max={$status?.max_views} min={1} validate={(v) => ($status && v <= $status?.max_views && v > 0) || $t('home.errors.max', { values: { n: $status?.max_views ?? 0 } })} /> + {#if !disableModSwitch} + {/if} - ($status && v < $status?.max_expiration) || + // Use <= insteaad of < to avoid error message when value is equals to max. + ($status && v <= $status?.max_expiration) || $t('home.errors.max', { values: { n: $status?.max_expiration ?? 0 } })} />
diff --git a/packages/frontend/src/lib/views/Create.svelte b/packages/frontend/src/lib/views/Create.svelte index a79aad6..e87c12a 100644 --- a/packages/frontend/src/lib/views/Create.svelte +++ b/packages/frontend/src/lib/views/Create.svelte @@ -66,6 +66,7 @@ const derived = customPassword && (await AES.derive(customPassword)) const key = derived ? derived[0] : await AES.generateKey() + const disableModSwitch = $status?.disable_mode_switch const data: Note = { contents: '', @@ -79,8 +80,16 @@ if (note.contents === '') throw new EmptyContentError() data.contents = await Adapters.Text.encrypt(note.contents, key) } - if (timeExpiration) data.expiration = parseInt(note.expiration as any) - else data.views = parseInt(note.views as any) + if (disableModSwitch && advanced) { + data.views = parseInt(note.views as any) + data.expiration = parseInt(note.expiration as any) + } else { + if (timeExpiration) { + data.expiration = parseInt(note.expiration as any) + } else { + data.views = parseInt(note.views as any) + } + } loading = $t('common.uploading') const response = await API.create(data)