use oiseau::{cache::Cache, query_row, query_rows}; use crate::model::{auth::User, links::Link, permissions::FinePermission, Error, Result}; use crate::{auto_method, DataManager}; use oiseau::{PostgresRow, execute, get, params}; impl DataManager { /// Get a [`Link`] from an SQL row. pub(crate) fn get_link_from_row(x: &PostgresRow) -> Link { Link { id: get!(x->0(i64)) as usize, created: get!(x->1(i64)) as usize, owner: get!(x->2(i64)) as usize, label: get!(x->3(String)), href: get!(x->4(String)), upload_id: get!(x->5(i64)) as usize, clicks: get!(x->6(i32)) as usize, position: get!(x->7(i32)) as usize, } } auto_method!(get_link_by_id()@get_link_from_row -> "SELECT * FROM links WHERE id = $1" --name="link" --returns=Link --cache-key-tmpl="atto.link:{}"); /// Get links by `owner`. pub async fn get_links_by_owner(&self, owner: usize) -> Result> { let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = query_rows!( &conn, "SELECT * FROM links WHERE owner = $1 ORDER BY position DESC", &[&(owner as i64)], |x| { Self::get_link_from_row(x) } ); if res.is_err() { return Err(Error::GeneralNotFound("link".to_string())); } Ok(res.unwrap()) } /// Get links by `owner`. pub async fn get_links_by_owner_count(&self, owner: usize) -> Result { let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = query_row!( &conn, "SELECT COUNT(*)::int FROM links WHERE owner = $1", &[&(owner as i64)], |x| Ok(x.get::(0)) ); if res.is_err() { return Err(Error::GeneralNotFound("link".to_string())); } Ok(res.unwrap()) } const MAXIMUM_FREE_LINKS: usize = 10; const MAXIMUM_SUPPORTER_LINKS: usize = 20; /// Create a new link in the database. /// /// # Arguments /// * `data` - a mock [`Link`] object to insert pub async fn create_link(&self, data: Link, user: &User) -> Result { if !user.permissions.check(FinePermission::SUPPORTER) { if (self.get_links_by_owner_count(user.id).await? as usize) >= Self::MAXIMUM_FREE_LINKS { return Err(Error::MiscError( "You already have the maximum number of links you can create".to_string(), )); } } else if !user.permissions.check(FinePermission::MANAGE_USERS) { if (self.get_links_by_owner_count(user.id).await? as usize) >= Self::MAXIMUM_SUPPORTER_LINKS { return Err(Error::MiscError( "You already have the maximum number of links you can create".to_string(), )); } } let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = execute!( &conn, "INSERT INTO links VALUES ($1, $2, $3, $4, $5, $6, $7, $8)", params![ &(data.id as i64), &(data.created as i64), &(data.owner as i64), &data.label, &data.href, &(data.upload_id as i64), &(data.clicks as i32), &(data.position as i32), ] ); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); } Ok(data) } pub async fn delete_link(&self, id: usize) -> Result<()> { let y = self.get_link_by_id(id).await?; let conn = match self.0.connect().await { Ok(c) => c, Err(e) => return Err(Error::DatabaseConnection(e.to_string())), }; let res = execute!(&conn, "DELETE FROM links WHERE id = $1", &[&(id as i64)]); if let Err(e) = res { return Err(Error::DatabaseError(e.to_string())); } // delete upload if y.upload_id != 0 { self.delete_upload(id).await?; } // ... self.0.1.remove(format!("atto.link:{}", id)).await; Ok(()) } auto_method!(update_link_label(&str) -> "UPDATE links SET label = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}"); auto_method!(update_link_href(&str) -> "UPDATE links SET href = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}"); auto_method!(update_link_upload_id(i64) -> "UPDATE links SET upload_id = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}"); auto_method!(update_link_position(i32) -> "UPDATE links SET position = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}"); auto_method!(incr_link_clicks() -> "UPDATE links SET clicks = $1 WHERE id = $2" --cache-key-tmpl="atto.link:{}" --incr); }