fies
This commit is contained in:
Generated
+1
-1
@@ -2581,7 +2581,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rustpad"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
dependencies = [
|
||||
"argon2",
|
||||
"aws-config",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rustpad"
|
||||
version = "0.1.0"
|
||||
version = "0.1.1"
|
||||
edition = "2024"
|
||||
rust-version = "1.94"
|
||||
description = "Collaborative Markdown notepad built with Axum, WebSockets and SQLite, PostgreSQL and MySQL"
|
||||
|
||||
+25
-15
@@ -1,7 +1,8 @@
|
||||
use axum::{
|
||||
Router,
|
||||
extract::{DefaultBodyLimit, Path, State},
|
||||
extract::{DefaultBodyLimit, Path, Request, State},
|
||||
http::{HeaderName, HeaderValue, StatusCode, header},
|
||||
middleware::{self, Next},
|
||||
response::{Html, IntoResponse, Response},
|
||||
routing::{get, post},
|
||||
};
|
||||
@@ -170,24 +171,10 @@ pub fn router(
|
||||
.layer(DefaultBodyLimit::max(
|
||||
upload_max_size_bytes.saturating_add(1024 * 1024),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
HeaderName::from_static("x-content-type-options"),
|
||||
HeaderValue::from_static("nosniff"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
HeaderName::from_static("x-frame-options"),
|
||||
HeaderValue::from_static("DENY"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
HeaderName::from_static("referrer-policy"),
|
||||
HeaderValue::from_static("strict-origin-when-cross-origin"),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
HeaderName::from_static("permissions-policy"),
|
||||
HeaderValue::from_static(
|
||||
"camera=(), microphone=(), geolocation=(), payment=(), usb=()",
|
||||
),
|
||||
))
|
||||
.layer(SetResponseHeaderLayer::if_not_present(
|
||||
HeaderName::from_static("cross-origin-opener-policy"),
|
||||
HeaderValue::from_static("same-origin"),
|
||||
@@ -197,9 +184,32 @@ pub fn router(
|
||||
HeaderValue::from_static("same-origin"),
|
||||
))
|
||||
.layer(TraceLayer::new_for_http())
|
||||
.layer(middleware::from_fn(add_non_asset_security_headers))
|
||||
.with_state(state)
|
||||
}
|
||||
|
||||
async fn add_non_asset_security_headers(request: Request, next: Next) -> Response {
|
||||
let is_asset = request.uri().path().starts_with("/assets/");
|
||||
let mut response = next.run(request).await;
|
||||
|
||||
if !is_asset {
|
||||
let headers = response.headers_mut();
|
||||
headers
|
||||
.entry("x-content-type-options")
|
||||
.or_insert(HeaderValue::from_static("nosniff"));
|
||||
headers
|
||||
.entry("referrer-policy")
|
||||
.or_insert(HeaderValue::from_static("strict-origin-when-cross-origin"));
|
||||
headers
|
||||
.entry("permissions-policy")
|
||||
.or_insert(HeaderValue::from_static(
|
||||
"camera=(), microphone=(), geolocation=(), payment=(), usb=()",
|
||||
));
|
||||
}
|
||||
|
||||
response
|
||||
}
|
||||
|
||||
async fn private_workspace_error(State(state): State<SharedState>) -> Response {
|
||||
error_response(
|
||||
StatusCode::FORBIDDEN,
|
||||
|
||||
@@ -133,6 +133,7 @@
|
||||
<label data-directory-profile-field>Organization<input id="profile-directory-organization" readonly></label>
|
||||
<p id="profile-nickname-suggestion" data-directory-profile-field class="profile-suggestion"></p>
|
||||
<label>Nickname<input id="profile-nickname" maxlength="40" required></label>
|
||||
<label data-local-profile-field>Current e-mail<input id="profile-current-email" type="email" readonly></label>
|
||||
<label data-local-profile-field>New e-mail<input id="profile-email" type="email" maxlength="320" placeholder="Leave empty to keep current"></label>
|
||||
<label data-local-profile-field>New password<input id="profile-new-password" type="password" minlength="8" maxlength="128" placeholder="Leave empty to keep current"></label>
|
||||
<label data-local-profile-field>Current password<input id="profile-password" type="password" minlength="8" maxlength="128" required></label>
|
||||
@@ -142,6 +143,7 @@
|
||||
<p id="profile-message" class="form-message" role="status"></p>
|
||||
</form>
|
||||
</dialog>
|
||||
<div id="toast" class="toast" role="status" aria-live="polite"></div>
|
||||
|
||||
</body>
|
||||
|
||||
|
||||
+11
-1
@@ -84,7 +84,16 @@ document.querySelector("#workspace-form").addEventListener("submit", async (even
|
||||
|
||||
handleAccountConfirmationToken();
|
||||
handleResetToken();
|
||||
{ const params=new URLSearchParams(location.search); const token=params.get("account_action_token"); if(token){ api("/api/auth/account-action/confirm",{method:"POST",body:JSON.stringify({token})}).then(r=>alert(r.message)).catch(e=>alert(e.message)).finally(()=>{params.delete("account_action_token");history.replaceState({},"",`${location.pathname}${params.size?`?${params}`:""}${location.hash}`);}); } }
|
||||
function toast(text) {
|
||||
const element = document.querySelector("#toast");
|
||||
if (!element) return;
|
||||
element.textContent = text;
|
||||
element.classList.add("visible");
|
||||
clearTimeout(toast.timer);
|
||||
toast.timer = setTimeout(() => element.classList.remove("visible"), 3000);
|
||||
}
|
||||
|
||||
{ const params=new URLSearchParams(location.search); const token=params.get("account_action_token"); if(token){ api("/api/auth/account-action/confirm",{method:"POST",body:JSON.stringify({token})}).then(r=>toast(r.message)).catch(e=>toast(e.message)).finally(()=>{params.delete("account_action_token");history.replaceState({},"",`${location.pathname}${params.size?`?${params}`:""}${location.hash}`);}); } }
|
||||
|
||||
const identityDialog = document.querySelector("#identity-dialog");
|
||||
const guestAccount = document.querySelector("#footer-account-guest");
|
||||
@@ -260,6 +269,7 @@ if (identityDialog) {
|
||||
|
||||
document.querySelector("#footer-profile")?.addEventListener("click", () => {
|
||||
document.querySelector("#profile-nickname").value = currentSession?.nickname || "";
|
||||
document.querySelector("#profile-current-email").value = currentSession?.email || "";
|
||||
document.querySelector("#profile-email").value = "";
|
||||
document.querySelector("#profile-new-password").value = "";
|
||||
document.querySelector("#profile-password").value = "";
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
|
||||
<body class="pad-page" data-registration-enabled="__REGISTRATION_ENABLED__">
|
||||
<header class="app-header">
|
||||
<div class="app-header__main"><a class="brand" href="/">RustPad</a><span class="header-divider"></span>
|
||||
<div class="app-header__main"><a class="brand home-brand" href="/">RustPad</a><span class="header-divider"></span>
|
||||
<div class="document-heading">
|
||||
<h1 id="pad-title">__PAD_TITLE__</h1>
|
||||
<p id="pad-url" class="document-url"></p>
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@
|
||||
|
||||
<body class="public-page hide-preview-line-numbers">
|
||||
<header class="public-header">
|
||||
<a class="brand" href="/">RustPad</a>
|
||||
<a class="brand home-brand" href="/">RustPad</a>
|
||||
<div class="public-header__actions">
|
||||
<label class="line-toggle"><input id="public-line-numbers-toggle" type="checkbox"> Line numbers</label>
|
||||
<button id="copy-public-link" class="secondary-button">Copy link</button>
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
|
||||
<body data-registration-enabled="__REGISTRATION_ENABLED__">
|
||||
<header class="app-header">
|
||||
<div class="app-header__main"><a class="brand" href="/">RustPad</a><span class="header-divider"></span>
|
||||
<div class="app-header__main"><a class="brand home-brand" href="/">RustPad</a><span class="header-divider"></span>
|
||||
<div class="document-heading">
|
||||
<h1 id="workspace-title">__WORKSPACE_TITLE__</h1>
|
||||
<p id="workspace-url" class="document-url"></p>
|
||||
|
||||
Reference in New Issue
Block a user