update rustpad
This commit is contained in:
+30
-3
@@ -20,7 +20,7 @@ use lettre::{
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sha2::{Digest, Sha256};
|
||||
use sqlx::FromRow;
|
||||
use sqlx::{any::AnyRow, FromRow, Row};
|
||||
use tracing::{debug, info, warn};
|
||||
|
||||
use crate::{
|
||||
@@ -32,7 +32,7 @@ const MIN_PASSWORD: usize = 8;
|
||||
const MAX_PASSWORD: usize = 128;
|
||||
const MAX_NICKNAME: usize = 40;
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct User {
|
||||
pub id: i64,
|
||||
pub nickname: String,
|
||||
@@ -103,7 +103,7 @@ pub struct ResourceActionRequest {
|
||||
#[serde(default)]
|
||||
password: Option<String>,
|
||||
}
|
||||
#[derive(Serialize, FromRow)]
|
||||
#[derive(Serialize)]
|
||||
pub struct ResourceItem {
|
||||
slug: String,
|
||||
title: String,
|
||||
@@ -117,6 +117,33 @@ pub struct ResourceItem {
|
||||
shared_by: String,
|
||||
}
|
||||
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for User {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
id: row.try_get("id")?,
|
||||
nickname: crate::row_decode::text(row, "nickname")?,
|
||||
email: crate::row_decode::text(row, "email")?,
|
||||
password_hash: crate::row_decode::text(row, "password_hash")?,
|
||||
confirmed_at: crate::row_decode::optional_text(row, "confirmed_at")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for ResourceItem {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self {
|
||||
slug: crate::row_decode::text(row, "slug")?,
|
||||
title: crate::row_decode::text(row, "title")?,
|
||||
protected: row.try_get("protected")?,
|
||||
updated_at: crate::row_decode::text(row, "updated_at")?,
|
||||
private_resource: row.try_get("private")?,
|
||||
owned: row.try_get("owned")?,
|
||||
permission: crate::row_decode::text(row, "permission")?,
|
||||
shared_by: crate::row_decode::text(row, "shared_by")?,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
pub struct PrivacyRequest {
|
||||
kind: String,
|
||||
|
||||
@@ -7,32 +7,7 @@ use chrono::{DateTime, NaiveDateTime, Utc};
|
||||
use rand_core::{OsRng, RngCore};
|
||||
use serde::Serialize;
|
||||
use sqlx::FromRow;
|
||||
use sqlx::{Any, Row, Transaction, any::AnyRow};
|
||||
|
||||
fn any_text(row: &AnyRow, column: &str) -> Result<String, sqlx::Error> {
|
||||
match row.try_get::<String, _>(column) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(string_error) => match row.try_get::<Vec<u8>, _>(column) {
|
||||
Ok(value) => {
|
||||
String::from_utf8(value).map_err(|error| sqlx::Error::Decode(Box::new(error)))
|
||||
}
|
||||
Err(_) => Err(string_error),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
fn any_optional_text(row: &AnyRow, column: &str) -> Result<Option<String>, sqlx::Error> {
|
||||
match row.try_get::<Option<String>, _>(column) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(string_error) => match row.try_get::<Option<Vec<u8>>, _>(column) {
|
||||
Ok(Some(value)) => String::from_utf8(value)
|
||||
.map(Some)
|
||||
.map_err(|error| sqlx::Error::Decode(Box::new(error))),
|
||||
Ok(None) => Ok(None),
|
||||
Err(_) => Err(string_error),
|
||||
},
|
||||
}
|
||||
}
|
||||
use sqlx::{any::AnyRow, Any, Row, Transaction};
|
||||
|
||||
async fn inserted_id(
|
||||
kind: DatabaseKind,
|
||||
@@ -51,7 +26,7 @@ async fn inserted_id(
|
||||
sqlx::query_scalar(query).fetch_one(&mut **tx).await
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Workspace {
|
||||
pub id: i64,
|
||||
pub slug: String,
|
||||
@@ -62,19 +37,7 @@ pub struct Workspace {
|
||||
pub is_private: i64,
|
||||
}
|
||||
|
||||
fn workspace_from_any_row(row: AnyRow) -> Result<Workspace, sqlx::Error> {
|
||||
Ok(Workspace {
|
||||
id: row.try_get("id")?,
|
||||
slug: any_text(&row, "slug")?,
|
||||
title: any_text(&row, "title")?,
|
||||
password_hash: any_optional_text(&row, "password_hash")?,
|
||||
created_at: any_text(&row, "created_at")?,
|
||||
updated_at: any_text(&row, "updated_at")?,
|
||||
is_private: row.try_get("is_private")?,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct Note {
|
||||
pub id: i64,
|
||||
#[serde(skip_serializing)]
|
||||
@@ -122,22 +85,7 @@ impl From<SqliteNote> for Note {
|
||||
}
|
||||
}
|
||||
|
||||
fn note_from_any_row(row: AnyRow) -> Result<Note, sqlx::Error> {
|
||||
Ok(Note {
|
||||
id: row.try_get("id")?,
|
||||
_workspace_id: row.try_get("workspace_id")?,
|
||||
slug: any_text(&row, "slug")?,
|
||||
title: any_text(&row, "title")?,
|
||||
content: any_text(&row, "content")?,
|
||||
created_at: any_text(&row, "created_at")?,
|
||||
updated_at: any_text(&row, "updated_at")?,
|
||||
owner_map: any_text(&row, "owner_map")?,
|
||||
protected: row.try_get("protected")?,
|
||||
created_by: any_optional_text(&row, "created_by")?,
|
||||
})
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, FromRow)]
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct Revision {
|
||||
pub id: i64,
|
||||
pub content: String,
|
||||
@@ -146,25 +94,7 @@ pub struct Revision {
|
||||
pub owner_map: String,
|
||||
}
|
||||
|
||||
fn revision_from_any_row(row: AnyRow) -> Result<Revision, sqlx::Error> {
|
||||
Ok(Revision {
|
||||
id: row.try_get("id")?,
|
||||
content: any_text(&row, "content")?,
|
||||
created_at: any_text(&row, "created_at")?,
|
||||
author: any_optional_text(&row, "author")?,
|
||||
owner_map: any_text(&row, "owner_map")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn find_workspace(pool: &Database, slug: &str) -> Result<Option<Workspace>, sqlx::Error> {
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q001))
|
||||
.bind(slug)
|
||||
.fetch_optional(pool.pool())
|
||||
.await?
|
||||
.map(workspace_from_any_row)
|
||||
.transpose();
|
||||
}
|
||||
sqlx::query_as::<_, Workspace>(queries::get(pool.kind(), queries::Q001))
|
||||
.bind(slug)
|
||||
.fetch_optional(pool.pool())
|
||||
@@ -187,14 +117,6 @@ pub async fn create_workspace(
|
||||
.execute(pool.pool())
|
||||
.await?;
|
||||
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return workspace_from_any_row(
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q001))
|
||||
.bind(slug)
|
||||
.fetch_one(pool.pool())
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
sqlx::query_as::<_, Workspace>(queries::get(pool.kind(), queries::Q001))
|
||||
.bind(slug)
|
||||
.fetch_one(pool.pool())
|
||||
@@ -229,15 +151,6 @@ pub async fn list_notes(pool: &Database, workspace_id: i64) -> Result<Vec<Note>,
|
||||
.map(Note::from)
|
||||
.collect());
|
||||
}
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q003))
|
||||
.bind(workspace_id)
|
||||
.fetch_all(pool.pool())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(note_from_any_row)
|
||||
.collect();
|
||||
}
|
||||
sqlx::query_as::<_, Note>(queries::get(pool.kind(), queries::Q003))
|
||||
.bind(workspace_id)
|
||||
.fetch_all(pool.pool())
|
||||
@@ -257,15 +170,6 @@ pub async fn find_note(
|
||||
.await?
|
||||
.map(Note::from));
|
||||
}
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q004))
|
||||
.bind(workspace_id)
|
||||
.bind(slug)
|
||||
.fetch_optional(pool.pool())
|
||||
.await?
|
||||
.map(note_from_any_row)
|
||||
.transpose();
|
||||
}
|
||||
sqlx::query_as::<_, Note>(queries::get(pool.kind(), queries::Q004))
|
||||
.bind(workspace_id)
|
||||
.bind(slug)
|
||||
@@ -331,15 +235,6 @@ pub async fn save_revision(
|
||||
}
|
||||
|
||||
pub async fn list_revisions(pool: &Database, note_id: i64) -> Result<Vec<Revision>, sqlx::Error> {
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q010))
|
||||
.bind(note_id)
|
||||
.fetch_all(pool.pool())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(revision_from_any_row)
|
||||
.collect();
|
||||
}
|
||||
sqlx::query_as::<_, Revision>(queries::get(pool.kind(), queries::Q010))
|
||||
.bind(note_id)
|
||||
.fetch_all(pool.pool())
|
||||
@@ -406,7 +301,7 @@ pub fn normalize_timestamp(value: &str) -> String {
|
||||
value.to_string()
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Pad {
|
||||
pub id: i64,
|
||||
pub slug: String,
|
||||
@@ -419,29 +314,7 @@ pub struct Pad {
|
||||
pub is_private: i64,
|
||||
}
|
||||
|
||||
fn pad_from_any_row(row: AnyRow) -> Result<Pad, sqlx::Error> {
|
||||
Ok(Pad {
|
||||
id: row.try_get("id")?,
|
||||
slug: any_text(&row, "slug")?,
|
||||
title: any_text(&row, "title")?,
|
||||
content: any_text(&row, "content")?,
|
||||
password_hash: any_optional_text(&row, "password_hash")?,
|
||||
created_at: any_text(&row, "created_at")?,
|
||||
updated_at: any_text(&row, "updated_at")?,
|
||||
owner_map: any_text(&row, "owner_map")?,
|
||||
is_private: row.try_get("is_private")?,
|
||||
})
|
||||
}
|
||||
|
||||
pub async fn find_pad(pool: &Database, slug: &str) -> Result<Option<Pad>, sqlx::Error> {
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q011))
|
||||
.bind(slug)
|
||||
.fetch_optional(pool.pool())
|
||||
.await?
|
||||
.map(pad_from_any_row)
|
||||
.transpose();
|
||||
}
|
||||
sqlx::query_as::<_, Pad>(queries::get(pool.kind(), queries::Q011))
|
||||
.bind(slug)
|
||||
.fetch_optional(pool.pool())
|
||||
@@ -464,14 +337,6 @@ pub async fn create_pad(
|
||||
.execute(pool.pool())
|
||||
.await?;
|
||||
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return pad_from_any_row(
|
||||
sqlx::query(queries::get(pool.kind(), queries::Q011))
|
||||
.bind(slug)
|
||||
.fetch_one(pool.pool())
|
||||
.await?,
|
||||
);
|
||||
}
|
||||
sqlx::query_as::<_, Pad>(queries::get(pool.kind(), queries::Q011))
|
||||
.bind(slug)
|
||||
.fetch_one(pool.pool())
|
||||
@@ -530,15 +395,6 @@ pub async fn list_pad_revisions(
|
||||
pool: &Database,
|
||||
pad_id: i64,
|
||||
) -> Result<Vec<Revision>, sqlx::Error> {
|
||||
if pool.kind() == DatabaseKind::MySql {
|
||||
return sqlx::query(queries::get(pool.kind(), queries::Q016))
|
||||
.bind(pad_id)
|
||||
.fetch_all(pool.pool())
|
||||
.await?
|
||||
.into_iter()
|
||||
.map(revision_from_any_row)
|
||||
.collect();
|
||||
}
|
||||
sqlx::query_as::<_, Revision>(queries::get(pool.kind(), queries::Q016))
|
||||
.bind(pad_id)
|
||||
.fetch_all(pool.pool())
|
||||
@@ -556,7 +412,7 @@ pub struct PublishedPage {
|
||||
pub updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct PublishedPageRow {
|
||||
token: String,
|
||||
pad_id: Option<i64>,
|
||||
@@ -567,7 +423,7 @@ struct PublishedPageRow {
|
||||
updated_at: String,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, FromRow)]
|
||||
#[derive(Debug, Clone)]
|
||||
struct PostgresPublishedPageRow {
|
||||
token: String,
|
||||
pad_id: Option<i64>,
|
||||
@@ -863,7 +719,7 @@ pub async fn find_file_owner(
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct NoteFile {
|
||||
pub id: i64,
|
||||
pub filename: String,
|
||||
@@ -1079,3 +935,35 @@ pub async fn delete_pad_file(
|
||||
.await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for Workspace {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self { id: row.try_get("id")?, slug: crate::row_decode::text(row, "slug")?, title: crate::row_decode::text(row, "title")?, 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")?, is_private: row.try_get("is_private")? })
|
||||
}
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for Note {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
let protected: i64 = row.try_get("protected")?;
|
||||
Ok(Self { id: row.try_get("id")?, _workspace_id: row.try_get("workspace_id")?, slug: crate::row_decode::text(row, "slug")?, title: crate::row_decode::text(row, "title")?, content: crate::row_decode::text(row, "content")?, 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")?, protected: protected != 0, created_by: crate::row_decode::optional_text(row, "created_by")? })
|
||||
}
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for Revision {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self { id: row.try_get("id")?, content: crate::row_decode::text(row, "content")?, created_at: crate::row_decode::text(row, "created_at")?, author: crate::row_decode::optional_text(row, "author")?, owner_map: crate::row_decode::text(row, "owner_map")? })
|
||||
}
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for Pad {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> {
|
||||
Ok(Self { id: row.try_get("id")?, slug: crate::row_decode::text(row, "slug")?, title: crate::row_decode::text(row, "title")?, content: crate::row_decode::text(row, "content")?, 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")? })
|
||||
}
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for PublishedPageRow {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> { Ok(Self { token: crate::row_decode::text(row,"token")?, pad_id: row.try_get("pad_id")?, note_id: row.try_get("note_id")?, allow_task_updates: row.try_get("allow_task_updates")?, title: crate::row_decode::text(row,"title")?, content: crate::row_decode::text(row,"content")?, updated_at: crate::row_decode::text(row,"updated_at")? }) }
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for PostgresPublishedPageRow {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> { Ok(Self { token: crate::row_decode::text(row,"token")?, pad_id: row.try_get("pad_id")?, note_id: row.try_get("note_id")?, allow_task_updates: row.try_get("allow_task_updates")?, title: crate::row_decode::text(row,"title")?, content: crate::row_decode::text(row,"content")?, updated_at: crate::row_decode::text(row,"updated_at")? }) }
|
||||
}
|
||||
impl<'r> sqlx::FromRow<'r, AnyRow> for NoteFile {
|
||||
fn from_row(row: &'r AnyRow) -> Result<Self, sqlx::Error> { let attached:i64=row.try_get("is_attached")?; Ok(Self { id:row.try_get("id")?, filename:crate::row_decode::text(row,"filename")?, url:crate::row_decode::text(row,"url")?, mime_type:crate::row_decode::text(row,"mime_type")?, size_bytes:row.try_get("size_bytes")?, created_at:crate::row_decode::text(row,"created_at")?, is_attached:attached!=0, detached_at:crate::row_decode::optional_text(row,"detached_at")? }) }
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
mod row_decode;
|
||||
mod api;
|
||||
mod app;
|
||||
mod assets;
|
||||
|
||||
+11
-7
@@ -129,9 +129,9 @@ pub const Q001_MYSQL: &str = "SELECT id, slug, CAST(title AS CHAR CHARACTER SET
|
||||
pub const Q001_POSTGRES: &str = "SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM workspaces WHERE slug = ?";
|
||||
pub const Q002: &str = "INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)";
|
||||
pub const Q003: &str = "SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? ORDER BY updated_at DESC, id DESC";
|
||||
pub const Q003_MYSQL: &str = "SELECT id, workspace_id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, protected, CAST(created_by AS CHAR CHARACTER SET utf8mb4) AS created_by FROM notes WHERE workspace_id = ? ORDER BY updated_at DESC, id DESC";
|
||||
pub const Q003_MYSQL: &str = "SELECT id, workspace_id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS SIGNED) AS protected, CAST(created_by AS CHAR CHARACTER SET utf8mb4) AS created_by FROM notes WHERE workspace_id = ? ORDER BY updated_at DESC, id DESC";
|
||||
pub const Q004: &str = "SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? AND slug = ?";
|
||||
pub const Q004_MYSQL: &str = "SELECT id, workspace_id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, protected, CAST(created_by AS CHAR CHARACTER SET utf8mb4) AS created_by FROM notes WHERE workspace_id = ? AND slug = ?";
|
||||
pub const Q004_MYSQL: &str = "SELECT id, workspace_id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS SIGNED) AS protected, CAST(created_by AS CHAR CHARACTER SET utf8mb4) AS created_by FROM notes WHERE workspace_id = ? AND slug = ?";
|
||||
pub const Q005: &str =
|
||||
"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES (?, ?, ?, ?, ?)";
|
||||
pub const Q006: &str =
|
||||
@@ -159,7 +159,7 @@ pub const Q018: &str = "INSERT INTO published_pages (token, pad_id) VALUES (?, ?
|
||||
pub const Q019: &str = "SELECT token FROM published_pages WHERE note_id = ?";
|
||||
pub const Q020: &str = "INSERT INTO published_pages (token, note_id) VALUES (?, ?)";
|
||||
pub const Q021: &str = "SELECT pp.token, pp.pad_id, pp.note_id, CASE WHEN pp.allow_task_updates THEN 1 ELSE 0 END AS allow_task_updates, COALESCE(p.title, n.title) AS title, COALESCE(p.content, n.content) AS content, COALESCE(p.updated_at, n.updated_at) AS updated_at FROM published_pages pp LEFT JOIN pads p ON p.id = pp.pad_id LEFT JOIN notes n ON n.id = pp.note_id WHERE pp.token = ?";
|
||||
pub const Q021_MYSQL: &str = "SELECT pp.token, pp.pad_id, pp.note_id, CASE WHEN pp.allow_task_updates THEN 1 ELSE 0 END AS allow_task_updates, CAST(COALESCE(p.title, n.title) AS CHAR CHARACTER SET utf8mb4) AS title, CAST(COALESCE(p.content, n.content) AS CHAR CHARACTER SET utf8mb4) AS content, COALESCE(p.updated_at, n.updated_at) AS updated_at FROM published_pages pp LEFT JOIN pads p ON p.id = pp.pad_id LEFT JOIN notes n ON n.id = pp.note_id WHERE pp.token = ?";
|
||||
pub const Q021_MYSQL: &str = "SELECT pp.token, pp.pad_id, pp.note_id, CAST(CASE WHEN pp.allow_task_updates THEN 1 ELSE 0 END AS SIGNED) AS allow_task_updates, CAST(COALESCE(p.title, n.title) AS CHAR CHARACTER SET utf8mb4) AS title, CAST(COALESCE(p.content, n.content) AS CHAR CHARACTER SET utf8mb4) AS content, COALESCE(p.updated_at, n.updated_at) AS updated_at FROM published_pages pp LEFT JOIN pads p ON p.id = pp.pad_id LEFT JOIN notes n ON n.id = pp.note_id WHERE pp.token = ?";
|
||||
pub const Q022: &str = "SELECT file_token FROM pads WHERE id = ?";
|
||||
pub const Q023: &str = "UPDATE pads SET file_token = ? WHERE id = ? AND file_token IS NULL";
|
||||
pub const Q024: &str = "SELECT file_token FROM notes WHERE id = ?";
|
||||
@@ -176,18 +176,18 @@ pub const Q031: &str = "DELETE FROM notes WHERE id = ?";
|
||||
pub const Q032: &str =
|
||||
"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)";
|
||||
pub const Q033: &str = "SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = ? ORDER BY id DESC";
|
||||
pub const Q033_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = ? ORDER BY id DESC";
|
||||
pub const Q033_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, CAST(CASE WHEN is_attached THEN 1 ELSE 0 END AS SIGNED) AS is_attached, detached_at FROM note_files WHERE note_id = ? ORDER BY id DESC";
|
||||
pub const Q034: &str = "UPDATE note_files SET is_attached = ?, detached_at = ? WHERE id = ?";
|
||||
pub const Q035: &str =
|
||||
"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)";
|
||||
pub const Q036: &str = "SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = ? ORDER BY id DESC";
|
||||
pub const Q036_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = ? ORDER BY id DESC";
|
||||
pub const Q036_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, CAST(CASE WHEN is_attached THEN 1 ELSE 0 END AS SIGNED) AS is_attached, detached_at FROM pad_files WHERE pad_id = ? ORDER BY id DESC";
|
||||
pub const Q037: &str = "UPDATE pad_files SET is_attached = ?, detached_at = ? WHERE id = ?";
|
||||
pub const Q038: &str = "SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = ? AND note_id = ?";
|
||||
pub const Q038_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = ? AND note_id = ?";
|
||||
pub const Q038_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, CAST(CASE WHEN is_attached THEN 1 ELSE 0 END AS SIGNED) AS is_attached, detached_at FROM note_files WHERE id = ? AND note_id = ?";
|
||||
pub const Q039: &str = "DELETE FROM note_files WHERE id = ? AND note_id = ?";
|
||||
pub const Q046: &str = "SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = ? AND pad_id = ?";
|
||||
pub const Q046_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = ? AND pad_id = ?";
|
||||
pub const Q046_MYSQL: &str = "SELECT id, CAST(filename AS CHAR CHARACTER SET utf8mb4) AS filename, url, mime_type, size_bytes, created_at, CAST(CASE WHEN is_attached THEN 1 ELSE 0 END AS SIGNED) AS is_attached, detached_at FROM pad_files WHERE id = ? AND pad_id = ?";
|
||||
pub const Q047: &str = "DELETE FROM pad_files WHERE id = ? AND pad_id = ?";
|
||||
|
||||
static POSTGRES_QUERIES: OnceLock<Mutex<HashMap<&'static str, &'static str>>> = OnceLock::new();
|
||||
@@ -220,6 +220,8 @@ pub fn get(kind: DatabaseKind, query: &'static str) -> &'static str {
|
||||
(DatabaseKind::MySql, Q036) => Q036_MYSQL,
|
||||
(DatabaseKind::MySql, Q038) => Q038_MYSQL,
|
||||
(DatabaseKind::MySql, Q046) => Q046_MYSQL,
|
||||
(DatabaseKind::MySql, Q044) => Q044_MYSQL,
|
||||
(DatabaseKind::MySql, Q045) => Q045_MYSQL,
|
||||
_ => query,
|
||||
};
|
||||
|
||||
@@ -258,6 +260,8 @@ pub const Q044: &str =
|
||||
"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE pad_id = ?";
|
||||
pub const Q045: &str =
|
||||
"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE note_id = ?";
|
||||
pub const Q044_MYSQL: &str = "SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE pad_id = ?";
|
||||
pub const Q045_MYSQL: &str = "SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE note_id = ?";
|
||||
|
||||
pub const Q021_POSTGRES: &str = "SELECT pp.token, pp.pad_id, pp.note_id, pp.allow_task_updates, COALESCE(p.title, n.title) AS title, COALESCE(p.content, n.content) AS content, COALESCE(p.updated_at, n.updated_at) AS updated_at FROM published_pages pp LEFT JOIN pads p ON p.id = pp.pad_id LEFT JOIN notes n ON n.id = pp.note_id WHERE pp.token = $1";
|
||||
pub const Q044_POSTGRES: &str = "SELECT allow_task_updates FROM published_pages WHERE pad_id = $1";
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
use sqlx::{any::AnyRow, ColumnIndex, Error, Row};
|
||||
|
||||
pub fn text<I>(row: &AnyRow, index: I) -> Result<String, Error>
|
||||
where
|
||||
I: ColumnIndex<AnyRow> + Copy,
|
||||
{
|
||||
match row.try_get::<String, _>(index) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(string_error) => match row.try_get::<Vec<u8>, _>(index) {
|
||||
Ok(value) => String::from_utf8(value).map_err(|error| Error::Decode(Box::new(error))),
|
||||
Err(_) => Err(string_error),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
pub fn optional_text<I>(row: &AnyRow, index: I) -> Result<Option<String>, Error>
|
||||
where
|
||||
I: ColumnIndex<AnyRow> + Copy,
|
||||
{
|
||||
match row.try_get::<Option<String>, _>(index) {
|
||||
Ok(value) => Ok(value),
|
||||
Err(string_error) => match row.try_get::<Option<Vec<u8>>, _>(index) {
|
||||
Ok(Some(value)) => String::from_utf8(value)
|
||||
.map(Some)
|
||||
.map_err(|error| Error::Decode(Box::new(error))),
|
||||
Ok(None) => Ok(None),
|
||||
Err(_) => Err(string_error),
|
||||
},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user