mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-01 17:50:50 +08:00
fix: test & lint
This commit is contained in:
@@ -29,7 +29,7 @@
|
||||
"test": "node --test ./__tests__/**/*.spec.js",
|
||||
"bench": "node ./benchmark/index.js",
|
||||
"build": "napi build --release --strip --no-const-enum",
|
||||
"build:debug": "napi build"
|
||||
"build:debug": "napi build --no-const-enum"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@napi-rs/cli": "3.5.0",
|
||||
|
||||
@@ -3,7 +3,7 @@ use std::{
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE};
|
||||
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
|
||||
use napi::Result;
|
||||
use serde::Deserialize;
|
||||
use sha2::{Digest, Sha256};
|
||||
@@ -41,8 +41,16 @@ fn blob_complete_success(
|
||||
}
|
||||
}
|
||||
|
||||
fn sha256_base64_url_with_padding(body: &[u8]) -> String {
|
||||
URL_SAFE.encode(Sha256::digest(body))
|
||||
fn normalize_base64_url_key(key: &str) -> &str {
|
||||
key.trim_end_matches('=')
|
||||
}
|
||||
|
||||
fn sha256_base64_url(body: &[u8]) -> String {
|
||||
URL_SAFE_NO_PAD.encode(Sha256::digest(body))
|
||||
}
|
||||
|
||||
fn sha256_base64_url_matches(body: &[u8], key: &str) -> bool {
|
||||
sha256_base64_url(body) == normalize_base64_url_key(key)
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -178,7 +186,7 @@ impl BackendRuntime {
|
||||
return Ok(blob_complete_failure("mime_mismatch"));
|
||||
}
|
||||
|
||||
if sha256_base64_url_with_padding(&object.body) != key {
|
||||
if !sha256_base64_url_matches(&object.body, &key) {
|
||||
match self.object_storage_delete(object_key).await {
|
||||
Ok(()) => {}
|
||||
Err(err) if object_missing_error(&err) => {}
|
||||
@@ -243,7 +251,7 @@ impl BackendRuntime {
|
||||
Err(err) => return Err(napi_error(format!("BlobComplete read fs object failed: {err}"))),
|
||||
};
|
||||
|
||||
if sha256_base64_url_with_padding(&body) != key {
|
||||
if !sha256_base64_url_matches(&body, &key) {
|
||||
let _ = fs::remove_file(&path);
|
||||
let _ = fs::remove_file(PathBuf::from(format!("{}.metadata.json", path.display())));
|
||||
return Ok(blob_complete_failure("checksum_mismatch"));
|
||||
@@ -268,13 +276,21 @@ impl BackendRuntime {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::sha256_base64_url_with_padding;
|
||||
use super::{sha256_base64_url, sha256_base64_url_matches};
|
||||
|
||||
#[test]
|
||||
fn sha256_base64_url_keeps_padding() {
|
||||
fn sha256_base64_url_omits_padding() {
|
||||
assert_eq!(
|
||||
sha256_base64_url_with_padding(b"hello"),
|
||||
"LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
|
||||
sha256_base64_url(b"hello"),
|
||||
"LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn sha256_base64_url_matches_legacy_padding() {
|
||||
assert!(sha256_base64_url_matches(
|
||||
b"hello",
|
||||
"LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -321,6 +321,38 @@ impl RuntimeStateRows {
|
||||
Ok(row.get::<i64, _>("expires_at_ms"))
|
||||
}
|
||||
|
||||
pub(super) async fn upsert_expired_or_consumed_payload_returning_expires_in_tx(
|
||||
&self,
|
||||
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
input: RuntimeStateInsertPayload<'_>,
|
||||
) -> Result<Option<i64>> {
|
||||
let row = sqlx::query(
|
||||
r#"
|
||||
INSERT INTO runtime_states (purpose, token_hash, lookup_key, payload, expires_at)
|
||||
VALUES ($1, $2, $3, $4, CURRENT_TIMESTAMP + ($5 * INTERVAL '1 millisecond'))
|
||||
ON CONFLICT (purpose, token_hash) DO UPDATE
|
||||
SET lookup_key = EXCLUDED.lookup_key,
|
||||
payload = EXCLUDED.payload,
|
||||
attempts = 0,
|
||||
consumed_at = NULL,
|
||||
expires_at = EXCLUDED.expires_at
|
||||
WHERE runtime_states.consumed_at IS NOT NULL
|
||||
OR runtime_states.expires_at <= CURRENT_TIMESTAMP
|
||||
RETURNING (EXTRACT(EPOCH FROM expires_at) * 1000)::BIGINT AS expires_at_ms
|
||||
"#,
|
||||
)
|
||||
.bind(input.purpose)
|
||||
.bind(token_hash(input.token))
|
||||
.bind(input.lookup_key)
|
||||
.bind(input.payload)
|
||||
.bind(input.ttl_ms as f64)
|
||||
.fetch_optional(&mut **tx)
|
||||
.await
|
||||
.map_err(|err| napi_error(format!("{} failed: {err}", input.context)))?;
|
||||
|
||||
Ok(row.map(|row| row.get::<i64, _>("expires_at_ms")))
|
||||
}
|
||||
|
||||
pub(super) async fn update_attempts_in_tx(
|
||||
&self,
|
||||
tx: &mut sqlx::Transaction<'_, sqlx::Postgres>,
|
||||
|
||||
@@ -33,6 +33,11 @@ pub(super) async fn create(
|
||||
}
|
||||
|
||||
let mut tx = rows.begin("RuntimeState workspace invite link").await?;
|
||||
sqlx::query("SELECT pg_advisory_xact_lock(hashtextextended($1, 0))")
|
||||
.bind(&workspace_id)
|
||||
.execute(&mut *tx)
|
||||
.await
|
||||
.map_err(|err| napi_error(format!("RuntimeState workspace invite link active lock failed: {err}")))?;
|
||||
|
||||
if let Some(existing) =
|
||||
get_by_key_in_tx(rows, &mut tx, WORKSPACE_INVITE_LINK_WORKSPACE_PURPOSE, &workspace_id).await?
|
||||
@@ -51,8 +56,8 @@ pub(super) async fn create(
|
||||
"inviterUserId": inviter_user_id,
|
||||
});
|
||||
|
||||
let expires_at_ms = rows
|
||||
.insert_payload_returning_expires_in_tx(
|
||||
let Some(expires_at_ms) = rows
|
||||
.upsert_expired_or_consumed_payload_returning_expires_in_tx(
|
||||
&mut tx,
|
||||
RuntimeStateInsertPayload {
|
||||
purpose: WORKSPACE_INVITE_LINK_WORKSPACE_PURPOSE,
|
||||
@@ -63,7 +68,16 @@ pub(super) async fn create(
|
||||
context: "RuntimeState workspace invite link create",
|
||||
},
|
||||
)
|
||||
.await?;
|
||||
.await?
|
||||
else {
|
||||
let existing = get_by_key_in_tx(rows, &mut tx, WORKSPACE_INVITE_LINK_WORKSPACE_PURPOSE, &workspace_id).await?;
|
||||
tx.commit().await.map_err(|err| {
|
||||
napi_error(format!(
|
||||
"RuntimeState workspace invite link transaction commit failed: {err}"
|
||||
))
|
||||
})?;
|
||||
return existing.ok_or_else(|| napi_error("RuntimeState workspace invite link active conflict missing row"));
|
||||
};
|
||||
rows
|
||||
.insert_payload_returning_expires_in_tx(
|
||||
&mut tx,
|
||||
|
||||
Reference in New Issue
Block a user