fix in db and collaborate
This commit is contained in:
@@ -78,14 +78,11 @@ pub async fn load_editor_preferences(
|
||||
user_id: i64,
|
||||
resource: EditorPreferenceResource,
|
||||
) -> Result<Option<EditorPreferences>, sqlx::Error> {
|
||||
let Some(row) = sqlx::query(queries::get(
|
||||
pool.kind(),
|
||||
preference_select_query(resource),
|
||||
))
|
||||
.bind(user_id)
|
||||
.bind(resource_id(resource))
|
||||
.fetch_optional(pool.pool())
|
||||
.await?
|
||||
let Some(row) = sqlx::query(queries::get(pool.kind(), preference_select_query(resource)))
|
||||
.bind(user_id)
|
||||
.bind(resource_id(resource))
|
||||
.fetch_optional(pool.pool())
|
||||
.await?
|
||||
else {
|
||||
return Ok(None);
|
||||
};
|
||||
@@ -113,20 +110,17 @@ pub async fn save_editor_configuration(
|
||||
let user_id = user_id.ok_or_else(|| {
|
||||
sqlx::Error::Protocol("user id is required for personal editor preferences".into())
|
||||
})?;
|
||||
sqlx::query(queries::get(
|
||||
pool.kind(),
|
||||
preference_upsert_query(resource),
|
||||
))
|
||||
.bind(user_id)
|
||||
.bind(resource_id(resource))
|
||||
.bind(preferences.compact_view)
|
||||
.bind(preferences.editor_line_numbers)
|
||||
.bind(preferences.preview_line_numbers)
|
||||
.bind(preferences.line_links)
|
||||
.bind(&preferences.font_family)
|
||||
.bind(preferences.font_size)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(queries::get(pool.kind(), preference_upsert_query(resource)))
|
||||
.bind(user_id)
|
||||
.bind(resource_id(resource))
|
||||
.bind(preferences.compact_view)
|
||||
.bind(preferences.editor_line_numbers)
|
||||
.bind(preferences.preview_line_numbers)
|
||||
.bind(preferences.line_links)
|
||||
.bind(&preferences.font_family)
|
||||
.bind(preferences.font_size)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
}
|
||||
|
||||
if let Some((resource_kind, resource_slug, settings)) = resource_settings {
|
||||
|
||||
+140
-4
@@ -72,6 +72,13 @@ pub struct Note {
|
||||
pub created_by_guest_id: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CollaborationSnapshot {
|
||||
pub content: String,
|
||||
pub owner_map: String,
|
||||
pub revision_id: i64,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
struct SqliteNote {
|
||||
id: i64,
|
||||
@@ -316,6 +323,68 @@ pub async fn save_revision(
|
||||
Ok((revision_id, updated_at))
|
||||
}
|
||||
|
||||
pub async fn save_collaborative_revision(
|
||||
pool: &Database,
|
||||
note_id: i64,
|
||||
workspace_id: i64,
|
||||
content: &str,
|
||||
author: Option<&str>,
|
||||
owner_map: &str,
|
||||
collaboration_client_id: &str,
|
||||
collaboration_update_id: i64,
|
||||
) -> Result<(i64, String), sqlx::Error> {
|
||||
let mut tx = pool.pool().begin().await?;
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q006))
|
||||
.bind(content)
|
||||
.bind(owner_map)
|
||||
.bind(note_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q007))
|
||||
.bind(workspace_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q054))
|
||||
.bind(note_id)
|
||||
.bind(content)
|
||||
.bind(author)
|
||||
.bind(owner_map)
|
||||
.bind(collaboration_client_id)
|
||||
.bind(collaboration_update_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
let revision_id = inserted_id(pool.kind(), &mut tx, "note_revisions").await?;
|
||||
let updated_at: String = sqlx::query_scalar(queries::get(pool.kind(), queries::Q009))
|
||||
.bind(note_id)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok((revision_id, updated_at))
|
||||
}
|
||||
|
||||
pub async fn latest_note_collaboration_update_id(
|
||||
pool: &Database,
|
||||
note_id: i64,
|
||||
collaboration_client_id: &str,
|
||||
) -> Result<Option<u64>, sqlx::Error> {
|
||||
let update_id = sqlx::query_scalar::<_, Option<i64>>(queries::get(pool.kind(), queries::Q056))
|
||||
.bind(note_id)
|
||||
.bind(collaboration_client_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?;
|
||||
Ok(update_id.and_then(|value| u64::try_from(value).ok()))
|
||||
}
|
||||
|
||||
pub async fn note_collaboration_snapshot(
|
||||
pool: &Database,
|
||||
note_id: i64,
|
||||
) -> Result<CollaborationSnapshot, sqlx::Error> {
|
||||
sqlx::query_as::<_, CollaborationSnapshot>(queries::get(pool.kind(), queries::Q058))
|
||||
.bind(note_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_revisions(pool: &Database, note_id: i64) -> Result<Vec<Revision>, sqlx::Error> {
|
||||
sqlx::query_as::<_, Revision>(queries::get(pool.kind(), queries::Q010))
|
||||
.bind(note_id)
|
||||
@@ -392,7 +461,6 @@ pub struct Pad {
|
||||
pub password_hash: Option<String>,
|
||||
pub created_at: String,
|
||||
pub updated_at: String,
|
||||
pub owner_map: String,
|
||||
pub is_private: i64,
|
||||
pub created_by_guest_id: Option<String>,
|
||||
}
|
||||
@@ -476,6 +544,63 @@ pub async fn save_pad_revision(
|
||||
Ok((revision_id, updated_at))
|
||||
}
|
||||
|
||||
pub async fn save_collaborative_pad_revision(
|
||||
pool: &Database,
|
||||
pad_id: i64,
|
||||
content: &str,
|
||||
author: Option<&str>,
|
||||
owner_map: &str,
|
||||
collaboration_client_id: &str,
|
||||
collaboration_update_id: i64,
|
||||
) -> Result<(i64, String), sqlx::Error> {
|
||||
let mut tx = pool.pool().begin().await?;
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q013))
|
||||
.bind(content)
|
||||
.bind(owner_map)
|
||||
.bind(pad_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q055))
|
||||
.bind(pad_id)
|
||||
.bind(content)
|
||||
.bind(author)
|
||||
.bind(owner_map)
|
||||
.bind(collaboration_client_id)
|
||||
.bind(collaboration_update_id)
|
||||
.execute(&mut *tx)
|
||||
.await?;
|
||||
let revision_id = inserted_id(pool.kind(), &mut tx, "revisions").await?;
|
||||
let updated_at: String = sqlx::query_scalar(queries::get(pool.kind(), queries::Q015))
|
||||
.bind(pad_id)
|
||||
.fetch_one(&mut *tx)
|
||||
.await?;
|
||||
tx.commit().await?;
|
||||
Ok((revision_id, updated_at))
|
||||
}
|
||||
|
||||
pub async fn latest_pad_collaboration_update_id(
|
||||
pool: &Database,
|
||||
pad_id: i64,
|
||||
collaboration_client_id: &str,
|
||||
) -> Result<Option<u64>, sqlx::Error> {
|
||||
let update_id = sqlx::query_scalar::<_, Option<i64>>(queries::get(pool.kind(), queries::Q057))
|
||||
.bind(pad_id)
|
||||
.bind(collaboration_client_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?;
|
||||
Ok(update_id.and_then(|value| u64::try_from(value).ok()))
|
||||
}
|
||||
|
||||
pub async fn pad_collaboration_snapshot(
|
||||
pool: &Database,
|
||||
pad_id: i64,
|
||||
) -> Result<CollaborationSnapshot, sqlx::Error> {
|
||||
sqlx::query_as::<_, CollaborationSnapshot>(queries::get(pool.kind(), queries::Q059))
|
||||
.bind(pad_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn list_pad_revisions(
|
||||
pool: &Database,
|
||||
pad_id: i64,
|
||||
@@ -486,6 +611,16 @@ pub async fn list_pad_revisions(
|
||||
.await
|
||||
}
|
||||
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for CollaborationSnapshot {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
content: crate::row_decode::text(row, "content")?,
|
||||
owner_map: crate::row_decode::text(row, "owner_map")?,
|
||||
revision_id: row.try_get("revision_id")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for Workspace {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
@@ -538,7 +673,6 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for Pad {
|
||||
password_hash: crate::row_decode::optional_text(row, "password_hash")?,
|
||||
created_at: crate::row_decode::text(row, "created_at")?,
|
||||
updated_at: crate::row_decode::text(row, "updated_at")?,
|
||||
owner_map: crate::row_decode::text(row, "owner_map")?,
|
||||
is_private: row.try_get("is_private")?,
|
||||
created_by_guest_id: crate::row_decode::optional_text(row, "created_by_guest_id")?,
|
||||
})
|
||||
@@ -570,7 +704,6 @@ mod password_verification_tests {
|
||||
password_hash,
|
||||
created_at: String::new(),
|
||||
updated_at: String::new(),
|
||||
owner_map: "[]".into(),
|
||||
is_private: 1,
|
||||
created_by_guest_id: None,
|
||||
}
|
||||
@@ -579,7 +712,10 @@ mod password_verification_tests {
|
||||
#[test]
|
||||
fn missing_password_does_not_grant_password_access() {
|
||||
assert!(!verify_workspace_password(&workspace(None), None));
|
||||
assert!(!verify_workspace_password(&workspace(None), Some("anything")));
|
||||
assert!(!verify_workspace_password(
|
||||
&workspace(None),
|
||||
Some("anything")
|
||||
));
|
||||
assert!(!verify_pad_password(&pad(None), None));
|
||||
assert!(!verify_pad_password(&pad(None), Some("anything")));
|
||||
}
|
||||
|
||||
+44
-42
@@ -15,6 +15,10 @@ pub struct PublishedPage {
|
||||
pub pad_id: Option<i64>,
|
||||
pub note_id: Option<i64>,
|
||||
pub allow_task_updates: bool,
|
||||
pub resource_slug: String,
|
||||
pub workspace_id: Option<i64>,
|
||||
pub workspace_slug: Option<String>,
|
||||
pub owner_map: String,
|
||||
pub title: String,
|
||||
pub content: String,
|
||||
pub updated_at: String,
|
||||
@@ -26,6 +30,10 @@ struct PublishedPageRow {
|
||||
pad_id: Option<i64>,
|
||||
note_id: Option<i64>,
|
||||
allow_task_updates: i64,
|
||||
resource_slug: String,
|
||||
workspace_id: Option<i64>,
|
||||
workspace_slug: Option<String>,
|
||||
owner_map: String,
|
||||
title: String,
|
||||
content: String,
|
||||
updated_at: String,
|
||||
@@ -37,6 +45,10 @@ struct PostgresPublishedPageRow {
|
||||
pad_id: Option<i64>,
|
||||
note_id: Option<i64>,
|
||||
allow_task_updates: bool,
|
||||
resource_slug: String,
|
||||
workspace_id: Option<i64>,
|
||||
workspace_slug: Option<String>,
|
||||
owner_map: String,
|
||||
title: String,
|
||||
content: String,
|
||||
updated_at: String,
|
||||
@@ -49,6 +61,10 @@ impl From<PostgresPublishedPageRow> for PublishedPage {
|
||||
pad_id: value.pad_id,
|
||||
note_id: value.note_id,
|
||||
allow_task_updates: value.allow_task_updates,
|
||||
resource_slug: value.resource_slug,
|
||||
workspace_id: value.workspace_id,
|
||||
workspace_slug: value.workspace_slug,
|
||||
owner_map: value.owner_map,
|
||||
title: value.title,
|
||||
content: value.content,
|
||||
updated_at: value.updated_at,
|
||||
@@ -62,6 +78,10 @@ impl From<PublishedPageRow> for PublishedPage {
|
||||
pad_id: value.pad_id,
|
||||
note_id: value.note_id,
|
||||
allow_task_updates: value.allow_task_updates != 0,
|
||||
resource_slug: value.resource_slug,
|
||||
workspace_id: value.workspace_id,
|
||||
workspace_slug: value.workspace_slug,
|
||||
owner_map: value.owner_map,
|
||||
title: value.title,
|
||||
content: value.content,
|
||||
updated_at: value.updated_at,
|
||||
@@ -310,29 +330,23 @@ pub async fn set_note_public_page_unprotected(
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn update_public_task(
|
||||
pool: &Database,
|
||||
token: &str,
|
||||
pub fn updated_public_task_content(
|
||||
content: &str,
|
||||
source_line: usize,
|
||||
checked: bool,
|
||||
) -> Result<Option<PublishedPage>, sqlx::Error> {
|
||||
let Some(mut page) = find_published_page(pool, token).await? else {
|
||||
return Ok(None);
|
||||
};
|
||||
if !page.allow_task_updates || source_line == 0 {
|
||||
return Ok(Some(page));
|
||||
) -> Option<String> {
|
||||
if source_line == 0 {
|
||||
return None;
|
||||
}
|
||||
let mut lines: Vec<String> = page.content.split('\n').map(str::to_owned).collect();
|
||||
let Some(line) = lines.get_mut(source_line - 1) else {
|
||||
return Ok(Some(page));
|
||||
};
|
||||
let mut lines: Vec<String> = content.split('\n').map(str::to_owned).collect();
|
||||
let line = lines.get_mut(source_line - 1)?;
|
||||
let bytes = line.as_bytes();
|
||||
let mut i = 0usize;
|
||||
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
|
||||
i += 1;
|
||||
}
|
||||
if i >= bytes.len() || !matches!(bytes[i], b'-' | b'*' | b'+') {
|
||||
return Ok(Some(page));
|
||||
return None;
|
||||
}
|
||||
i += 1;
|
||||
while i < bytes.len() && bytes[i].is_ascii_whitespace() {
|
||||
@@ -343,24 +357,10 @@ pub async fn update_public_task(
|
||||
|| !matches!(bytes[i + 1], b' ' | b'x' | b'X')
|
||||
|| bytes[i + 2] != b']'
|
||||
{
|
||||
return Ok(Some(page));
|
||||
return None;
|
||||
}
|
||||
line.replace_range(i + 1..i + 2, if checked { "x" } else { " " });
|
||||
page.content = lines.join("\n");
|
||||
if let Some(id) = page.pad_id {
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q042))
|
||||
.bind(&page.content)
|
||||
.bind(id)
|
||||
.execute(pool.pool())
|
||||
.await?;
|
||||
} else if let Some(id) = page.note_id {
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q043))
|
||||
.bind(&page.content)
|
||||
.bind(id)
|
||||
.execute(pool.pool())
|
||||
.await?;
|
||||
}
|
||||
find_published_page(pool, token).await
|
||||
Some(lines.join("\n"))
|
||||
}
|
||||
|
||||
pub async fn pad_file_token(pool: &Database, pad_id: i64) -> Result<String, sqlx::Error> {
|
||||
@@ -416,6 +416,10 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for PublishedPageRow {
|
||||
pad_id: row.try_get("pad_id")?,
|
||||
note_id: row.try_get("note_id")?,
|
||||
allow_task_updates: row.try_get("allow_task_updates")?,
|
||||
resource_slug: crate::row_decode::text(row, "resource_slug")?,
|
||||
workspace_id: row.try_get("workspace_id")?,
|
||||
workspace_slug: crate::row_decode::optional_text(row, "workspace_slug")?,
|
||||
owner_map: crate::row_decode::text(row, "owner_map")?,
|
||||
title: crate::row_decode::text(row, "title")?,
|
||||
content: crate::row_decode::text(row, "content")?,
|
||||
updated_at: crate::row_decode::text(row, "updated_at")?,
|
||||
@@ -429,6 +433,10 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for PostgresPublishedPageRow {
|
||||
pad_id: row.try_get("pad_id")?,
|
||||
note_id: row.try_get("note_id")?,
|
||||
allow_task_updates: row.try_get("allow_task_updates")?,
|
||||
resource_slug: crate::row_decode::text(row, "resource_slug")?,
|
||||
workspace_id: row.try_get("workspace_id")?,
|
||||
workspace_slug: crate::row_decode::optional_text(row, "workspace_slug")?,
|
||||
owner_map: crate::row_decode::text(row, "owner_map")?,
|
||||
title: crate::row_decode::text(row, "title")?,
|
||||
content: crate::row_decode::text(row, "content")?,
|
||||
updated_at: crate::row_decode::text(row, "updated_at")?,
|
||||
@@ -437,17 +445,14 @@ impl<'r> sqlx::FromRow<'r, AnyRow> for PostgresPublishedPageRow {
|
||||
}
|
||||
|
||||
pub async fn pad_public_page_disabled(pool: &Database, pad_id: i64) -> Result<bool, sqlx::Error> {
|
||||
let sql = match pool.kind() {
|
||||
DatabaseKind::Postgres => "SELECT public_page_disabled FROM pads WHERE id = $1",
|
||||
_ => "SELECT public_page_disabled FROM pads WHERE id = ?",
|
||||
};
|
||||
let query = queries::get(pool.kind(), queries::PAD_PUBLIC_PAGE_DISABLED);
|
||||
if pool.kind() == DatabaseKind::Postgres {
|
||||
return Ok(sqlx::query_scalar::<_, bool>(sql)
|
||||
return Ok(sqlx::query_scalar::<_, bool>(query)
|
||||
.bind(pad_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?);
|
||||
}
|
||||
Ok(sqlx::query_scalar::<_, i64>(sql)
|
||||
Ok(sqlx::query_scalar::<_, i64>(query)
|
||||
.bind(pad_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?
|
||||
@@ -455,17 +460,14 @@ pub async fn pad_public_page_disabled(pool: &Database, pad_id: i64) -> Result<bo
|
||||
}
|
||||
|
||||
pub async fn note_public_page_disabled(pool: &Database, note_id: i64) -> Result<bool, sqlx::Error> {
|
||||
let sql = match pool.kind() {
|
||||
DatabaseKind::Postgres => "SELECT public_page_disabled FROM notes WHERE id = $1",
|
||||
_ => "SELECT public_page_disabled FROM notes WHERE id = ?",
|
||||
};
|
||||
let query = queries::get(pool.kind(), queries::NOTE_PUBLIC_PAGE_DISABLED);
|
||||
if pool.kind() == DatabaseKind::Postgres {
|
||||
return Ok(sqlx::query_scalar::<_, bool>(sql)
|
||||
return Ok(sqlx::query_scalar::<_, bool>(query)
|
||||
.bind(note_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?);
|
||||
}
|
||||
Ok(sqlx::query_scalar::<_, i64>(sql)
|
||||
Ok(sqlx::query_scalar::<_, i64>(query)
|
||||
.bind(note_id)
|
||||
.fetch_one(pool.pool())
|
||||
.await?
|
||||
|
||||
Reference in New Issue
Block a user