fix in smtp and split rs files

This commit is contained in:
Mateusz Gruszczyński
2026-07-27 23:02:20 +02:00
parent d6c1c52310
commit e7a580a278
15 changed files with 1022 additions and 341 deletions
-1
View File
@@ -20,7 +20,6 @@ const MIN_PASSWORD_LENGTH: usize = 8;
const MAX_PASSWORD_LENGTH: usize = 128; const MAX_PASSWORD_LENGTH: usize = 128;
const MIN_WORKSPACE_SLUG_LENGTH: usize = 6; const MIN_WORKSPACE_SLUG_LENGTH: usize = 6;
include!("workspace_notes.rs"); include!("workspace_notes.rs");
include!("pads_public.rs"); include!("pads_public.rs");
include!("files.rs"); include!("files.rs");
+1 -2
View File
@@ -1,6 +1,5 @@
mod pages; mod pages;
use pages::*;
use axum::{ use axum::{
Router, Router,
extract::{DefaultBodyLimit, Request}, extract::{DefaultBodyLimit, Request},
@@ -9,6 +8,7 @@ use axum::{
response::Response, response::Response,
routing::{get, post}, routing::{get, post},
}; };
use pages::*;
use tower::{ServiceBuilder, service_fn}; use tower::{ServiceBuilder, service_fn};
use tower_http::{services::ServeDir, set_header::SetResponseHeaderLayer, trace::TraceLayer}; use tower_http::{services::ServeDir, set_header::SetResponseHeaderLayer, trace::TraceLayer};
@@ -220,4 +220,3 @@ async fn add_non_asset_security_headers(request: Request, next: Next) -> Respons
response response
} }
+4 -1
View File
@@ -78,7 +78,10 @@ pub(super) async fn pad(State(state): State<SharedState>, Path(slug): Path<Strin
} }
} }
pub(super) async fn public_page(State(state): State<SharedState>, Path(token): Path<String>) -> Response { pub(super) async fn public_page(
State(state): State<SharedState>,
Path(token): Path<String>,
) -> Response {
match db::find_published_page(&state.db, &token).await { match db::find_published_page(&state.db, &token).await {
Ok(Some(_)) => assets::render_html( Ok(Some(_)) => assets::render_html(
include_str!("../../static/public.html"), include_str!("../../static/public.html"),
+10 -2
View File
@@ -172,12 +172,20 @@ pub async fn authenticate(
} }
fn directory_nickname(display_name: &str, email: &str, username: &str) -> String { fn directory_nickname(display_name: &str, email: &str, username: &str) -> String {
let words: Vec<&str> = display_name.split_whitespace().filter(|v| !v.is_empty()).collect(); let words: Vec<&str> = display_name
.split_whitespace()
.filter(|v| !v.is_empty())
.collect();
let candidate = if words.len() >= 2 { let candidate = if words.len() >= 2 {
let first = words[0].chars().next().unwrap_or('u'); let first = words[0].chars().next().unwrap_or('u');
format!("{}.{}", first, words[words.len() - 1]) format!("{}.{}", first, words[words.len() - 1])
} else { } else {
email.split('@').next().filter(|v| !v.is_empty()).unwrap_or(username).to_owned() email
.split('@')
.next()
.filter(|v| !v.is_empty())
.unwrap_or(username)
.to_owned()
}; };
candidate.to_lowercase() candidate.to_lowercase()
} }
+49 -22
View File
@@ -1,8 +1,8 @@
mod smtp; mod smtp;
mod values; mod values;
use std::{net::IpAddr, path::Path};
use smtp::load_smtp; use smtp::load_smtp;
use std::{net::IpAddr, path::Path};
use values::ConfigValues; use values::ConfigValues;
#[derive(Debug, Clone, Copy, PartialEq, Eq)] #[derive(Debug, Clone, Copy, PartialEq, Eq)]
@@ -14,7 +14,12 @@ pub enum AuthorizationType {
impl AuthorizationType { impl AuthorizationType {
fn from_values(values: &ConfigValues) -> Result<Self, Box<dyn std::error::Error>> { fn from_values(values: &ConfigValues) -> Result<Self, Box<dyn std::error::Error>> {
match values.get("AUTHORIZATION_TYPE", "local").trim().to_ascii_lowercase().as_str() { match values
.get("AUTHORIZATION_TYPE", "local")
.trim()
.to_ascii_lowercase()
.as_str()
{
"local" => Ok(Self::Local), "local" => Ok(Self::Local),
"ldap" => Ok(Self::Ldap), "ldap" => Ok(Self::Ldap),
"ad" => Ok(Self::Ad), "ad" => Ok(Self::Ad),
@@ -63,13 +68,22 @@ impl Config {
let port = values.get("APP_PORT", "3000").parse()?; let port = values.get("APP_PORT", "3000").parse()?;
let database_max_connections = values.get("DATABASE_MAX_CONNECTIONS", "8").parse()?; let database_max_connections = values.get("DATABASE_MAX_CONNECTIONS", "8").parse()?;
let upload_max_size_mb: usize = values.get("UPLOAD_MAX_SIZE_MB", "20").parse()?; let upload_max_size_mb: usize = values.get("UPLOAD_MAX_SIZE_MB", "20").parse()?;
let anonymous_access_token_ttl_days = values.positive_i64("ANONYMOUS_ACCESS_TOKEN_TTL_DAYS", 7)?; let anonymous_access_token_ttl_days =
values.positive_i64("ANONYMOUS_ACCESS_TOKEN_TTL_DAYS", 7)?;
let user_session_ttl_days = values.positive_i64("USER_SESSION_TTL_DAYS", 3)?; let user_session_ttl_days = values.positive_i64("USER_SESSION_TTL_DAYS", 3)?;
let unconfirmed_account_ttl_days = values.positive_i64("UNCONFIRMED_ACCOUNT_TTL_DAYS", 3)?; let unconfirmed_account_ttl_days =
values.positive_i64("UNCONFIRMED_ACCOUNT_TTL_DAYS", 3)?;
let files_dir = values.get("FILES_DIR", "data/files"); let files_dir = values.get("FILES_DIR", "data/files");
let storage = match values.get("STORAGE_DRIVER", "local").trim().to_ascii_lowercase().as_str() { let storage = match values
"local" => crate::storage::StorageConfig::Local { root: files_dir.clone().into() }, .get("STORAGE_DRIVER", "local")
.trim()
.to_ascii_lowercase()
.as_str()
{
"local" => crate::storage::StorageConfig::Local {
root: files_dir.clone().into(),
},
"s3" => crate::storage::StorageConfig::S3 { "s3" => crate::storage::StorageConfig::S3 {
endpoint: values.optional("S3_ENDPOINT"), endpoint: values.optional("S3_ENDPOINT"),
region: values.get("S3_REGION", "us-east-1"), region: values.get("S3_REGION", "us-east-1"),
@@ -92,7 +106,10 @@ impl Config {
let context = format!("AUTHORIZATION_TYPE={}", authorization_type.as_str()); let context = format!("AUTHORIZATION_TYPE={}", authorization_type.as_str());
let (default_filter, default_username_attribute) = match authorization_type { let (default_filter, default_username_attribute) = match authorization_type {
AuthorizationType::Ldap => ("(uid={username})", "uid"), AuthorizationType::Ldap => ("(uid={username})", "uid"),
AuthorizationType::Ad => ("(|(sAMAccountName={username})(userPrincipalName={username}))", "sAMAccountName"), AuthorizationType::Ad => (
"(|(sAMAccountName={username})(userPrincipalName={username}))",
"sAMAccountName",
),
AuthorizationType::Local => unreachable!(), AuthorizationType::Local => unreachable!(),
}; };
Some(crate::auth::ldap::LdapConfig { Some(crate::auth::ldap::LdapConfig {
@@ -102,21 +119,28 @@ impl Config {
bind_password: values.get("LDAP_BIND_PASSWORD", ""), bind_password: values.get("LDAP_BIND_PASSWORD", ""),
base_dn: values.required("LDAP_BASE_DN", &context)?, base_dn: values.required("LDAP_BASE_DN", &context)?,
user_filter: values.get("LDAP_USER_FILTER", default_filter), user_filter: values.get("LDAP_USER_FILTER", default_filter),
username_attribute: values.get("LDAP_USERNAME_ATTRIBUTE", default_username_attribute), username_attribute: values
.get("LDAP_USERNAME_ATTRIBUTE", default_username_attribute),
email_attribute: values.get("LDAP_EMAIL_ATTRIBUTE", "mail"), email_attribute: values.get("LDAP_EMAIL_ATTRIBUTE", "mail"),
display_name_attribute: values.get("LDAP_DISPLAY_NAME_ATTRIBUTE", "displayName"), display_name_attribute: values
external_id_attribute: values.get("LDAP_EXTERNAL_ID_ATTRIBUTE", match authorization_type { .get("LDAP_DISPLAY_NAME_ATTRIBUTE", "displayName"),
AuthorizationType::Ldap => "entryUUID", external_id_attribute: values.get(
AuthorizationType::Ad => "objectGUID", "LDAP_EXTERNAL_ID_ATTRIBUTE",
AuthorizationType::Local => unreachable!(), match authorization_type {
}), AuthorizationType::Ldap => "entryUUID",
AuthorizationType::Ad => "objectGUID",
AuthorizationType::Local => unreachable!(),
},
),
organization: values.get("LDAP_ORGANIZATION", "organization"), organization: values.get("LDAP_ORGANIZATION", "organization"),
provider: authorization_type.as_str().to_owned(), provider: authorization_type.as_str().to_owned(),
email_required: values.bool("LDAP_EMAIL_REQUIRED", true)?, email_required: values.bool("LDAP_EMAIL_REQUIRED", true)?,
link_existing_by_email: values.bool("LDAP_LINK_EXISTING_BY_EMAIL", false)?, link_existing_by_email: values.bool("LDAP_LINK_EXISTING_BY_EMAIL", false)?,
tls_verify: values.bool("LDAP_TLS_VERIFY", true)?, tls_verify: values.bool("LDAP_TLS_VERIFY", true)?,
connect_timeout_seconds: values.positive_u64("LDAP_CONNECT_TIMEOUT_SECONDS", 5)?, connect_timeout_seconds: values
operation_timeout_seconds: values.positive_u64("LDAP_OPERATION_TIMEOUT_SECONDS", 10)?, .positive_u64("LDAP_CONNECT_TIMEOUT_SECONDS", 5)?,
operation_timeout_seconds: values
.positive_u64("LDAP_OPERATION_TIMEOUT_SECONDS", 10)?,
}) })
} }
}; };
@@ -131,10 +155,14 @@ impl Config {
static_dir: values.get("STATIC_DIR", "static"), static_dir: values.get("STATIC_DIR", "static"),
files_dir, files_dir,
storage, storage,
upload_max_size_bytes: upload_max_size_mb.checked_mul(1024 * 1024).ok_or("UPLOAD_MAX_SIZE_MB is too large")?, upload_max_size_bytes: upload_max_size_mb
.checked_mul(1024 * 1024)
.ok_or("UPLOAD_MAX_SIZE_MB is too large")?,
asset_version: env!("CARGO_PKG_VERSION").to_owned(), asset_version: env!("CARGO_PKG_VERSION").to_owned(),
asset_cache_max_age_seconds: values.nonnegative_u64("ASSET_CACHE_MAX_AGE_SECONDS", 600)?, asset_cache_max_age_seconds: values
file_cache_max_age_seconds: values.nonnegative_u64("FILE_CACHE_MAX_AGE_SECONDS", 600)?, .nonnegative_u64("ASSET_CACHE_MAX_AGE_SECONDS", 600)?,
file_cache_max_age_seconds: values
.nonnegative_u64("FILE_CACHE_MAX_AGE_SECONDS", 600)?,
smtp, smtp,
registration_enabled: values.bool("REGISTRATION_ENABLED", false)?, registration_enabled: values.bool("REGISTRATION_ENABLED", false)?,
account_confirmation_required: values.bool("ACCOUNT_CONFIRMATION_REQUIRED", false)?, account_confirmation_required: values.bool("ACCOUNT_CONFIRMATION_REQUIRED", false)?,
@@ -165,12 +193,11 @@ impl Config {
return Err("STATIC_DIR and FILES_DIR cannot be empty".into()); return Err("STATIC_DIR and FILES_DIR cannot be empty".into());
} }
if let Some(smtp) = &self.smtp { if let Some(smtp) = &self.smtp {
if !(smtp.public_url.starts_with("http://") || smtp.public_url.starts_with("https://")) { if !(smtp.public_url.starts_with("http://") || smtp.public_url.starts_with("https://"))
{
return Err("PUBLIC_URL must start with http:// or https://".into()); return Err("PUBLIC_URL must start with http:// or https://".into());
} }
} }
Ok(()) Ok(())
} }
} }
+4 -1
View File
@@ -84,7 +84,10 @@ mod tests {
#[test] #[test]
fn accepts_supported_security_modes() { fn accepts_supported_security_modes() {
assert_eq!(parse_smtp_security("none").unwrap(), SmtpSecurity::None); assert_eq!(parse_smtp_security("none").unwrap(), SmtpSecurity::None);
assert_eq!(parse_smtp_security("starttls").unwrap(), SmtpSecurity::StartTls); assert_eq!(
parse_smtp_security("starttls").unwrap(),
SmtpSecurity::StartTls
);
assert_eq!(parse_smtp_security("tls").unwrap(), SmtpSecurity::Tls); assert_eq!(parse_smtp_security("tls").unwrap(), SmtpSecurity::Tls);
assert!(parse_smtp_security("auto").is_err()); assert!(parse_smtp_security("auto").is_err());
} }
+134 -37
View File
@@ -1,19 +1,53 @@
use std::{collections::HashMap, env, path::Path}; use std::{collections::HashMap, env, path::Path};
const KNOWN_CONFIG_KEYS: &[&str] = &[ const KNOWN_CONFIG_KEYS: &[&str] = &[
"APP_HOST", "APP_PORT", "DATABASE_URL", "DATABASE_MAX_CONNECTIONS", "APP_HOST",
"STATIC_DIR", "FILES_DIR", "STORAGE_DRIVER", "UPLOAD_MAX_SIZE_MB", "APP_PORT",
"ASSET_CACHE_MAX_AGE_SECONDS", "FILE_CACHE_MAX_AGE_SECONDS", "DATABASE_URL",
"REGISTRATION_ENABLED", "ACCOUNT_CONFIRMATION_REQUIRED", "SHARE_CONFIRMATION_REQUIRED", "DATABASE_MAX_CONNECTIONS",
"FRONTEND_LOG_LEVEL", "ANONYMOUS_ACCESS_TOKEN_TTL_DAYS", "USER_SESSION_TTL_DAYS", "STATIC_DIR",
"UNCONFIRMED_ACCOUNT_TTL_DAYS", "AUTHORIZATION_TYPE", "FILES_DIR",
"S3_ENDPOINT", "S3_REGION", "S3_BUCKET", "S3_ACCESS_KEY", "S3_SECRET_KEY", "STORAGE_DRIVER",
"S3_FORCE_PATH_STYLE", "SMTP_HOST", "SMTP_PORT", "SMTP_SECURITY", "SMTP_USERNAME", "SMTP_PASSWORD", "UPLOAD_MAX_SIZE_MB",
"SMTP_FROM", "PUBLIC_URL", "LDAP_URL", "LDAP_STARTTLS", "LDAP_BIND_DN", "ASSET_CACHE_MAX_AGE_SECONDS",
"LDAP_BIND_PASSWORD", "LDAP_BASE_DN", "LDAP_USER_FILTER", "LDAP_USERNAME_ATTRIBUTE", "FILE_CACHE_MAX_AGE_SECONDS",
"LDAP_EMAIL_ATTRIBUTE", "LDAP_DISPLAY_NAME_ATTRIBUTE", "LDAP_EXTERNAL_ID_ATTRIBUTE", "REGISTRATION_ENABLED",
"LDAP_ORGANIZATION", "LDAP_EMAIL_REQUIRED", "LDAP_LINK_EXISTING_BY_EMAIL", "ACCOUNT_CONFIRMATION_REQUIRED",
"LDAP_TLS_VERIFY", "LDAP_CONNECT_TIMEOUT_SECONDS", "LDAP_OPERATION_TIMEOUT_SECONDS", "SHARE_CONFIRMATION_REQUIRED",
"FRONTEND_LOG_LEVEL",
"ANONYMOUS_ACCESS_TOKEN_TTL_DAYS",
"USER_SESSION_TTL_DAYS",
"UNCONFIRMED_ACCOUNT_TTL_DAYS",
"AUTHORIZATION_TYPE",
"S3_ENDPOINT",
"S3_REGION",
"S3_BUCKET",
"S3_ACCESS_KEY",
"S3_SECRET_KEY",
"S3_FORCE_PATH_STYLE",
"SMTP_HOST",
"SMTP_PORT",
"SMTP_SECURITY",
"SMTP_USERNAME",
"SMTP_PASSWORD",
"SMTP_FROM",
"PUBLIC_URL",
"LDAP_URL",
"LDAP_STARTTLS",
"LDAP_BIND_DN",
"LDAP_BIND_PASSWORD",
"LDAP_BASE_DN",
"LDAP_USER_FILTER",
"LDAP_USERNAME_ATTRIBUTE",
"LDAP_EMAIL_ATTRIBUTE",
"LDAP_DISPLAY_NAME_ATTRIBUTE",
"LDAP_EXTERNAL_ID_ATTRIBUTE",
"LDAP_ORGANIZATION",
"LDAP_EMAIL_REQUIRED",
"LDAP_LINK_EXISTING_BY_EMAIL",
"LDAP_TLS_VERIFY",
"LDAP_CONNECT_TIMEOUT_SECONDS",
"LDAP_OPERATION_TIMEOUT_SECONDS",
]; ];
#[derive(Default)] #[derive(Default)]
@@ -23,7 +57,9 @@ pub(super) struct ConfigValues {
impl ConfigValues { impl ConfigValues {
pub(super) fn load(path: Option<&Path>) -> Result<Self, Box<dyn std::error::Error>> { pub(super) fn load(path: Option<&Path>) -> Result<Self, Box<dyn std::error::Error>> {
let Some(path) = path else { return Ok(Self::default()); }; let Some(path) = path else {
return Ok(Self::default());
};
let content = std::fs::read_to_string(path) let content = std::fs::read_to_string(path)
.map_err(|error| format!("cannot read config file {}: {error}", path.display()))?; .map_err(|error| format!("cannot read config file {}: {error}", path.display()))?;
let file = parse_yaml_config(&content) let file = parse_yaml_config(&content)
@@ -37,26 +73,50 @@ impl ConfigValues {
} }
pub(super) fn get(&self, name: &str, default: &str) -> String { pub(super) fn get(&self, name: &str, default: &str) -> String {
env::var(name).ok().or_else(|| self.file.get(name).cloned()).unwrap_or_else(|| default.to_owned()) env::var(name)
.ok()
.or_else(|| self.file.get(name).cloned())
.unwrap_or_else(|| default.to_owned())
} }
pub(super) fn optional(&self, name: &str) -> Option<String> { pub(super) fn optional(&self, name: &str) -> Option<String> {
env::var(name).ok().or_else(|| self.file.get(name).cloned()).filter(|value| !value.trim().is_empty()) env::var(name)
.ok()
.or_else(|| self.file.get(name).cloned())
.filter(|value| !value.trim().is_empty())
} }
pub(super) fn required(&self, name: &str, context: &str) -> Result<String, Box<dyn std::error::Error>> { pub(super) fn required(
self.optional(name).ok_or_else(|| format!("{name} is required when {context}").into()) &self,
name: &str,
context: &str,
) -> Result<String, Box<dyn std::error::Error>> {
self.optional(name)
.ok_or_else(|| format!("{name} is required when {context}").into())
} }
pub(super) fn bool(&self, name: &str, default: bool) -> Result<bool, Box<dyn std::error::Error>> { pub(super) fn bool(
match self.get(name, if default { "true" } else { "false" }).trim().to_ascii_lowercase().as_str() { &self,
name: &str,
default: bool,
) -> Result<bool, Box<dyn std::error::Error>> {
match self
.get(name, if default { "true" } else { "false" })
.trim()
.to_ascii_lowercase()
.as_str()
{
"1" | "true" | "yes" | "on" => Ok(true), "1" | "true" | "yes" | "on" => Ok(true),
"0" | "false" | "no" | "off" => Ok(false), "0" | "false" | "no" | "off" => Ok(false),
_ => Err(format!("{name} must be true or false").into()), _ => Err(format!("{name} must be true or false").into()),
} }
} }
pub(super) fn log_level(&self, name: &str, default: &str) -> Result<String, Box<dyn std::error::Error>> { pub(super) fn log_level(
&self,
name: &str,
default: &str,
) -> Result<String, Box<dyn std::error::Error>> {
let value = self.get(name, default).trim().to_ascii_lowercase(); let value = self.get(name, default).trim().to_ascii_lowercase();
match value.as_str() { match value.as_str() {
"off" | "error" | "warn" | "info" | "debug" => Ok(value), "off" | "error" | "warn" | "info" | "debug" => Ok(value),
@@ -64,20 +124,44 @@ impl ConfigValues {
} }
} }
pub(super) fn positive_i64(&self, name: &str, default: i64) -> Result<i64, Box<dyn std::error::Error>> { pub(super) fn positive_i64(
let value: i64 = self.get(name, &default.to_string()).parse().map_err(|_| format!("{name} must be an integer"))?; &self,
if value <= 0 { return Err(format!("{name} must be greater than 0").into()); } name: &str,
default: i64,
) -> Result<i64, Box<dyn std::error::Error>> {
let value: i64 = self
.get(name, &default.to_string())
.parse()
.map_err(|_| format!("{name} must be an integer"))?;
if value <= 0 {
return Err(format!("{name} must be greater than 0").into());
}
Ok(value) Ok(value)
} }
pub(super) fn positive_u64(&self, name: &str, default: u64) -> Result<u64, Box<dyn std::error::Error>> { pub(super) fn positive_u64(
let value: u64 = self.get(name, &default.to_string()).parse().map_err(|_| format!("{name} must be a non-negative integer"))?; &self,
if value == 0 { return Err(format!("{name} must be greater than 0").into()); } name: &str,
default: u64,
) -> Result<u64, Box<dyn std::error::Error>> {
let value: u64 = self
.get(name, &default.to_string())
.parse()
.map_err(|_| format!("{name} must be a non-negative integer"))?;
if value == 0 {
return Err(format!("{name} must be greater than 0").into());
}
Ok(value) Ok(value)
} }
pub(super) fn nonnegative_u64(&self, name: &str, default: u64) -> Result<u64, Box<dyn std::error::Error>> { pub(super) fn nonnegative_u64(
self.get(name, &default.to_string()).parse().map_err(|_| format!("{name} must be a non-negative integer").into()) &self,
name: &str,
default: u64,
) -> Result<u64, Box<dyn std::error::Error>> {
self.get(name, &default.to_string())
.parse()
.map_err(|_| format!("{name} must be a non-negative integer").into())
} }
} }
@@ -88,7 +172,9 @@ fn parse_yaml_config(content: &str) -> Result<HashMap<String, String>, String> {
for (index, original) in content.lines().enumerate() { for (index, original) in content.lines().enumerate() {
let line_number = index + 1; let line_number = index + 1;
if original.contains('\t') { if original.contains('\t') {
return Err(format!("line {line_number}: tabs are not allowed for indentation")); return Err(format!(
"line {line_number}: tabs are not allowed for indentation"
));
} }
let without_comment = strip_yaml_comment(original); let without_comment = strip_yaml_comment(original);
if without_comment.trim().is_empty() || without_comment.trim() == "---" { if without_comment.trim().is_empty() || without_comment.trim() == "---" {
@@ -100,10 +186,17 @@ fn parse_yaml_config(content: &str) -> Result<HashMap<String, String>, String> {
.split_once(':') .split_once(':')
.ok_or_else(|| format!("line {line_number}: expected key: value"))?; .ok_or_else(|| format!("line {line_number}: expected key: value"))?;
let key = raw_key.trim(); let key = raw_key.trim();
if key.is_empty() || !key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-') { if key.is_empty()
|| !key
.chars()
.all(|c| c.is_ascii_alphanumeric() || c == '_' || c == '-')
{
return Err(format!("line {line_number}: invalid key {key:?}")); return Err(format!("line {line_number}: invalid key {key:?}"));
} }
while sections.last().is_some_and(|(section_indent, _)| *section_indent >= indent) { while sections
.last()
.is_some_and(|(section_indent, _)| *section_indent >= indent)
{
sections.pop(); sections.pop();
} }
let normalized = key.to_ascii_uppercase().replace('-', "_"); let normalized = key.to_ascii_uppercase().replace('-', "_");
@@ -112,14 +205,19 @@ fn parse_yaml_config(content: &str) -> Result<HashMap<String, String>, String> {
sections.push((indent, normalized)); sections.push((indent, normalized));
continue; continue;
} }
if matches!(value.chars().next(), Some('[' | '{' | '|' | '>' | '&' | '*' | '!')) { if matches!(
return Err(format!("line {line_number}: only scalar values and nested mappings are supported")); value.chars().next(),
Some('[' | '{' | '|' | '>' | '&' | '*' | '!')
) {
return Err(format!(
"line {line_number}: only scalar values and nested mappings are supported"
));
} }
let mut path: Vec<&str> = sections.iter().map(|(_, key)| key.as_str()).collect(); let mut path: Vec<&str> = sections.iter().map(|(_, key)| key.as_str()).collect();
path.push(&normalized); path.push(&normalized);
let full_key = path.join("_"); let full_key = path.join("_");
let parsed_value = parse_yaml_scalar(value) let parsed_value =
.map_err(|error| format!("line {line_number}: {error}"))?; parse_yaml_scalar(value).map_err(|error| format!("line {line_number}: {error}"))?;
if output.insert(full_key.clone(), parsed_value).is_some() { if output.insert(full_key.clone(), parsed_value).is_some() {
return Err(format!("line {line_number}: duplicate key {full_key}")); return Err(format!("line {line_number}: duplicate key {full_key}"));
} }
@@ -166,4 +264,3 @@ fn parse_yaml_scalar(value: &str) -> Result<String, String> {
} }
Ok(value.to_owned()) Ok(value.to_owned())
} }
+1 -2
View File
@@ -7,8 +7,7 @@ use chrono::{DateTime, NaiveDateTime, Utc};
use rand_core::{OsRng, RngCore}; use rand_core::{OsRng, RngCore};
use serde::Serialize; use serde::Serialize;
use sqlx::FromRow; use sqlx::FromRow;
use sqlx::{any::AnyRow, Any, Row, Transaction}; use sqlx::{Any, Row, Transaction, any::AnyRow};
include!("workspace_notes.rs"); include!("workspace_notes.rs");
include!("pads_public.rs"); include!("pads_public.rs");
+35 -11
View File
@@ -1,4 +1,3 @@
mod row_decode;
mod api; mod api;
mod app; mod app;
mod assets; mod assets;
@@ -7,6 +6,7 @@ mod config;
mod database; mod database;
mod db; mod db;
mod queries; mod queries;
mod row_decode;
mod state; mod state;
mod storage; mod storage;
mod websocket; mod websocket;
@@ -20,7 +20,6 @@ use tokio::net::TcpListener;
use tracing::{info, warn}; use tracing::{info, warn};
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt}; use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
#[tokio::main] #[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> { async fn main() -> Result<(), Box<dyn std::error::Error>> {
dotenvy::dotenv().ok(); dotenvy::dotenv().ok();
@@ -29,7 +28,13 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let config = Config::load(cli.config.as_deref())?; let config = Config::load(cli.config.as_deref())?;
if matches!(cli.command, Command::CheckConfig) { if matches!(cli.command, Command::CheckConfig) {
println!("configuration is valid{}", cli.config.as_ref().map(|path| format!(" ({})", path.display())).unwrap_or_default()); println!(
"configuration is valid{}",
cli.config
.as_ref()
.map(|path| format!(" ({})", path.display()))
.unwrap_or_default()
);
return Ok(()); return Ok(());
} }
info!( info!(
@@ -99,12 +104,25 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut interval = tokio::time::interval(std::time::Duration::from_secs(24 * 60 * 60)); let mut interval = tokio::time::interval(std::time::Duration::from_secs(24 * 60 * 60));
loop { loop {
interval.tick().await; interval.tick().await;
let cutoff = (chrono::Utc::now() - chrono::Duration::days(cleanup_state.unconfirmed_account_ttl_days)).to_rfc3339(); let cutoff = (chrono::Utc::now()
match sqlx::query(crate::queries::get(cleanup_state.db.kind(), crate::queries::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS)) - chrono::Duration::days(cleanup_state.unconfirmed_account_ttl_days))
.bind(cutoff).execute(cleanup_state.db.pool()).await { .to_rfc3339();
Ok(result) if result.rows_affected() > 0 => info!(deleted = result.rows_affected(), "removed expired unconfirmed accounts"), match sqlx::query(crate::queries::get(
Ok(_) => {}, cleanup_state.db.kind(),
Err(error) => tracing::error!(%error, "failed to remove expired unconfirmed accounts"), crate::queries::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS,
))
.bind(cutoff)
.execute(cleanup_state.db.pool())
.await
{
Ok(result) if result.rows_affected() > 0 => info!(
deleted = result.rows_affected(),
"removed expired unconfirmed accounts"
),
Ok(_) => {}
Err(error) => {
tracing::error!(%error, "failed to remove expired unconfirmed accounts")
}
} }
} }
}); });
@@ -126,7 +144,11 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
enum Command { Run, CheckConfig, Migrate } enum Command {
Run,
CheckConfig,
Migrate,
}
struct Cli { struct Cli {
command: Command, command: Command,
@@ -166,7 +188,9 @@ fn parse_command() -> Result<Cli, Box<dyn std::error::Error>> {
} }
command = Command::Migrate; command = Command::Migrate;
} }
_ if arg.starts_with('-') => return Err(format!("unknown option: {arg}; use --help").into()), _ if arg.starts_with('-') => {
return Err(format!("unknown option: {arg}; use --help").into());
}
_ => return Err(format!("unknown command: {arg}; use --help").into()), _ => return Err(format!("unknown command: {arg}; use --help").into()),
} }
} }
+18 -5
View File
@@ -143,7 +143,8 @@ pub const SQLITE_JOURNAL_WAL: Query = Query::SQLITE_JOURNAL_WAL;
pub const SQLITE_BUSY_TIMEOUT: Query = Query::SQLITE_BUSY_TIMEOUT; pub const SQLITE_BUSY_TIMEOUT: Query = Query::SQLITE_BUSY_TIMEOUT;
pub const SQLITE_LAST_INSERT_ID: Query = Query::SQLITE_LAST_INSERT_ID; pub const SQLITE_LAST_INSERT_ID: Query = Query::SQLITE_LAST_INSERT_ID;
pub const MYSQL_LAST_INSERT_ID: Query = Query::MYSQL_LAST_INSERT_ID; pub const MYSQL_LAST_INSERT_ID: Query = Query::MYSQL_LAST_INSERT_ID;
pub const POSTGRES_NOTE_REVISION_LAST_INSERT_ID: Query = Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID; pub const POSTGRES_NOTE_REVISION_LAST_INSERT_ID: Query =
Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID;
pub const POSTGRES_PAD_REVISION_LAST_INSERT_ID: Query = Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID; pub const POSTGRES_PAD_REVISION_LAST_INSERT_ID: Query = Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID;
pub const AUTH_LATEST_CONFIRMATION_CREATED_AT: Query = Query::AUTH_LATEST_CONFIRMATION_CREATED_AT; pub const AUTH_LATEST_CONFIRMATION_CREATED_AT: Query = Query::AUTH_LATEST_CONFIRMATION_CREATED_AT;
pub const AUTH_UPDATE_NICKNAME: Query = Query::AUTH_UPDATE_NICKNAME; pub const AUTH_UPDATE_NICKNAME: Query = Query::AUTH_UPDATE_NICKNAME;
@@ -157,7 +158,8 @@ pub const AUTH_CONSUME_ACCOUNT_ACTION: Query = Query::AUTH_CONSUME_ACCOUNT_ACTIO
pub const AUTH_UPDATE_EMAIL: Query = Query::AUTH_UPDATE_EMAIL; pub const AUTH_UPDATE_EMAIL: Query = Query::AUTH_UPDATE_EMAIL;
pub const AUTH_DELETE_ACCOUNT_ACTIONS: Query = Query::AUTH_DELETE_ACCOUNT_ACTIONS; pub const AUTH_DELETE_ACCOUNT_ACTIONS: Query = Query::AUTH_DELETE_ACCOUNT_ACTIONS;
pub const AUTH_INSERT_ACCOUNT_ACTION: Query = Query::AUTH_INSERT_ACCOUNT_ACTION; pub const AUTH_INSERT_ACCOUNT_ACTION: Query = Query::AUTH_INSERT_ACCOUNT_ACTION;
pub const AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS: Query = Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS; pub const AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS: Query =
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS;
pub const AUTH_INSERT_USER: Query = Query::AUTH_INSERT_USER; pub const AUTH_INSERT_USER: Query = Query::AUTH_INSERT_USER;
pub const AUTH_INSERT_DIRECTORY_USER: Query = Query::AUTH_INSERT_DIRECTORY_USER; pub const AUTH_INSERT_DIRECTORY_USER: Query = Query::AUTH_INSERT_DIRECTORY_USER;
pub const AUTH_UPDATE_DIRECTORY_USER: Query = Query::AUTH_UPDATE_DIRECTORY_USER; pub const AUTH_UPDATE_DIRECTORY_USER: Query = Query::AUTH_UPDATE_DIRECTORY_USER;
@@ -167,7 +169,8 @@ pub const AUTH_DELETE_USER: Query = Query::AUTH_DELETE_USER;
pub const AUTH_SESSION_EXPIRES_AT: Query = Query::AUTH_SESSION_EXPIRES_AT; pub const AUTH_SESSION_EXPIRES_AT: Query = Query::AUTH_SESSION_EXPIRES_AT;
pub const AUTH_DELETE_SESSION_BY_TOKEN: Query = Query::AUTH_DELETE_SESSION_BY_TOKEN; pub const AUTH_DELETE_SESSION_BY_TOKEN: Query = Query::AUTH_DELETE_SESSION_BY_TOKEN;
pub const AUTH_REFRESH_SESSION: Query = Query::AUTH_REFRESH_SESSION; pub const AUTH_REFRESH_SESSION: Query = Query::AUTH_REFRESH_SESSION;
pub const AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER: Query = Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER; pub const AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER: Query =
Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER;
pub const AUTH_INSERT_CONFIRMATION_TOKEN: Query = Query::AUTH_INSERT_CONFIRMATION_TOKEN; pub const AUTH_INSERT_CONFIRMATION_TOKEN: Query = Query::AUTH_INSERT_CONFIRMATION_TOKEN;
pub const AUTH_FIND_CONFIRMATION_TOKEN: Query = Query::AUTH_FIND_CONFIRMATION_TOKEN; pub const AUTH_FIND_CONFIRMATION_TOKEN: Query = Query::AUTH_FIND_CONFIRMATION_TOKEN;
pub const AUTH_CONFIRM_USER: Query = Query::AUTH_CONFIRM_USER; pub const AUTH_CONFIRM_USER: Query = Query::AUTH_CONFIRM_USER;
@@ -194,7 +197,8 @@ pub const USER_SET_WORKSPACE_PASSWORD: Query = Query::USER_SET_WORKSPACE_PASSWOR
pub const USER_SET_PAD_PASSWORD: Query = Query::USER_SET_PAD_PASSWORD; pub const USER_SET_PAD_PASSWORD: Query = Query::USER_SET_PAD_PASSWORD;
pub const USER_SET_WORKSPACE_PRIVACY: Query = Query::USER_SET_WORKSPACE_PRIVACY; pub const USER_SET_WORKSPACE_PRIVACY: Query = Query::USER_SET_WORKSPACE_PRIVACY;
pub const USER_SET_PAD_PRIVACY: Query = Query::USER_SET_PAD_PRIVACY; pub const USER_SET_PAD_PRIVACY: Query = Query::USER_SET_PAD_PRIVACY;
pub const RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE: Query = Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE; pub const RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE: Query =
Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE;
pub const RESOURCE_ACCESS_TOKENS_INSERT: Query = Query::RESOURCE_ACCESS_TOKENS_INSERT; pub const RESOURCE_ACCESS_TOKENS_INSERT: Query = Query::RESOURCE_ACCESS_TOKENS_INSERT;
pub const RESOURCE_ACCESS_TOKENS_VALID_COUNT: Query = Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT; pub const RESOURCE_ACCESS_TOKENS_VALID_COUNT: Query = Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT;
pub const RESOURCE_PERMISSION_DELETE_USER: Query = Query::RESOURCE_PERMISSION_DELETE_USER; pub const RESOURCE_PERMISSION_DELETE_USER: Query = Query::RESOURCE_PERMISSION_DELETE_USER;
@@ -265,7 +269,16 @@ mod tests {
use super::*; use super::*;
#[test] #[test]
fn every_backend_has_explicit_queries() { fn every_backend_has_explicit_queries() {
for query in [Q001, Q003, Q004, Q011, Q021, Q033, USER_LIST_WORKSPACES, USER_LIST_PADS] { for query in [
Q001,
Q003,
Q004,
Q011,
Q021,
Q033,
USER_LIST_WORKSPACES,
USER_LIST_PADS,
] {
assert!(!get(DatabaseKind::Sqlite, query).is_empty()); assert!(!get(DatabaseKind::Sqlite, query).is_empty());
assert!(!get(DatabaseKind::Postgres, query).is_empty()); assert!(!get(DatabaseKind::Postgres, query).is_empty());
assert!(!get(DatabaseKind::MySql, query).is_empty()); assert!(!get(DatabaseKind::MySql, query).is_empty());
+261 -87
View File
@@ -7,121 +7,295 @@ pub fn get(query: Query) -> &'static str {
Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#, Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#,
Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#, Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#,
Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#, Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#,
Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#, Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => {
Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#, r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = ? ORDER BY created_at DESC LIMIT 1"#, }
Query::AUTH_UPDATE_NICKNAME => r#"UPDATE users SET nickname = ?, nickname_key = ?, updated_at = ? WHERE id = ?"#, Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => {
Query::AUTH_UPDATE_EDITOR_COLOR => r#"UPDATE users SET editor_color = ?, updated_at = ? WHERE id = ?"#, r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#
}
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => {
r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = ? ORDER BY created_at DESC LIMIT 1"#
}
Query::AUTH_UPDATE_NICKNAME => {
r#"UPDATE users SET nickname = ?, nickname_key = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_UPDATE_EDITOR_COLOR => {
r#"UPDATE users SET editor_color = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = ?"#, Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = ?"#,
Query::RESOURCE_COLOR_BY_USER => r#"SELECT color FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#, Query::RESOURCE_COLOR_BY_USER => {
Query::RESOURCE_COLOR_DELETE => r#"DELETE FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#, r#"SELECT color FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#
Query::RESOURCE_COLOR_INSERT => r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES (?, ?, ?, ?)"#, }
Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => r#"SELECT user_id, action, CAST(payload AS CHAR CHARACTER SET utf8mb4) AS payload, expires_at, used_at FROM account_action_tokens WHERE token = ?"#, Query::RESOURCE_COLOR_DELETE => {
Query::AUTH_CONSUME_ACCOUNT_ACTION => r#"UPDATE account_action_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL"#, r#"DELETE FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#
Query::AUTH_UPDATE_EMAIL => r#"UPDATE users SET email = ?, email_key = ?, updated_at = ? WHERE id = ?"#, }
Query::AUTH_DELETE_ACCOUNT_ACTIONS => r#"DELETE FROM account_action_tokens WHERE user_id = ? AND action = ?"#, Query::RESOURCE_COLOR_INSERT => {
Query::AUTH_INSERT_ACCOUNT_ACTION => r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES (?, ?, ?, ?, ?, ?)"#, r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES (?, ?, ?, ?)"#
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < ?"#, }
Query::AUTH_INSERT_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES (?, ?, ?, ?, ?, ?)"#, Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => {
Query::AUTH_INSERT_DIRECTORY_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, r#"SELECT user_id, action, CAST(payload AS CHAR CHARACTER SET utf8mb4) AS payload, expires_at, used_at FROM account_action_tokens WHERE token = ?"#
Query::AUTH_UPDATE_DIRECTORY_USER => r#"UPDATE users SET nickname = ?, nickname_key = ?, email = ?, email_key = ?, auth_provider = ?, external_id = ?, external_dn = ?, directory_display_name = ?, updated_at = ? WHERE id = ?"#, }
Query::AUTH_DIRECTORY_PROFILE_BY_USER => r#"SELECT auth_provider, directory_display_name FROM users WHERE id = ?"#, Query::AUTH_CONSUME_ACCOUNT_ACTION => {
Query::AUTH_USER_BY_EXTERNAL_ID => r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE auth_provider = ? AND external_id = ?"#, r#"UPDATE account_action_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL"#
}
Query::AUTH_UPDATE_EMAIL => {
r#"UPDATE users SET email = ?, email_key = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_DELETE_ACCOUNT_ACTIONS => {
r#"DELETE FROM account_action_tokens WHERE user_id = ? AND action = ?"#
}
Query::AUTH_INSERT_ACCOUNT_ACTION => {
r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES (?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => {
r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < ?"#
}
Query::AUTH_INSERT_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES (?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_INSERT_DIRECTORY_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_UPDATE_DIRECTORY_USER => {
r#"UPDATE users SET nickname = ?, nickname_key = ?, email = ?, email_key = ?, auth_provider = ?, external_id = ?, external_dn = ?, directory_display_name = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_DIRECTORY_PROFILE_BY_USER => {
r#"SELECT auth_provider, directory_display_name FROM users WHERE id = ?"#
}
Query::AUTH_USER_BY_EXTERNAL_ID => {
r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE auth_provider = ? AND external_id = ?"#
}
Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = ?"#, Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = ?"#,
Query::AUTH_SESSION_EXPIRES_AT => r#"SELECT expires_at FROM user_sessions WHERE token = ?"#, Query::AUTH_SESSION_EXPIRES_AT => r#"SELECT expires_at FROM user_sessions WHERE token = ?"#,
Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = ?"#, Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = ?"#,
Query::AUTH_REFRESH_SESSION => r#"UPDATE user_sessions SET expires_at = ? WHERE token = ? AND expires_at > ?"#, Query::AUTH_REFRESH_SESSION => {
Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => r#"DELETE FROM account_confirmation_tokens WHERE user_id = ?"#, r#"UPDATE user_sessions SET expires_at = ? WHERE token = ? AND expires_at > ?"#
Query::AUTH_INSERT_CONFIRMATION_TOKEN => r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#, }
Query::AUTH_FIND_CONFIRMATION_TOKEN => r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = ?"#, Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => {
Query::AUTH_CONFIRM_USER => r#"UPDATE users SET confirmed_at = ?, updated_at = ? WHERE id = ?"#, r#"DELETE FROM account_confirmation_tokens WHERE user_id = ?"#
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => r#"UPDATE account_confirmation_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#, }
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => r#"DELETE FROM password_reset_tokens WHERE user_id = ?"#, Query::AUTH_INSERT_CONFIRMATION_TOKEN => {
Query::AUTH_INSERT_RESET_TOKEN => r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#, r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#
Query::AUTH_FIND_RESET_TOKEN => r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = ?"#, }
Query::AUTH_UPDATE_PASSWORD => r#"UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?"#, Query::AUTH_FIND_CONFIRMATION_TOKEN => {
Query::AUTH_CONSUME_RESET_TOKEN => r#"UPDATE password_reset_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#, r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = ?"#
}
Query::AUTH_CONFIRM_USER => {
r#"UPDATE users SET confirmed_at = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => {
r#"UPDATE account_confirmation_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#
}
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => {
r#"DELETE FROM password_reset_tokens WHERE user_id = ?"#
}
Query::AUTH_INSERT_RESET_TOKEN => {
r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#
}
Query::AUTH_FIND_RESET_TOKEN => {
r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = ?"#
}
Query::AUTH_UPDATE_PASSWORD => {
r#"UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_CONSUME_RESET_TOKEN => {
r#"UPDATE password_reset_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#
}
Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = ?"#, Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = ?"#,
Query::AUTH_USER_BY_SESSION => r#"SELECT u.id, u.nickname, u.email, CAST(u.password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > ?"#, Query::AUTH_USER_BY_SESSION => {
Query::AUTH_INSERT_SESSION => r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES (?, ?, ?)"#, r#"SELECT u.id, u.nickname, u.email, CAST(u.password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > ?"#
Query::AUTH_USER_BY_NICKNAME => r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE nickname_key = ?"#, }
Query::AUTH_USER_BY_EMAIL => r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE email_key = ?"#, Query::AUTH_INSERT_SESSION => {
Query::USER_ATTACH_WORKSPACE => r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT ?, id FROM workspaces WHERE slug = ?"#, r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES (?, ?, ?)"#
Query::USER_ATTACH_PAD => r#"INSERT INTO user_pads (user_id, pad_id) SELECT ?, id FROM pads WHERE slug = ?"#, }
Query::USER_LIST_WORKSPACES => r#"SELECT w.slug, CAST(w.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS protected, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? UNION SELECT w.slug, CAST(w.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = ? ORDER BY updated_at DESC"#, Query::AUTH_USER_BY_NICKNAME => {
Query::USER_LIST_PADS => r#"SELECT p.slug, CAST(p.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS protected, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? UNION SELECT p.slug, CAST(p.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = ? ORDER BY updated_at DESC"#, r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE nickname_key = ?"#
Query::USER_OWNS_WORKSPACE => r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? AND w.slug = ?"#, }
Query::USER_OWNS_PAD => r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? AND p.slug = ?"#, Query::AUTH_USER_BY_EMAIL => {
r#"SELECT id, nickname, email, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, confirmed_at FROM users WHERE email_key = ?"#
}
Query::USER_ATTACH_WORKSPACE => {
r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT ?, id FROM workspaces WHERE slug = ?"#
}
Query::USER_ATTACH_PAD => {
r#"INSERT INTO user_pads (user_id, pad_id) SELECT ?, id FROM pads WHERE slug = ?"#
}
Query::USER_LIST_WORKSPACES => {
r#"SELECT w.slug, CAST(w.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS protected, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? UNION SELECT w.slug, CAST(w.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = ? ORDER BY updated_at DESC"#
}
Query::USER_LIST_PADS => {
r#"SELECT p.slug, CAST(p.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS protected, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? UNION SELECT p.slug, CAST(p.title AS CHAR CHARACTER SET utf8mb4) AS title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = ? ORDER BY updated_at DESC"#
}
Query::USER_OWNS_WORKSPACE => {
r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? AND w.slug = ?"#
}
Query::USER_OWNS_PAD => {
r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? AND p.slug = ?"#
}
Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = ?"#, Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = ?"#,
Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = ?"#, Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = ?"#,
Query::USER_SET_WORKSPACE_PASSWORD => r#"UPDATE workspaces SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, Query::USER_SET_WORKSPACE_PASSWORD => {
Query::USER_SET_PAD_PASSWORD => r#"UPDATE pads SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, r#"UPDATE workspaces SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::USER_SET_WORKSPACE_PRIVACY => r#"UPDATE workspaces SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, }
Query::USER_SET_PAD_PRIVACY => r#"UPDATE pads SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, Query::USER_SET_PAD_PASSWORD => {
Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => r#"DELETE FROM resource_access_tokens WHERE resource_kind = ? AND resource_slug = ?"#, r#"UPDATE pads SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::RESOURCE_ACCESS_TOKENS_INSERT => r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES (?, ?, ?, ?)"#, }
Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND expires_at > ?"#, Query::USER_SET_WORKSPACE_PRIVACY => {
Query::RESOURCE_PERMISSION_DELETE_USER => r#"DELETE FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, r#"UPDATE workspaces SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::RESOURCE_PERMISSION_INSERT => r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES (?, ?, ?, ?)"#, }
Query::SHARE_INVITATION_DELETE_USER => r#"DELETE FROM resource_share_invitations WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, Query::USER_SET_PAD_PRIVACY => {
Query::SHARE_INVITATION_INSERT => r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)"#, r#"UPDATE pads SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::SHARE_INVITATION_DELETE_TOKEN => r#"DELETE FROM resource_share_invitations WHERE token_hash = ?"#, }
Query::SHARE_INVITATION_FIND_TOKEN => r#"SELECT resource_kind, resource_slug, user_id, permission, CAST(expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at, CAST(accepted_at AS CHAR CHARACTER SET utf8mb4) AS accepted_at FROM resource_share_invitations WHERE token_hash = ?"#, Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => {
Query::SHARE_INVITATION_ACCEPT => r#"UPDATE resource_share_invitations SET accepted_at = ? WHERE token_hash = ?"#, r#"DELETE FROM resource_access_tokens WHERE resource_kind = ? AND resource_slug = ?"#
Query::RESOURCE_SHARING_USERS => r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = ? AND rp.resource_slug = ? ORDER BY u.email"#, }
Query::RESOURCE_SHARING_LINKS => r#"SELECT token_hash, CAST(token AS CHAR CHARACTER SET utf8mb4) AS token, permission, CAST(expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at, CAST(created_at AS CHAR CHARACTER SET utf8mb4) AS created_at FROM resource_share_links WHERE resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL ORDER BY created_at DESC"#, Query::RESOURCE_ACCESS_TOKENS_INSERT => {
Query::RESOURCE_SHARING_PENDING => r#"SELECT u.email, u.nickname, i.permission, CAST(i.expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = ? AND i.resource_slug = ? AND i.accepted_at IS NULL ORDER BY u.email"#, r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES (?, ?, ?, ?)"#
Query::SHARE_LINK_INSERT => r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)"#, }
Query::SHARE_LINK_UPDATE => r#"UPDATE resource_share_links SET permission = ?, expires_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL"#, Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => {
Query::SHARE_LINK_REVOKE => r#"UPDATE resource_share_links SET revoked_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ?"#, r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND expires_at > ?"#
Query::RESOURCE_PERMISSION_BY_USER => r#"SELECT permission FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, }
Query::SHARE_LINK_PERMISSION => r#"SELECT permission FROM resource_share_links WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)"#, Query::RESOURCE_PERMISSION_DELETE_USER => {
Query::Q001 => r#"SELECT id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM workspaces WHERE slug = ?"#, r#"DELETE FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::RESOURCE_PERMISSION_INSERT => {
r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES (?, ?, ?, ?)"#
}
Query::SHARE_INVITATION_DELETE_USER => {
r#"DELETE FROM resource_share_invitations WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::SHARE_INVITATION_INSERT => {
r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)"#
}
Query::SHARE_INVITATION_DELETE_TOKEN => {
r#"DELETE FROM resource_share_invitations WHERE token_hash = ?"#
}
Query::SHARE_INVITATION_FIND_TOKEN => {
r#"SELECT resource_kind, resource_slug, user_id, permission, CAST(expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at, CAST(accepted_at AS CHAR CHARACTER SET utf8mb4) AS accepted_at FROM resource_share_invitations WHERE token_hash = ?"#
}
Query::SHARE_INVITATION_ACCEPT => {
r#"UPDATE resource_share_invitations SET accepted_at = ? WHERE token_hash = ?"#
}
Query::RESOURCE_SHARING_USERS => {
r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = ? AND rp.resource_slug = ? ORDER BY u.email"#
}
Query::RESOURCE_SHARING_LINKS => {
r#"SELECT token_hash, CAST(token AS CHAR CHARACTER SET utf8mb4) AS token, permission, CAST(expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at, CAST(created_at AS CHAR CHARACTER SET utf8mb4) AS created_at FROM resource_share_links WHERE resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL ORDER BY created_at DESC"#
}
Query::RESOURCE_SHARING_PENDING => {
r#"SELECT u.email, u.nickname, i.permission, CAST(i.expires_at AS CHAR CHARACTER SET utf8mb4) AS expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = ? AND i.resource_slug = ? AND i.accepted_at IS NULL ORDER BY u.email"#
}
Query::SHARE_LINK_INSERT => {
r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)"#
}
Query::SHARE_LINK_UPDATE => {
r#"UPDATE resource_share_links SET permission = ?, expires_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL"#
}
Query::SHARE_LINK_REVOKE => {
r#"UPDATE resource_share_links SET revoked_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ?"#
}
Query::RESOURCE_PERMISSION_BY_USER => {
r#"SELECT permission FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::SHARE_LINK_PERMISSION => {
r#"SELECT permission FROM resource_share_links WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)"#
}
Query::Q001 => {
r#"SELECT id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM workspaces WHERE slug = ?"#
}
Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)"#, Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)"#,
Query::Q003 => r#"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"#, Query::Q003 => {
Query::Q004 => r#"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 = ?"#, r#"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"#
Query::Q005 => r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES (?, ?, ?, ?, ?)"#, }
Query::Q006 => r#"UPDATE notes SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q004 => {
r#"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 = ?"#
}
Query::Q005 => {
r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q006 => {
r#"UPDATE notes SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q007 => r#"UPDATE workspaces SET updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q007 => r#"UPDATE workspaces SET updated_at = CURRENT_TIMESTAMP WHERE id = ?"#,
Query::Q008 => r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#, Query::Q008 => {
r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#
}
Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = ?"#, Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = ?"#,
Query::Q010 => r#"SELECT id, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, CAST(author AS CHAR CHARACTER SET utf8mb4) AS author, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map FROM note_revisions WHERE note_id = ? ORDER BY id DESC LIMIT 100"#, Query::Q010 => {
Query::Q011 => r#"SELECT id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM pads WHERE slug = ?"#, r#"SELECT id, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, CAST(author AS CHAR CHARACTER SET utf8mb4) AS author, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map FROM note_revisions WHERE note_id = ? ORDER BY id DESC LIMIT 100"#
}
Query::Q011 => {
r#"SELECT id, slug, CAST(title AS CHAR CHARACTER SET utf8mb4) AS title, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, CAST(password_hash AS CHAR CHARACTER SET utf8mb4) AS password_hash, created_at, updated_at, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS SIGNED) AS is_private FROM pads WHERE slug = ?"#
}
Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES (?, ?, ?)"#, Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES (?, ?, ?)"#,
Query::Q013 => r#"UPDATE pads SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q013 => {
Query::Q014 => r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#, r#"UPDATE pads SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q014 => {
r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#
}
Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = ?"#, Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = ?"#,
Query::Q016 => r#"SELECT id, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, CAST(author AS CHAR CHARACTER SET utf8mb4) AS author, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map FROM revisions WHERE pad_id = ? ORDER BY id DESC LIMIT 100"#, Query::Q016 => {
r#"SELECT id, CAST(content AS CHAR CHARACTER SET utf8mb4) AS content, created_at, CAST(author AS CHAR CHARACTER SET utf8mb4) AS author, CAST(owner_map AS CHAR CHARACTER SET utf8mb4) AS owner_map FROM revisions WHERE pad_id = ? ORDER BY id DESC LIMIT 100"#
}
Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = ?"#, Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = ?"#,
Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES (?, ?)"#, Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES (?, ?)"#,
Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = ?"#, Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = ?"#,
Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES (?, ?)"#, Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES (?, ?)"#,
Query::Q021 => r#"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 = ?"#, Query::Q021 => {
r#"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 = ?"#
}
Query::Q022 => r#"SELECT file_token FROM pads WHERE id = ?"#, Query::Q022 => r#"SELECT file_token FROM pads WHERE id = ?"#,
Query::Q023 => r#"UPDATE pads SET file_token = ? WHERE id = ? AND file_token IS NULL"#, Query::Q023 => r#"UPDATE pads SET file_token = ? WHERE id = ? AND file_token IS NULL"#,
Query::Q024 => r#"SELECT file_token FROM notes WHERE id = ?"#, Query::Q024 => r#"SELECT file_token FROM notes WHERE id = ?"#,
Query::Q025 => r#"UPDATE notes SET file_token = ? WHERE id = ? AND file_token IS NULL"#, Query::Q025 => r#"UPDATE notes SET file_token = ? WHERE id = ? AND file_token IS NULL"#,
Query::Q026 => r#"SELECT id FROM pads WHERE file_token = ?"#, Query::Q026 => r#"SELECT id FROM pads WHERE file_token = ?"#,
Query::Q027 => r#"SELECT id FROM notes WHERE file_token = ?"#, Query::Q027 => r#"SELECT id FROM notes WHERE file_token = ?"#,
Query::Q028 => r#"SELECT CAST(content AS CHAR CHARACTER SET utf8mb4) FROM note_revisions WHERE id = ? AND note_id = ?"#, Query::Q028 => {
Query::Q029 => r#"SELECT CAST(content AS CHAR CHARACTER SET utf8mb4) FROM revisions WHERE id = ? AND pad_id = ?"#, r#"SELECT CAST(content AS CHAR CHARACTER SET utf8mb4) FROM note_revisions WHERE id = ? AND note_id = ?"#
Query::Q030 => r#"SELECT CAST(owner_map AS CHAR CHARACTER SET utf8mb4) FROM revisions WHERE id = ? AND pad_id = ?"#, }
Query::Q029 => {
r#"SELECT CAST(content AS CHAR CHARACTER SET utf8mb4) FROM revisions WHERE id = ? AND pad_id = ?"#
}
Query::Q030 => {
r#"SELECT CAST(owner_map AS CHAR CHARACTER SET utf8mb4) FROM revisions WHERE id = ? AND pad_id = ?"#
}
Query::Q031 => r#"DELETE FROM notes WHERE id = ?"#, Query::Q031 => r#"DELETE FROM notes WHERE id = ?"#,
Query::Q032 => r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#, Query::Q032 => {
Query::Q033 => r#"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"#, r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q033 => {
r#"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"#
}
Query::Q034 => r#"UPDATE note_files SET is_attached = ?, detached_at = ? WHERE id = ?"#, Query::Q034 => r#"UPDATE note_files SET is_attached = ?, detached_at = ? WHERE id = ?"#,
Query::Q035 => r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#, Query::Q035 => {
Query::Q036 => r#"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"#, r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q036 => {
r#"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"#
}
Query::Q037 => r#"UPDATE pad_files SET is_attached = ?, detached_at = ? WHERE id = ?"#, Query::Q037 => r#"UPDATE pad_files SET is_attached = ?, detached_at = ? WHERE id = ?"#,
Query::Q038 => r#"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 = ?"#, Query::Q038 => {
r#"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 = ?"#
}
Query::Q039 => r#"DELETE FROM note_files WHERE id = ? AND note_id = ?"#, Query::Q039 => r#"DELETE FROM note_files WHERE id = ? AND note_id = ?"#,
Query::Q046 => r#"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 = ?"#, Query::Q046 => {
r#"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 = ?"#
}
Query::Q047 => r#"DELETE FROM pad_files WHERE id = ? AND pad_id = ?"#, Query::Q047 => r#"DELETE FROM pad_files WHERE id = ? AND pad_id = ?"#,
Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE pad_id = ?"#, Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE pad_id = ?"#,
Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE note_id = ?"#, Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE note_id = ?"#,
Query::Q042 => r#"UPDATE pads SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q042 => {
Query::Q043 => r#"UPDATE notes SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, r#"UPDATE pads SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
Query::Q044 => r#"SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE pad_id = ?"#, }
Query::Q045 => r#"SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE note_id = ?"#, Query::Q043 => {
r#"UPDATE notes SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q044 => {
r#"SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE pad_id = ?"#
}
Query::Q045 => {
r#"SELECT CAST(CASE WHEN allow_task_updates THEN 1 ELSE 0 END AS SIGNED) FROM published_pages WHERE note_id = ?"#
}
} }
} }
+252 -84
View File
@@ -7,96 +7,248 @@ pub fn get(query: Query) -> &'static str {
Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#, Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#,
Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#, Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#,
Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#, Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#,
Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#, Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => {
Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#, r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1"#, }
Query::AUTH_UPDATE_NICKNAME => r#"UPDATE users SET nickname = $1, nickname_key = $2, updated_at = $3 WHERE id = $4"#, Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => {
Query::AUTH_UPDATE_EDITOR_COLOR => r#"UPDATE users SET editor_color = $1, updated_at = $2 WHERE id = $3"#, r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#
}
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => {
r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = $1 ORDER BY created_at DESC LIMIT 1"#
}
Query::AUTH_UPDATE_NICKNAME => {
r#"UPDATE users SET nickname = $1, nickname_key = $2, updated_at = $3 WHERE id = $4"#
}
Query::AUTH_UPDATE_EDITOR_COLOR => {
r#"UPDATE users SET editor_color = $1, updated_at = $2 WHERE id = $3"#
}
Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = $1"#, Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = $1"#,
Query::RESOURCE_COLOR_BY_USER => r#"SELECT color FROM user_resource_colors WHERE user_id = $1 AND resource_kind = $2 AND resource_slug = $3"#, Query::RESOURCE_COLOR_BY_USER => {
Query::RESOURCE_COLOR_DELETE => r#"DELETE FROM user_resource_colors WHERE user_id = $1 AND resource_kind = $2 AND resource_slug = $3"#, r#"SELECT color FROM user_resource_colors WHERE user_id = $1 AND resource_kind = $2 AND resource_slug = $3"#
Query::RESOURCE_COLOR_INSERT => r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES ($1, $2, $3, $4)"#, }
Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => r#"SELECT user_id, action, payload, expires_at, used_at FROM account_action_tokens WHERE token = $1"#, Query::RESOURCE_COLOR_DELETE => {
Query::AUTH_CONSUME_ACCOUNT_ACTION => r#"UPDATE account_action_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL"#, r#"DELETE FROM user_resource_colors WHERE user_id = $1 AND resource_kind = $2 AND resource_slug = $3"#
Query::AUTH_UPDATE_EMAIL => r#"UPDATE users SET email = $1, email_key = $2, updated_at = $3 WHERE id = $4"#, }
Query::AUTH_DELETE_ACCOUNT_ACTIONS => r#"DELETE FROM account_action_tokens WHERE user_id = $1 AND action = $2"#, Query::RESOURCE_COLOR_INSERT => {
Query::AUTH_INSERT_ACCOUNT_ACTION => r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES ($1, $2, $3, $4, $5, $6)"#, r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES ($1, $2, $3, $4)"#
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < $1"#, }
Query::AUTH_INSERT_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES ($1, $2, $3, $4, $5, $6)"#, Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => {
Query::AUTH_INSERT_DIRECTORY_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"#, r#"SELECT user_id, action, payload, expires_at, used_at FROM account_action_tokens WHERE token = $1"#
Query::AUTH_UPDATE_DIRECTORY_USER => r#"UPDATE users SET nickname = $1, nickname_key = $2, email = $3, email_key = $4, auth_provider = $5, external_id = $6, external_dn = $7, directory_display_name = $8, updated_at = $9 WHERE id = $10"#, }
Query::AUTH_DIRECTORY_PROFILE_BY_USER => r#"SELECT auth_provider, directory_display_name FROM users WHERE id = $1"#, Query::AUTH_CONSUME_ACCOUNT_ACTION => {
Query::AUTH_USER_BY_EXTERNAL_ID => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE auth_provider = $1 AND external_id = $2"#, r#"UPDATE account_action_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL"#
}
Query::AUTH_UPDATE_EMAIL => {
r#"UPDATE users SET email = $1, email_key = $2, updated_at = $3 WHERE id = $4"#
}
Query::AUTH_DELETE_ACCOUNT_ACTIONS => {
r#"DELETE FROM account_action_tokens WHERE user_id = $1 AND action = $2"#
}
Query::AUTH_INSERT_ACCOUNT_ACTION => {
r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES ($1, $2, $3, $4, $5, $6)"#
}
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => {
r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < $1"#
}
Query::AUTH_INSERT_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES ($1, $2, $3, $4, $5, $6)"#
}
Query::AUTH_INSERT_DIRECTORY_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)"#
}
Query::AUTH_UPDATE_DIRECTORY_USER => {
r#"UPDATE users SET nickname = $1, nickname_key = $2, email = $3, email_key = $4, auth_provider = $5, external_id = $6, external_dn = $7, directory_display_name = $8, updated_at = $9 WHERE id = $10"#
}
Query::AUTH_DIRECTORY_PROFILE_BY_USER => {
r#"SELECT auth_provider, directory_display_name FROM users WHERE id = $1"#
}
Query::AUTH_USER_BY_EXTERNAL_ID => {
r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE auth_provider = $1 AND external_id = $2"#
}
Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = $1"#, Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = $1"#,
Query::AUTH_SESSION_EXPIRES_AT => r#"SELECT expires_at FROM user_sessions WHERE token = $1"#, Query::AUTH_SESSION_EXPIRES_AT => {
r#"SELECT expires_at FROM user_sessions WHERE token = $1"#
}
Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = $1"#, Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = $1"#,
Query::AUTH_REFRESH_SESSION => r#"UPDATE user_sessions SET expires_at = $1 WHERE token = $2 AND expires_at > $3"#, Query::AUTH_REFRESH_SESSION => {
Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => r#"DELETE FROM account_confirmation_tokens WHERE user_id = $1"#, r#"UPDATE user_sessions SET expires_at = $1 WHERE token = $2 AND expires_at > $3"#
Query::AUTH_INSERT_CONFIRMATION_TOKEN => r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES ($1, $2, $3)"#, }
Query::AUTH_FIND_CONFIRMATION_TOKEN => r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = $1"#, Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => {
Query::AUTH_CONFIRM_USER => r#"UPDATE users SET confirmed_at = $1, updated_at = $2 WHERE id = $3"#, r#"DELETE FROM account_confirmation_tokens WHERE user_id = $1"#
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => r#"UPDATE account_confirmation_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL AND expires_at > $3"#, }
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => r#"DELETE FROM password_reset_tokens WHERE user_id = $1"#, Query::AUTH_INSERT_CONFIRMATION_TOKEN => {
Query::AUTH_INSERT_RESET_TOKEN => r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES ($1, $2, $3)"#, r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES ($1, $2, $3)"#
Query::AUTH_FIND_RESET_TOKEN => r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = $1"#, }
Query::AUTH_UPDATE_PASSWORD => r#"UPDATE users SET password_hash = $1, updated_at = $2 WHERE id = $3"#, Query::AUTH_FIND_CONFIRMATION_TOKEN => {
Query::AUTH_CONSUME_RESET_TOKEN => r#"UPDATE password_reset_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL AND expires_at > $3"#, r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = $1"#
}
Query::AUTH_CONFIRM_USER => {
r#"UPDATE users SET confirmed_at = $1, updated_at = $2 WHERE id = $3"#
}
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => {
r#"UPDATE account_confirmation_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL AND expires_at > $3"#
}
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => {
r#"DELETE FROM password_reset_tokens WHERE user_id = $1"#
}
Query::AUTH_INSERT_RESET_TOKEN => {
r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES ($1, $2, $3)"#
}
Query::AUTH_FIND_RESET_TOKEN => {
r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = $1"#
}
Query::AUTH_UPDATE_PASSWORD => {
r#"UPDATE users SET password_hash = $1, updated_at = $2 WHERE id = $3"#
}
Query::AUTH_CONSUME_RESET_TOKEN => {
r#"UPDATE password_reset_tokens SET used_at = $1 WHERE token = $2 AND used_at IS NULL AND expires_at > $3"#
}
Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = $1"#, Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = $1"#,
Query::AUTH_USER_BY_SESSION => r#"SELECT u.id, u.nickname, u.email, u.password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = $1 AND s.expires_at > $2"#, Query::AUTH_USER_BY_SESSION => {
Query::AUTH_INSERT_SESSION => r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES ($1, $2, $3)"#, r#"SELECT u.id, u.nickname, u.email, u.password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = $1 AND s.expires_at > $2"#
Query::AUTH_USER_BY_NICKNAME => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE nickname_key = $1"#, }
Query::AUTH_USER_BY_EMAIL => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE email_key = $1"#, Query::AUTH_INSERT_SESSION => {
Query::USER_ATTACH_WORKSPACE => r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT $1, id FROM workspaces WHERE slug = $2"#, r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES ($1, $2, $3)"#
Query::USER_ATTACH_PAD => r#"INSERT INTO user_pads (user_id, pad_id) SELECT $1, id FROM pads WHERE slug = $2"#, }
Query::USER_LIST_WORKSPACES => r#"SELECT w.slug, w.title, CAST(CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT) AS protected, w.updated_at, CAST(CASE WHEN w.is_private THEN 1 ELSE 0 END AS BIGINT) AS private, CAST(1 AS BIGINT) AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = $1 UNION SELECT w.slug, w.title, CAST(CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT), w.updated_at, CAST(CASE WHEN w.is_private THEN 1 ELSE 0 END AS BIGINT), CAST(0 AS BIGINT), rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = $2 ORDER BY updated_at DESC"#, Query::AUTH_USER_BY_NICKNAME => {
Query::USER_LIST_PADS => r#"SELECT p.slug, p.title, CAST(CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT) AS protected, p.updated_at, CAST(CASE WHEN p.is_private THEN 1 ELSE 0 END AS BIGINT) AS private, CAST(1 AS BIGINT) AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = $1 UNION SELECT p.slug, p.title, CAST(CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT), p.updated_at, CAST(CASE WHEN p.is_private THEN 1 ELSE 0 END AS BIGINT), CAST(0 AS BIGINT), rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = $2 ORDER BY updated_at DESC"#, r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE nickname_key = $1"#
Query::USER_OWNS_WORKSPACE => r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = $1 AND w.slug = $2"#, }
Query::USER_OWNS_PAD => r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = $1 AND p.slug = $2"#, Query::AUTH_USER_BY_EMAIL => {
r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE email_key = $1"#
}
Query::USER_ATTACH_WORKSPACE => {
r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT $1, id FROM workspaces WHERE slug = $2"#
}
Query::USER_ATTACH_PAD => {
r#"INSERT INTO user_pads (user_id, pad_id) SELECT $1, id FROM pads WHERE slug = $2"#
}
Query::USER_LIST_WORKSPACES => {
r#"SELECT w.slug, w.title, CAST(CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT) AS protected, w.updated_at, CAST(CASE WHEN w.is_private THEN 1 ELSE 0 END AS BIGINT) AS private, CAST(1 AS BIGINT) AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = $1 UNION SELECT w.slug, w.title, CAST(CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT), w.updated_at, CAST(CASE WHEN w.is_private THEN 1 ELSE 0 END AS BIGINT), CAST(0 AS BIGINT), rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = $2 ORDER BY updated_at DESC"#
}
Query::USER_LIST_PADS => {
r#"SELECT p.slug, p.title, CAST(CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT) AS protected, p.updated_at, CAST(CASE WHEN p.is_private THEN 1 ELSE 0 END AS BIGINT) AS private, CAST(1 AS BIGINT) AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = $1 UNION SELECT p.slug, p.title, CAST(CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS BIGINT), p.updated_at, CAST(CASE WHEN p.is_private THEN 1 ELSE 0 END AS BIGINT), CAST(0 AS BIGINT), rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = $2 ORDER BY updated_at DESC"#
}
Query::USER_OWNS_WORKSPACE => {
r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = $1 AND w.slug = $2"#
}
Query::USER_OWNS_PAD => {
r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = $1 AND p.slug = $2"#
}
Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = $1"#, Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = $1"#,
Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = $1"#, Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = $1"#,
Query::USER_SET_WORKSPACE_PASSWORD => r#"UPDATE workspaces SET password_hash = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#, Query::USER_SET_WORKSPACE_PASSWORD => {
Query::USER_SET_PAD_PASSWORD => r#"UPDATE pads SET password_hash = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#, r#"UPDATE workspaces SET password_hash = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#
Query::USER_SET_WORKSPACE_PRIVACY => r#"UPDATE workspaces SET is_private = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#, }
Query::USER_SET_PAD_PRIVACY => r#"UPDATE pads SET is_private = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#, Query::USER_SET_PAD_PASSWORD => {
Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => r#"DELETE FROM resource_access_tokens WHERE resource_kind = $1 AND resource_slug = $2"#, r#"UPDATE pads SET password_hash = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#
Query::RESOURCE_ACCESS_TOKENS_INSERT => r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES ($1, $2, $3, $4)"#, }
Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = $1 AND resource_kind = $2 AND resource_slug = $3 AND expires_at > $4"#, Query::USER_SET_WORKSPACE_PRIVACY => {
Query::RESOURCE_PERMISSION_DELETE_USER => r#"DELETE FROM resource_permissions WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#, r#"UPDATE workspaces SET is_private = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#
Query::RESOURCE_PERMISSION_INSERT => r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES ($1, $2, $3, $4)"#, }
Query::SHARE_INVITATION_DELETE_USER => r#"DELETE FROM resource_share_invitations WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#, Query::USER_SET_PAD_PRIVACY => {
Query::SHARE_INVITATION_INSERT => r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7)"#, r#"UPDATE pads SET is_private = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE slug = $2"#
Query::SHARE_INVITATION_DELETE_TOKEN => r#"DELETE FROM resource_share_invitations WHERE token_hash = $1"#, }
Query::SHARE_INVITATION_FIND_TOKEN => r#"SELECT resource_kind, resource_slug, user_id, permission, expires_at, accepted_at FROM resource_share_invitations WHERE token_hash = $1"#, Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => {
Query::SHARE_INVITATION_ACCEPT => r#"UPDATE resource_share_invitations SET accepted_at = $1 WHERE token_hash = $2"#, r#"DELETE FROM resource_access_tokens WHERE resource_kind = $1 AND resource_slug = $2"#
Query::RESOURCE_SHARING_USERS => r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = $1 AND rp.resource_slug = $2 ORDER BY u.email"#, }
Query::RESOURCE_SHARING_LINKS => r#"SELECT token_hash, token, permission, expires_at, created_at FROM resource_share_links WHERE resource_kind = $1 AND resource_slug = $2 AND revoked_at IS NULL ORDER BY created_at DESC"#, Query::RESOURCE_ACCESS_TOKENS_INSERT => {
Query::RESOURCE_SHARING_PENDING => r#"SELECT u.email, u.nickname, i.permission, i.expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = $1 AND i.resource_slug = $2 AND i.accepted_at IS NULL ORDER BY u.email"#, r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES ($1, $2, $3, $4)"#
Query::SHARE_LINK_INSERT => r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES ($1, $2, $3, $4, $5, $6, $7)"#, }
Query::SHARE_LINK_UPDATE => r#"UPDATE resource_share_links SET permission = $1, expires_at = $2 WHERE token_hash = $3 AND resource_kind = $4 AND resource_slug = $5 AND revoked_at IS NULL"#, Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => {
Query::SHARE_LINK_REVOKE => r#"UPDATE resource_share_links SET revoked_at = $1 WHERE token_hash = $2 AND resource_kind = $3 AND resource_slug = $4"#, r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = $1 AND resource_kind = $2 AND resource_slug = $3 AND expires_at > $4"#
Query::RESOURCE_PERMISSION_BY_USER => r#"SELECT permission FROM resource_permissions WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#, }
Query::SHARE_LINK_PERMISSION => r#"SELECT permission FROM resource_share_links WHERE token_hash = $1 AND resource_kind = $2 AND resource_slug = $3 AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > $4)"#, Query::RESOURCE_PERMISSION_DELETE_USER => {
Query::Q001 => r#"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 = $1"#, r#"DELETE FROM resource_permissions WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#
}
Query::RESOURCE_PERMISSION_INSERT => {
r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES ($1, $2, $3, $4)"#
}
Query::SHARE_INVITATION_DELETE_USER => {
r#"DELETE FROM resource_share_invitations WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#
}
Query::SHARE_INVITATION_INSERT => {
r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES ($1, $2, $3, $4, $5, $6, $7)"#
}
Query::SHARE_INVITATION_DELETE_TOKEN => {
r#"DELETE FROM resource_share_invitations WHERE token_hash = $1"#
}
Query::SHARE_INVITATION_FIND_TOKEN => {
r#"SELECT resource_kind, resource_slug, user_id, permission, expires_at, accepted_at FROM resource_share_invitations WHERE token_hash = $1"#
}
Query::SHARE_INVITATION_ACCEPT => {
r#"UPDATE resource_share_invitations SET accepted_at = $1 WHERE token_hash = $2"#
}
Query::RESOURCE_SHARING_USERS => {
r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = $1 AND rp.resource_slug = $2 ORDER BY u.email"#
}
Query::RESOURCE_SHARING_LINKS => {
r#"SELECT token_hash, token, permission, expires_at, created_at FROM resource_share_links WHERE resource_kind = $1 AND resource_slug = $2 AND revoked_at IS NULL ORDER BY created_at DESC"#
}
Query::RESOURCE_SHARING_PENDING => {
r#"SELECT u.email, u.nickname, i.permission, i.expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = $1 AND i.resource_slug = $2 AND i.accepted_at IS NULL ORDER BY u.email"#
}
Query::SHARE_LINK_INSERT => {
r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES ($1, $2, $3, $4, $5, $6, $7)"#
}
Query::SHARE_LINK_UPDATE => {
r#"UPDATE resource_share_links SET permission = $1, expires_at = $2 WHERE token_hash = $3 AND resource_kind = $4 AND resource_slug = $5 AND revoked_at IS NULL"#
}
Query::SHARE_LINK_REVOKE => {
r#"UPDATE resource_share_links SET revoked_at = $1 WHERE token_hash = $2 AND resource_kind = $3 AND resource_slug = $4"#
}
Query::RESOURCE_PERMISSION_BY_USER => {
r#"SELECT permission FROM resource_permissions WHERE resource_kind = $1 AND resource_slug = $2 AND user_id = $3"#
}
Query::SHARE_LINK_PERMISSION => {
r#"SELECT permission FROM resource_share_links WHERE token_hash = $1 AND resource_kind = $2 AND resource_slug = $3 AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > $4)"#
}
Query::Q001 => {
r#"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 = $1"#
}
Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES ($1, $2, $3)"#, Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES ($1, $2, $3)"#,
Query::Q003 => r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS BIGINT) AS protected, created_by FROM notes WHERE workspace_id = $1 ORDER BY updated_at DESC, id DESC"#, Query::Q003 => {
Query::Q004 => r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS BIGINT) AS protected, created_by FROM notes WHERE workspace_id = $1 AND slug = $2"#, r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS BIGINT) AS protected, created_by FROM notes WHERE workspace_id = $1 ORDER BY updated_at DESC, id DESC"#
Query::Q005 => r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES ($1, $2, $3, $4, $5)"#, }
Query::Q006 => r#"UPDATE notes SET content = $1, owner_map = $2, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $3"#, Query::Q004 => {
Query::Q007 => r#"UPDATE workspaces SET updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $1"#, r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, CAST(CASE WHEN protected THEN 1 ELSE 0 END AS BIGINT) AS protected, created_by FROM notes WHERE workspace_id = $1 AND slug = $2"#
Query::Q008 => r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES ($1, $2, $3, $4)"#, }
Query::Q005 => {
r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES ($1, $2, $3, $4, $5)"#
}
Query::Q006 => {
r#"UPDATE notes SET content = $1, owner_map = $2, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $3"#
}
Query::Q007 => {
r#"UPDATE workspaces SET updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $1"#
}
Query::Q008 => {
r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES ($1, $2, $3, $4)"#
}
Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = $1"#, Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = $1"#,
Query::Q010 => r#"SELECT id, content, created_at, author, owner_map FROM note_revisions WHERE note_id = $1 ORDER BY id DESC LIMIT 100"#, Query::Q010 => {
Query::Q011 => r#"SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM pads WHERE slug = $1"#, r#"SELECT id, content, created_at, author, owner_map FROM note_revisions WHERE note_id = $1 ORDER BY id DESC LIMIT 100"#
}
Query::Q011 => {
r#"SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS BIGINT) AS is_private FROM pads WHERE slug = $1"#
}
Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES ($1, $2, $3)"#, Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES ($1, $2, $3)"#,
Query::Q013 => r#"UPDATE pads SET content = $1, owner_map = $2, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $3"#, Query::Q013 => {
Query::Q014 => r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES ($1, $2, $3, $4)"#, r#"UPDATE pads SET content = $1, owner_map = $2, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $3"#
}
Query::Q014 => {
r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES ($1, $2, $3, $4)"#
}
Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = $1"#, Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = $1"#,
Query::Q016 => r#"SELECT id, content, created_at, author, owner_map FROM revisions WHERE pad_id = $1 ORDER BY id DESC LIMIT 100"#, Query::Q016 => {
r#"SELECT id, content, created_at, author, owner_map FROM revisions WHERE pad_id = $1 ORDER BY id DESC LIMIT 100"#
}
Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = $1"#, Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = $1"#,
Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES ($1, $2)"#, Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES ($1, $2)"#,
Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = $1"#, Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = $1"#,
Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES ($1, $2)"#, Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES ($1, $2)"#,
Query::Q021 => r#"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"#, Query::Q021 => {
r#"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"#
}
Query::Q022 => r#"SELECT file_token FROM pads WHERE id = $1"#, Query::Q022 => r#"SELECT file_token FROM pads WHERE id = $1"#,
Query::Q023 => r#"UPDATE pads SET file_token = $1 WHERE id = $2 AND file_token IS NULL"#, Query::Q023 => r#"UPDATE pads SET file_token = $1 WHERE id = $2 AND file_token IS NULL"#,
Query::Q024 => r#"SELECT file_token FROM notes WHERE id = $1"#, Query::Q024 => r#"SELECT file_token FROM notes WHERE id = $1"#,
@@ -107,20 +259,36 @@ pub fn get(query: Query) -> &'static str {
Query::Q029 => r#"SELECT content FROM revisions WHERE id = $1 AND pad_id = $2"#, Query::Q029 => r#"SELECT content FROM revisions WHERE id = $1 AND pad_id = $2"#,
Query::Q030 => r#"SELECT owner_map FROM revisions WHERE id = $1 AND pad_id = $2"#, Query::Q030 => r#"SELECT owner_map FROM revisions WHERE id = $1 AND pad_id = $2"#,
Query::Q031 => r#"DELETE FROM notes WHERE id = $1"#, Query::Q031 => r#"DELETE FROM notes WHERE id = $1"#,
Query::Q032 => r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES ($1, $2, $3, $4, $5)"#, Query::Q032 => {
Query::Q033 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = $1 ORDER BY id DESC"#, r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES ($1, $2, $3, $4, $5)"#
}
Query::Q033 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = $1 ORDER BY id DESC"#
}
Query::Q034 => r#"UPDATE note_files SET is_attached = $1, detached_at = $2 WHERE id = $3"#, Query::Q034 => r#"UPDATE note_files SET is_attached = $1, detached_at = $2 WHERE id = $3"#,
Query::Q035 => r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES ($1, $2, $3, $4, $5)"#, Query::Q035 => {
Query::Q036 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = $1 ORDER BY id DESC"#, r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES ($1, $2, $3, $4, $5)"#
}
Query::Q036 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = $1 ORDER BY id DESC"#
}
Query::Q037 => r#"UPDATE pad_files SET is_attached = $1, detached_at = $2 WHERE id = $3"#, Query::Q037 => r#"UPDATE pad_files SET is_attached = $1, detached_at = $2 WHERE id = $3"#,
Query::Q038 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = $1 AND note_id = $2"#, Query::Q038 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = $1 AND note_id = $2"#
}
Query::Q039 => r#"DELETE FROM note_files WHERE id = $1 AND note_id = $2"#, Query::Q039 => r#"DELETE FROM note_files WHERE id = $1 AND note_id = $2"#,
Query::Q046 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = $1 AND pad_id = $2"#, Query::Q046 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = $1 AND pad_id = $2"#
}
Query::Q047 => r#"DELETE FROM pad_files WHERE id = $1 AND pad_id = $2"#, Query::Q047 => r#"DELETE FROM pad_files WHERE id = $1 AND pad_id = $2"#,
Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = $1 WHERE pad_id = $2"#, Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = $1 WHERE pad_id = $2"#,
Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = $1 WHERE note_id = $2"#, Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = $1 WHERE note_id = $2"#,
Query::Q042 => r#"UPDATE pads SET content = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $2"#, Query::Q042 => {
Query::Q043 => r#"UPDATE notes SET content = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $2"#, r#"UPDATE pads SET content = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $2"#
}
Query::Q043 => {
r#"UPDATE notes SET content = $1, updated_at = (CURRENT_TIMESTAMP::text) WHERE id = $2"#
}
Query::Q044 => r#"SELECT allow_task_updates FROM published_pages WHERE pad_id = $1"#, Query::Q044 => r#"SELECT allow_task_updates FROM published_pages WHERE pad_id = $1"#,
Query::Q045 => r#"SELECT allow_task_updates FROM published_pages WHERE note_id = $1"#, Query::Q045 => r#"SELECT allow_task_updates FROM published_pages WHERE note_id = $1"#,
} }
+252 -84
View File
@@ -7,96 +7,244 @@ pub fn get(query: Query) -> &'static str {
Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#, Query::SQLITE_BUSY_TIMEOUT => r#"PRAGMA busy_timeout = 5000"#,
Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#, Query::SQLITE_LAST_INSERT_ID => r#"SELECT last_insert_rowid()"#,
Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#, Query::MYSQL_LAST_INSERT_ID => r#"SELECT LAST_INSERT_ID()"#,
Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#, Query::POSTGRES_NOTE_REVISION_LAST_INSERT_ID => {
Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#, r#"SELECT currval(pg_get_serial_sequence('note_revisions', 'id'))"#
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = ? ORDER BY created_at DESC LIMIT 1"#, }
Query::AUTH_UPDATE_NICKNAME => r#"UPDATE users SET nickname = ?, nickname_key = ?, updated_at = ? WHERE id = ?"#, Query::POSTGRES_PAD_REVISION_LAST_INSERT_ID => {
Query::AUTH_UPDATE_EDITOR_COLOR => r#"UPDATE users SET editor_color = ?, updated_at = ? WHERE id = ?"#, r#"SELECT currval(pg_get_serial_sequence('revisions', 'id'))"#
}
Query::AUTH_LATEST_CONFIRMATION_CREATED_AT => {
r#"SELECT created_at FROM account_confirmation_tokens WHERE user_id = ? ORDER BY created_at DESC LIMIT 1"#
}
Query::AUTH_UPDATE_NICKNAME => {
r#"UPDATE users SET nickname = ?, nickname_key = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_UPDATE_EDITOR_COLOR => {
r#"UPDATE users SET editor_color = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = ?"#, Query::AUTH_EDITOR_COLOR_BY_USER => r#"SELECT editor_color FROM users WHERE id = ?"#,
Query::RESOURCE_COLOR_BY_USER => r#"SELECT color FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#, Query::RESOURCE_COLOR_BY_USER => {
Query::RESOURCE_COLOR_DELETE => r#"DELETE FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#, r#"SELECT color FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#
Query::RESOURCE_COLOR_INSERT => r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES (?, ?, ?, ?)"#, }
Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => r#"SELECT user_id, action, payload, expires_at, used_at FROM account_action_tokens WHERE token = ?"#, Query::RESOURCE_COLOR_DELETE => {
Query::AUTH_CONSUME_ACCOUNT_ACTION => r#"UPDATE account_action_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL"#, r#"DELETE FROM user_resource_colors WHERE user_id = ? AND resource_kind = ? AND resource_slug = ?"#
Query::AUTH_UPDATE_EMAIL => r#"UPDATE users SET email = ?, email_key = ?, updated_at = ? WHERE id = ?"#, }
Query::AUTH_DELETE_ACCOUNT_ACTIONS => r#"DELETE FROM account_action_tokens WHERE user_id = ? AND action = ?"#, Query::RESOURCE_COLOR_INSERT => {
Query::AUTH_INSERT_ACCOUNT_ACTION => r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES (?, ?, ?, ?, ?, ?)"#, r#"INSERT INTO user_resource_colors (user_id, resource_kind, resource_slug, color) VALUES (?, ?, ?, ?)"#
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < ?"#, }
Query::AUTH_INSERT_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES (?, ?, ?, ?, ?, ?)"#, Query::AUTH_ACCOUNT_ACTION_BY_TOKEN => {
Query::AUTH_INSERT_DIRECTORY_USER => r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#, r#"SELECT user_id, action, payload, expires_at, used_at FROM account_action_tokens WHERE token = ?"#
Query::AUTH_UPDATE_DIRECTORY_USER => r#"UPDATE users SET nickname = ?, nickname_key = ?, email = ?, email_key = ?, auth_provider = ?, external_id = ?, external_dn = ?, directory_display_name = ?, updated_at = ? WHERE id = ?"#, }
Query::AUTH_DIRECTORY_PROFILE_BY_USER => r#"SELECT auth_provider, directory_display_name FROM users WHERE id = ?"#, Query::AUTH_CONSUME_ACCOUNT_ACTION => {
Query::AUTH_USER_BY_EXTERNAL_ID => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE auth_provider = ? AND external_id = ?"#, r#"UPDATE account_action_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL"#
}
Query::AUTH_UPDATE_EMAIL => {
r#"UPDATE users SET email = ?, email_key = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_DELETE_ACCOUNT_ACTIONS => {
r#"DELETE FROM account_action_tokens WHERE user_id = ? AND action = ?"#
}
Query::AUTH_INSERT_ACCOUNT_ACTION => {
r#"INSERT INTO account_action_tokens (token,user_id,action,payload,expires_at,created_at) VALUES (?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_DELETE_EXPIRED_UNCONFIRMED_USERS => {
r#"DELETE FROM users WHERE confirmed_at IS NULL AND created_at < ?"#
}
Query::AUTH_INSERT_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at) VALUES (?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_INSERT_DIRECTORY_USER => {
r#"INSERT INTO users (nickname, nickname_key, email, email_key, password_hash, confirmed_at, auth_provider, external_id, external_dn, directory_display_name) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)"#
}
Query::AUTH_UPDATE_DIRECTORY_USER => {
r#"UPDATE users SET nickname = ?, nickname_key = ?, email = ?, email_key = ?, auth_provider = ?, external_id = ?, external_dn = ?, directory_display_name = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_DIRECTORY_PROFILE_BY_USER => {
r#"SELECT auth_provider, directory_display_name FROM users WHERE id = ?"#
}
Query::AUTH_USER_BY_EXTERNAL_ID => {
r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE auth_provider = ? AND external_id = ?"#
}
Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = ?"#, Query::AUTH_DELETE_USER => r#"DELETE FROM users WHERE id = ?"#,
Query::AUTH_SESSION_EXPIRES_AT => r#"SELECT expires_at FROM user_sessions WHERE token = ?"#, Query::AUTH_SESSION_EXPIRES_AT => r#"SELECT expires_at FROM user_sessions WHERE token = ?"#,
Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = ?"#, Query::AUTH_DELETE_SESSION_BY_TOKEN => r#"DELETE FROM user_sessions WHERE token = ?"#,
Query::AUTH_REFRESH_SESSION => r#"UPDATE user_sessions SET expires_at = ? WHERE token = ? AND expires_at > ?"#, Query::AUTH_REFRESH_SESSION => {
Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => r#"DELETE FROM account_confirmation_tokens WHERE user_id = ?"#, r#"UPDATE user_sessions SET expires_at = ? WHERE token = ? AND expires_at > ?"#
Query::AUTH_INSERT_CONFIRMATION_TOKEN => r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#, }
Query::AUTH_FIND_CONFIRMATION_TOKEN => r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = ?"#, Query::AUTH_DELETE_CONFIRMATION_TOKENS_BY_USER => {
Query::AUTH_CONFIRM_USER => r#"UPDATE users SET confirmed_at = ?, updated_at = ? WHERE id = ?"#, r#"DELETE FROM account_confirmation_tokens WHERE user_id = ?"#
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => r#"UPDATE account_confirmation_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#, }
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => r#"DELETE FROM password_reset_tokens WHERE user_id = ?"#, Query::AUTH_INSERT_CONFIRMATION_TOKEN => {
Query::AUTH_INSERT_RESET_TOKEN => r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#, r#"INSERT INTO account_confirmation_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#
Query::AUTH_FIND_RESET_TOKEN => r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = ?"#, }
Query::AUTH_UPDATE_PASSWORD => r#"UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?"#, Query::AUTH_FIND_CONFIRMATION_TOKEN => {
Query::AUTH_CONSUME_RESET_TOKEN => r#"UPDATE password_reset_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#, r#"SELECT user_id, expires_at, used_at FROM account_confirmation_tokens WHERE token = ?"#
}
Query::AUTH_CONFIRM_USER => {
r#"UPDATE users SET confirmed_at = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_CONSUME_CONFIRMATION_TOKEN => {
r#"UPDATE account_confirmation_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#
}
Query::AUTH_DELETE_RESET_TOKENS_BY_USER => {
r#"DELETE FROM password_reset_tokens WHERE user_id = ?"#
}
Query::AUTH_INSERT_RESET_TOKEN => {
r#"INSERT INTO password_reset_tokens (token, user_id, expires_at) VALUES (?, ?, ?)"#
}
Query::AUTH_FIND_RESET_TOKEN => {
r#"SELECT user_id, expires_at, used_at FROM password_reset_tokens WHERE token = ?"#
}
Query::AUTH_UPDATE_PASSWORD => {
r#"UPDATE users SET password_hash = ?, updated_at = ? WHERE id = ?"#
}
Query::AUTH_CONSUME_RESET_TOKEN => {
r#"UPDATE password_reset_tokens SET used_at = ? WHERE token = ? AND used_at IS NULL AND expires_at > ?"#
}
Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = ?"#, Query::AUTH_DELETE_SESSIONS_BY_USER => r#"DELETE FROM user_sessions WHERE user_id = ?"#,
Query::AUTH_USER_BY_SESSION => r#"SELECT u.id, u.nickname, u.email, u.password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > ?"#, Query::AUTH_USER_BY_SESSION => {
Query::AUTH_INSERT_SESSION => r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES (?, ?, ?)"#, r#"SELECT u.id, u.nickname, u.email, u.password_hash, u.confirmed_at FROM user_sessions s JOIN users u ON u.id = s.user_id WHERE s.token = ? AND s.expires_at > ?"#
Query::AUTH_USER_BY_NICKNAME => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE nickname_key = ?"#, }
Query::AUTH_USER_BY_EMAIL => r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE email_key = ?"#, Query::AUTH_INSERT_SESSION => {
Query::USER_ATTACH_WORKSPACE => r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT ?, id FROM workspaces WHERE slug = ?"#, r#"INSERT INTO user_sessions (token, user_id, expires_at) VALUES (?, ?, ?)"#
Query::USER_ATTACH_PAD => r#"INSERT INTO user_pads (user_id, pad_id) SELECT ?, id FROM pads WHERE slug = ?"#, }
Query::USER_LIST_WORKSPACES => r#"SELECT w.slug, w.title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS protected, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? UNION SELECT w.slug, w.title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = ? ORDER BY updated_at DESC"#, Query::AUTH_USER_BY_NICKNAME => {
Query::USER_LIST_PADS => r#"SELECT p.slug, p.title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS protected, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? UNION SELECT p.slug, p.title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = ? ORDER BY updated_at DESC"#, r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE nickname_key = ?"#
Query::USER_OWNS_WORKSPACE => r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? AND w.slug = ?"#, }
Query::USER_OWNS_PAD => r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? AND p.slug = ?"#, Query::AUTH_USER_BY_EMAIL => {
r#"SELECT id, nickname, email, password_hash, confirmed_at FROM users WHERE email_key = ?"#
}
Query::USER_ATTACH_WORKSPACE => {
r#"INSERT INTO user_workspaces (user_id, workspace_id) SELECT ?, id FROM workspaces WHERE slug = ?"#
}
Query::USER_ATTACH_PAD => {
r#"INSERT INTO user_pads (user_id, pad_id) SELECT ?, id FROM pads WHERE slug = ?"#
}
Query::USER_LIST_WORKSPACES => {
r#"SELECT w.slug, w.title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END AS protected, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? UNION SELECT w.slug, w.title, CASE WHEN w.password_hash IS NULL THEN 0 ELSE 1 END, w.updated_at, CASE WHEN w.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_workspaces owner_uw JOIN users u ON u.id = owner_uw.user_id WHERE owner_uw.workspace_id = w.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN workspaces w ON w.slug = rp.resource_slug WHERE rp.resource_kind = 'workspace' AND rp.user_id = ? ORDER BY updated_at DESC"#
}
Query::USER_LIST_PADS => {
r#"SELECT p.slug, p.title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END AS protected, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END AS private, 1 AS owned, 'rw' AS permission, '' AS shared_by FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? UNION SELECT p.slug, p.title, CASE WHEN p.password_hash IS NULL THEN 0 ELSE 1 END, p.updated_at, CASE WHEN p.is_private THEN 1 ELSE 0 END, 0, rp.permission, COALESCE((SELECT u.nickname FROM user_pads owner_up JOIN users u ON u.id = owner_up.user_id WHERE owner_up.pad_id = p.id LIMIT 1), 'Unknown user') AS shared_by FROM resource_permissions rp JOIN pads p ON p.slug = rp.resource_slug WHERE rp.resource_kind = 'pad' AND rp.user_id = ? ORDER BY updated_at DESC"#
}
Query::USER_OWNS_WORKSPACE => {
r#"SELECT COUNT(*) FROM user_workspaces uw JOIN workspaces w ON w.id = uw.workspace_id WHERE uw.user_id = ? AND w.slug = ?"#
}
Query::USER_OWNS_PAD => {
r#"SELECT COUNT(*) FROM user_pads up JOIN pads p ON p.id = up.pad_id WHERE up.user_id = ? AND p.slug = ?"#
}
Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = ?"#, Query::USER_DELETE_WORKSPACE => r#"DELETE FROM workspaces WHERE slug = ?"#,
Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = ?"#, Query::USER_DELETE_PAD => r#"DELETE FROM pads WHERE slug = ?"#,
Query::USER_SET_WORKSPACE_PASSWORD => r#"UPDATE workspaces SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, Query::USER_SET_WORKSPACE_PASSWORD => {
Query::USER_SET_PAD_PASSWORD => r#"UPDATE pads SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, r#"UPDATE workspaces SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::USER_SET_WORKSPACE_PRIVACY => r#"UPDATE workspaces SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, }
Query::USER_SET_PAD_PRIVACY => r#"UPDATE pads SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#, Query::USER_SET_PAD_PASSWORD => {
Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => r#"DELETE FROM resource_access_tokens WHERE resource_kind = ? AND resource_slug = ?"#, r#"UPDATE pads SET password_hash = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::RESOURCE_ACCESS_TOKENS_INSERT => r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES (?, ?, ?, ?)"#, }
Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND expires_at > ?"#, Query::USER_SET_WORKSPACE_PRIVACY => {
Query::RESOURCE_PERMISSION_DELETE_USER => r#"DELETE FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, r#"UPDATE workspaces SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::RESOURCE_PERMISSION_INSERT => r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES (?, ?, ?, ?)"#, }
Query::SHARE_INVITATION_DELETE_USER => r#"DELETE FROM resource_share_invitations WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, Query::USER_SET_PAD_PRIVACY => {
Query::SHARE_INVITATION_INSERT => r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)"#, r#"UPDATE pads SET is_private = ?, updated_at = CURRENT_TIMESTAMP WHERE slug = ?"#
Query::SHARE_INVITATION_DELETE_TOKEN => r#"DELETE FROM resource_share_invitations WHERE token_hash = ?"#, }
Query::SHARE_INVITATION_FIND_TOKEN => r#"SELECT resource_kind, resource_slug, user_id, permission, expires_at, accepted_at FROM resource_share_invitations WHERE token_hash = ?"#, Query::RESOURCE_ACCESS_TOKENS_DELETE_BY_RESOURCE => {
Query::SHARE_INVITATION_ACCEPT => r#"UPDATE resource_share_invitations SET accepted_at = ? WHERE token_hash = ?"#, r#"DELETE FROM resource_access_tokens WHERE resource_kind = ? AND resource_slug = ?"#
Query::RESOURCE_SHARING_USERS => r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = ? AND rp.resource_slug = ? ORDER BY u.email"#, }
Query::RESOURCE_SHARING_LINKS => r#"SELECT token_hash, token, permission, expires_at, created_at FROM resource_share_links WHERE resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL ORDER BY created_at DESC"#, Query::RESOURCE_ACCESS_TOKENS_INSERT => {
Query::RESOURCE_SHARING_PENDING => r#"SELECT u.email, u.nickname, i.permission, i.expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = ? AND i.resource_slug = ? AND i.accepted_at IS NULL ORDER BY u.email"#, r#"INSERT INTO resource_access_tokens (token_hash, resource_kind, resource_slug, expires_at) VALUES (?, ?, ?, ?)"#
Query::SHARE_LINK_INSERT => r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)"#, }
Query::SHARE_LINK_UPDATE => r#"UPDATE resource_share_links SET permission = ?, expires_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL"#, Query::RESOURCE_ACCESS_TOKENS_VALID_COUNT => {
Query::SHARE_LINK_REVOKE => r#"UPDATE resource_share_links SET revoked_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ?"#, r#"SELECT COUNT(*) FROM resource_access_tokens WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND expires_at > ?"#
Query::RESOURCE_PERMISSION_BY_USER => r#"SELECT permission FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#, }
Query::SHARE_LINK_PERMISSION => r#"SELECT permission FROM resource_share_links WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)"#, Query::RESOURCE_PERMISSION_DELETE_USER => {
Query::Q001 => r#"SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM workspaces WHERE slug = ?"#, r#"DELETE FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::RESOURCE_PERMISSION_INSERT => {
r#"INSERT INTO resource_permissions (resource_kind, resource_slug, user_id, permission) VALUES (?, ?, ?, ?)"#
}
Query::SHARE_INVITATION_DELETE_USER => {
r#"DELETE FROM resource_share_invitations WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::SHARE_INVITATION_INSERT => {
r#"INSERT INTO resource_share_invitations (token_hash, resource_kind, resource_slug, user_id, permission, created_by, expires_at) VALUES (?, ?, ?, ?, ?, ?, ?)"#
}
Query::SHARE_INVITATION_DELETE_TOKEN => {
r#"DELETE FROM resource_share_invitations WHERE token_hash = ?"#
}
Query::SHARE_INVITATION_FIND_TOKEN => {
r#"SELECT resource_kind, resource_slug, user_id, permission, expires_at, accepted_at FROM resource_share_invitations WHERE token_hash = ?"#
}
Query::SHARE_INVITATION_ACCEPT => {
r#"UPDATE resource_share_invitations SET accepted_at = ? WHERE token_hash = ?"#
}
Query::RESOURCE_SHARING_USERS => {
r#"SELECT u.email, u.nickname, rp.permission FROM resource_permissions rp JOIN users u ON u.id = rp.user_id WHERE rp.resource_kind = ? AND rp.resource_slug = ? ORDER BY u.email"#
}
Query::RESOURCE_SHARING_LINKS => {
r#"SELECT token_hash, token, permission, expires_at, created_at FROM resource_share_links WHERE resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL ORDER BY created_at DESC"#
}
Query::RESOURCE_SHARING_PENDING => {
r#"SELECT u.email, u.nickname, i.permission, i.expires_at FROM resource_share_invitations i JOIN users u ON u.id = i.user_id WHERE i.resource_kind = ? AND i.resource_slug = ? AND i.accepted_at IS NULL ORDER BY u.email"#
}
Query::SHARE_LINK_INSERT => {
r#"INSERT INTO resource_share_links (token_hash, token, resource_kind, resource_slug, permission, expires_at, created_by) VALUES (?, ?, ?, ?, ?, ?, ?)"#
}
Query::SHARE_LINK_UPDATE => {
r#"UPDATE resource_share_links SET permission = ?, expires_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL"#
}
Query::SHARE_LINK_REVOKE => {
r#"UPDATE resource_share_links SET revoked_at = ? WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ?"#
}
Query::RESOURCE_PERMISSION_BY_USER => {
r#"SELECT permission FROM resource_permissions WHERE resource_kind = ? AND resource_slug = ? AND user_id = ?"#
}
Query::SHARE_LINK_PERMISSION => {
r#"SELECT permission FROM resource_share_links WHERE token_hash = ? AND resource_kind = ? AND resource_slug = ? AND revoked_at IS NULL AND (expires_at IS NULL OR expires_at > ?)"#
}
Query::Q001 => {
r#"SELECT id, slug, title, password_hash, created_at, updated_at, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM workspaces WHERE slug = ?"#
}
Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)"#, Query::Q002 => r#"INSERT INTO workspaces (slug, title, password_hash) VALUES (?, ?, ?)"#,
Query::Q003 => r#"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"#, Query::Q003 => {
Query::Q004 => r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? AND slug = ?"#, r#"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"#
Query::Q005 => r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES (?, ?, ?, ?, ?)"#, }
Query::Q006 => r#"UPDATE notes SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q004 => {
r#"SELECT id, workspace_id, slug, title, content, created_at, updated_at, owner_map, protected, created_by FROM notes WHERE workspace_id = ? AND slug = ?"#
}
Query::Q005 => {
r#"INSERT INTO notes (workspace_id, slug, title, protected, created_by) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q006 => {
r#"UPDATE notes SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q007 => r#"UPDATE workspaces SET updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q007 => r#"UPDATE workspaces SET updated_at = CURRENT_TIMESTAMP WHERE id = ?"#,
Query::Q008 => r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#, Query::Q008 => {
r#"INSERT INTO note_revisions (note_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#
}
Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = ?"#, Query::Q009 => r#"SELECT updated_at FROM notes WHERE id = ?"#,
Query::Q010 => r#"SELECT id, content, created_at, author, owner_map FROM note_revisions WHERE note_id = ? ORDER BY id DESC LIMIT 100"#, Query::Q010 => {
Query::Q011 => r#"SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM pads WHERE slug = ?"#, r#"SELECT id, content, created_at, author, owner_map FROM note_revisions WHERE note_id = ? ORDER BY id DESC LIMIT 100"#
}
Query::Q011 => {
r#"SELECT id, slug, title, content, password_hash, created_at, updated_at, owner_map, CAST(CASE WHEN is_private THEN 1 ELSE 0 END AS INTEGER) AS is_private FROM pads WHERE slug = ?"#
}
Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES (?, ?, ?)"#, Query::Q012 => r#"INSERT INTO pads (slug, title, password_hash) VALUES (?, ?, ?)"#,
Query::Q013 => r#"UPDATE pads SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q013 => {
Query::Q014 => r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#, r#"UPDATE pads SET content = ?, owner_map = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q014 => {
r#"INSERT INTO revisions (pad_id, content, author, owner_map) VALUES (?, ?, ?, ?)"#
}
Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = ?"#, Query::Q015 => r#"SELECT updated_at FROM pads WHERE id = ?"#,
Query::Q016 => r#"SELECT id, content, created_at, author, owner_map FROM revisions WHERE pad_id = ? ORDER BY id DESC LIMIT 100"#, Query::Q016 => {
r#"SELECT id, content, created_at, author, owner_map FROM revisions WHERE pad_id = ? ORDER BY id DESC LIMIT 100"#
}
Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = ?"#, Query::Q017 => r#"SELECT token FROM published_pages WHERE pad_id = ?"#,
Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES (?, ?)"#, Query::Q018 => r#"INSERT INTO published_pages (token, pad_id) VALUES (?, ?)"#,
Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = ?"#, Query::Q019 => r#"SELECT token FROM published_pages WHERE note_id = ?"#,
Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES (?, ?)"#, Query::Q020 => r#"INSERT INTO published_pages (token, note_id) VALUES (?, ?)"#,
Query::Q021 => r#"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 = ?"#, Query::Q021 => {
r#"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 = ?"#
}
Query::Q022 => r#"SELECT file_token FROM pads WHERE id = ?"#, Query::Q022 => r#"SELECT file_token FROM pads WHERE id = ?"#,
Query::Q023 => r#"UPDATE pads SET file_token = ? WHERE id = ? AND file_token IS NULL"#, Query::Q023 => r#"UPDATE pads SET file_token = ? WHERE id = ? AND file_token IS NULL"#,
Query::Q024 => r#"SELECT file_token FROM notes WHERE id = ?"#, Query::Q024 => r#"SELECT file_token FROM notes WHERE id = ?"#,
@@ -107,21 +255,41 @@ pub fn get(query: Query) -> &'static str {
Query::Q029 => r#"SELECT content FROM revisions WHERE id = ? AND pad_id = ?"#, Query::Q029 => r#"SELECT content FROM revisions WHERE id = ? AND pad_id = ?"#,
Query::Q030 => r#"SELECT owner_map FROM revisions WHERE id = ? AND pad_id = ?"#, Query::Q030 => r#"SELECT owner_map FROM revisions WHERE id = ? AND pad_id = ?"#,
Query::Q031 => r#"DELETE FROM notes WHERE id = ?"#, Query::Q031 => r#"DELETE FROM notes WHERE id = ?"#,
Query::Q032 => r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#, Query::Q032 => {
Query::Q033 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = ? ORDER BY id DESC"#, r#"INSERT INTO note_files (note_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q033 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE note_id = ? ORDER BY id DESC"#
}
Query::Q034 => r#"UPDATE note_files SET is_attached = ?, detached_at = ? WHERE id = ?"#, Query::Q034 => r#"UPDATE note_files SET is_attached = ?, detached_at = ? WHERE id = ?"#,
Query::Q035 => r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#, Query::Q035 => {
Query::Q036 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = ? ORDER BY id DESC"#, r#"INSERT INTO pad_files (pad_id, filename, url, mime_type, size_bytes) VALUES (?, ?, ?, ?, ?)"#
}
Query::Q036 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE pad_id = ? ORDER BY id DESC"#
}
Query::Q037 => r#"UPDATE pad_files SET is_attached = ?, detached_at = ? WHERE id = ?"#, Query::Q037 => r#"UPDATE pad_files SET is_attached = ?, detached_at = ? WHERE id = ?"#,
Query::Q038 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = ? AND note_id = ?"#, Query::Q038 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM note_files WHERE id = ? AND note_id = ?"#
}
Query::Q039 => r#"DELETE FROM note_files WHERE id = ? AND note_id = ?"#, Query::Q039 => r#"DELETE FROM note_files WHERE id = ? AND note_id = ?"#,
Query::Q046 => r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = ? AND pad_id = ?"#, Query::Q046 => {
r#"SELECT id, filename, url, mime_type, size_bytes, created_at, is_attached, detached_at FROM pad_files WHERE id = ? AND pad_id = ?"#
}
Query::Q047 => r#"DELETE FROM pad_files WHERE id = ? AND pad_id = ?"#, Query::Q047 => r#"DELETE FROM pad_files WHERE id = ? AND pad_id = ?"#,
Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE pad_id = ?"#, Query::Q040 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE pad_id = ?"#,
Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE note_id = ?"#, Query::Q041 => r#"UPDATE published_pages SET allow_task_updates = ? WHERE note_id = ?"#,
Query::Q042 => r#"UPDATE pads SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, Query::Q042 => {
Query::Q043 => r#"UPDATE notes SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#, r#"UPDATE pads SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
Query::Q044 => r#"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE pad_id = ?"#, }
Query::Q045 => r#"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE note_id = ?"#, Query::Q043 => {
r#"UPDATE notes SET content = ?, updated_at = CURRENT_TIMESTAMP WHERE id = ?"#
}
Query::Q044 => {
r#"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE pad_id = ?"#
}
Query::Q045 => {
r#"SELECT CASE WHEN allow_task_updates THEN 1 ELSE 0 END FROM published_pages WHERE note_id = ?"#
}
} }
} }
+1 -1
View File
@@ -1,4 +1,4 @@
use sqlx::{any::AnyRow, ColumnIndex, Error, Row}; use sqlx::{ColumnIndex, Error, Row, any::AnyRow};
pub fn text<I>(row: &AnyRow, index: I) -> Result<String, Error> pub fn text<I>(row: &AnyRow, index: I) -> Result<String, Error>
where where
-1
View File
@@ -14,7 +14,6 @@ use serde::{Deserialize, Serialize};
use std::time::{Duration, Instant}; use std::time::{Duration, Instant};
use tracing::{debug, info, warn}; use tracing::{debug, info, warn};
include!("messages.rs"); include!("messages.rs");
include!("note.rs"); include!("note.rs");
include!("pad.rs"); include!("pad.rs");