Files
rustpad/src/db/files.rs
T
2026-07-27 23:27:56 +02:00

281 lines
7.1 KiB
Rust

use super::*;
#[derive(Debug, Clone, Copy)]
pub enum FileOwnerKind {
Pad,
Note,
}
#[derive(Debug, Clone, Copy)]
pub struct FileOwner {
pub kind: FileOwnerKind,
pub id: i64,
}
pub async fn find_file_owner(
pool: &Database,
token: &str,
) -> Result<Option<FileOwner>, sqlx::Error> {
if let Some(id) = sqlx::query_scalar::<_, i64>(queries::get(pool.kind(), queries::Q026))
.bind(token)
.fetch_optional(pool.pool())
.await?
{
return Ok(Some(FileOwner {
kind: FileOwnerKind::Pad,
id,
}));
}
if let Some(id) = sqlx::query_scalar::<_, i64>(queries::get(pool.kind(), queries::Q027))
.bind(token)
.fetch_optional(pool.pool())
.await?
{
return Ok(Some(FileOwner {
kind: FileOwnerKind::Note,
id,
}));
}
Ok(None)
}
#[derive(Debug, Clone, Serialize)]
pub struct NoteFile {
pub id: i64,
pub filename: String,
pub url: String,
pub mime_type: String,
pub size_bytes: i64,
pub created_at: String,
pub is_attached: bool,
pub detached_at: Option<String>,
}
#[derive(Debug, Clone, FromRow)]
struct SqliteNoteFile {
id: i64,
filename: String,
url: String,
mime_type: String,
size_bytes: i64,
created_at: String,
is_attached: i64,
detached_at: Option<String>,
}
impl From<SqliteNoteFile> for NoteFile {
fn from(value: SqliteNoteFile) -> Self {
Self {
id: value.id,
filename: value.filename,
url: value.url,
mime_type: value.mime_type,
size_bytes: value.size_bytes,
created_at: value.created_at,
is_attached: value.is_attached != 0,
detached_at: value.detached_at,
}
}
}
pub async fn delete_note(pool: &Database, note_id: i64) -> Result<(), sqlx::Error> {
sqlx::query(queries::get(pool.kind(), queries::Q031))
.bind(note_id)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn register_note_file(
pool: &Database,
note_id: i64,
filename: &str,
url: &str,
mime_type: &str,
size_bytes: i64,
) -> Result<(), sqlx::Error> {
sqlx::query(queries::get(pool.kind(), queries::Q032))
.bind(note_id)
.bind(filename)
.bind(url)
.bind(mime_type)
.bind(size_bytes)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn list_note_files(pool: &Database, note_id: i64) -> Result<Vec<NoteFile>, sqlx::Error> {
list_files(pool, queries::Q033, note_id).await
}
async fn list_files(
pool: &Database,
query: queries::Query,
owner_id: i64,
) -> Result<Vec<NoteFile>, sqlx::Error> {
if pool.kind() == DatabaseKind::Sqlite {
return Ok(
sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), query))
.bind(owner_id)
.fetch_all(pool.pool())
.await?
.into_iter()
.map(NoteFile::from)
.collect(),
);
}
sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), query))
.bind(owner_id)
.fetch_all(pool.pool())
.await
}
pub async fn set_note_file_attached(
pool: &Database,
file_id: i64,
attached: bool,
) -> Result<(), sqlx::Error> {
let detached_at: Option<String> = if attached {
None
} else {
Some(chrono::Utc::now().to_rfc3339())
};
sqlx::query(queries::get(pool.kind(), queries::Q034))
.bind(attached)
.bind(detached_at)
.bind(file_id)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn register_pad_file(
pool: &Database,
pad_id: i64,
filename: &str,
url: &str,
mime_type: &str,
size_bytes: i64,
) -> Result<(), sqlx::Error> {
sqlx::query(queries::get(pool.kind(), queries::Q035))
.bind(pad_id)
.bind(filename)
.bind(url)
.bind(mime_type)
.bind(size_bytes)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn list_pad_files(pool: &Database, pad_id: i64) -> Result<Vec<NoteFile>, sqlx::Error> {
list_files(pool, queries::Q036, pad_id).await
}
pub async fn set_pad_file_attached(
pool: &Database,
file_id: i64,
attached: bool,
) -> Result<(), sqlx::Error> {
let detached_at: Option<String> = if attached {
None
} else {
Some(chrono::Utc::now().to_rfc3339())
};
sqlx::query(queries::get(pool.kind(), queries::Q037))
.bind(attached)
.bind(detached_at)
.bind(file_id)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn find_note_file(
pool: &Database,
note_id: i64,
file_id: i64,
) -> Result<Option<NoteFile>, sqlx::Error> {
if pool.kind() == DatabaseKind::Sqlite {
return Ok(
sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), queries::Q038))
.bind(file_id)
.bind(note_id)
.fetch_optional(pool.pool())
.await?
.map(NoteFile::from),
);
}
sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), queries::Q038))
.bind(file_id)
.bind(note_id)
.fetch_optional(pool.pool())
.await
}
pub async fn delete_note_file(
pool: &Database,
note_id: i64,
file_id: i64,
) -> Result<(), sqlx::Error> {
sqlx::query(queries::get(pool.kind(), queries::Q039))
.bind(file_id)
.bind(note_id)
.execute(pool.pool())
.await?;
Ok(())
}
pub async fn find_pad_file(
pool: &Database,
pad_id: i64,
file_id: i64,
) -> Result<Option<NoteFile>, sqlx::Error> {
if pool.kind() == DatabaseKind::Sqlite {
return Ok(
sqlx::query_as::<_, SqliteNoteFile>(queries::get(pool.kind(), queries::Q046))
.bind(file_id)
.bind(pad_id)
.fetch_optional(pool.pool())
.await?
.map(NoteFile::from),
);
}
sqlx::query_as::<_, NoteFile>(queries::get(pool.kind(), queries::Q046))
.bind(file_id)
.bind(pad_id)
.fetch_optional(pool.pool())
.await
}
pub async fn delete_pad_file(
pool: &Database,
pad_id: i64,
file_id: i64,
) -> Result<(), sqlx::Error> {
sqlx::query(queries::get(pool.kind(), queries::Q047))
.bind(file_id)
.bind(pad_id)
.execute(pool.pool())
.await?;
Ok(())
}
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")?,
})
}
}