aboutsummaryrefslogtreecommitdiff
path: root/logic/src
diff options
context:
space:
mode:
authormetamuffin <metamuffin@disroot.org>2025-05-26 18:24:16 +0200
committermetamuffin <metamuffin@disroot.org>2025-05-26 18:24:16 +0200
commit3b15caade07e8fbe351fed9aceb3f435bf58368e (patch)
treecce91c229b78061ad36f29d76a76d67c3c737c59 /logic/src
parent1eeff5c03e8985d16d4f2b6283741dd82b369bd3 (diff)
downloadjellything-3b15caade07e8fbe351fed9aceb3f435bf58368e.tar
jellything-3b15caade07e8fbe351fed9aceb3f435bf58368e.tar.bz2
jellything-3b15caade07e8fbe351fed9aceb3f435bf58368e.tar.zst
move all direct database access to logic crate
Diffstat (limited to 'logic/src')
-rw-r--r--logic/src/account.rs60
-rw-r--r--logic/src/admin/mod.rs40
-rw-r--r--logic/src/admin/user.rs46
-rw-r--r--logic/src/assets.rs131
-rw-r--r--logic/src/home.rs11
-rw-r--r--logic/src/items.rs6
-rw-r--r--logic/src/lib.rs17
-rw-r--r--logic/src/login.rs10
-rw-r--r--logic/src/node.rs85
-rw-r--r--logic/src/search.rs14
-rw-r--r--logic/src/session.rs19
-rw-r--r--logic/src/stats.rs7
12 files changed, 385 insertions, 61 deletions
diff --git a/logic/src/account.rs b/logic/src/account.rs
new file mode 100644
index 0000000..a352437
--- /dev/null
+++ b/logic/src/account.rs
@@ -0,0 +1,60 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+
+use crate::{DATABASE, login::hash_password, session::Session};
+use anyhow::Result;
+use jellycommon::user::{PlayerKind, Theme, User};
+
+pub fn update_user_password(session: &Session, password: &str) -> Result<()> {
+ DATABASE.update_user(&session.user.name, |user| {
+ user.password = hash_password(&session.user.name, password);
+ Ok(())
+ })?;
+ Ok(())
+}
+pub fn update_user_display_name(session: &Session, display_name: &str) -> Result<()> {
+ DATABASE.update_user(&session.user.name, |user| {
+ user.display_name = display_name.to_owned();
+ Ok(())
+ })?;
+ Ok(())
+}
+pub fn update_user_native_secret(session: &Session, native_secret: &str) -> Result<()> {
+ DATABASE.update_user(&session.user.name, |user| {
+ user.native_secret = native_secret.to_owned();
+ Ok(())
+ })?;
+ Ok(())
+}
+pub fn update_user_theme(session: &Session, theme: Theme) -> Result<()> {
+ DATABASE.update_user(&session.user.name, |user| {
+ user.theme = theme;
+ Ok(())
+ })?;
+ Ok(())
+}
+pub fn update_user_player_preference(
+ session: &Session,
+ player_preference: PlayerKind,
+) -> Result<()> {
+ DATABASE.update_user(&session.user.name, |user| {
+ user.player_preference = player_preference;
+ Ok(())
+ })?;
+ Ok(())
+}
+pub fn register_user(invitation: &str, username: &str, password: &str) -> Result<()> {
+ DATABASE.register_user(
+ &invitation,
+ &username,
+ User {
+ display_name: username.to_owned(),
+ name: username.to_owned(),
+ password: hash_password(&username, &password),
+ ..Default::default()
+ },
+ )
+}
diff --git a/logic/src/admin/mod.rs b/logic/src/admin/mod.rs
index 2545ba4..804cb2b 100644
--- a/logic/src/admin/mod.rs
+++ b/logic/src/admin/mod.rs
@@ -7,14 +7,42 @@
pub mod log;
pub mod user;
-use crate::session::AdminSession;
-use anyhow::Result;
-use jellydb::Database;
-use jellyimport::IMPORT_ERRORS;
+use crate::{DATABASE, session::AdminSession};
+use anyhow::{Result, anyhow};
+use jellyimport::{IMPORT_ERRORS, import_wrap};
+use rand::Rng;
+use std::time::{Duration, Instant};
+use tokio::task::spawn_blocking;
pub async fn get_import_errors(_session: &AdminSession) -> Vec<String> {
IMPORT_ERRORS.read().await.to_owned()
}
-pub fn list_invites(_session: &AdminSession, database: &Database) -> Result<Vec<String>> {
- database.list_invites()
+pub fn list_invites(_session: &AdminSession) -> Result<Vec<String>> {
+ DATABASE.list_invites()
+}
+
+pub fn create_invite(_session: &AdminSession) -> Result<String> {
+ let i = format!("{}", rand::rng().random::<u128>());
+ DATABASE.create_invite(&i)?;
+ Ok(i)
+}
+pub fn delete_invite(_session: &AdminSession, invite: &str) -> Result<()> {
+ if !DATABASE.delete_invite(invite)? {
+ Err(anyhow!("invite does not exist"))?;
+ };
+ Ok(())
+}
+pub async fn update_search_index(_session: &AdminSession) -> Result<()> {
+ spawn_blocking(move || DATABASE.search_create_index()).await?
+}
+pub async fn do_import(
+ _session: &AdminSession,
+ incremental: bool,
+) -> Result<(Duration, Result<()>)> {
+ let t = Instant::now();
+ if !incremental {
+ DATABASE.clear_nodes()?;
+ }
+ let r = import_wrap((*DATABASE).clone(), incremental).await;
+ Ok((t.elapsed(), r))
}
diff --git a/logic/src/admin/user.rs b/logic/src/admin/user.rs
index 3ec3852..e277077 100644
--- a/logic/src/admin/user.rs
+++ b/logic/src/admin/user.rs
@@ -4,14 +4,48 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::session::AdminSession;
-use anyhow::Result;
-use jellycommon::api::ApiAdminUsersResponse;
-use jellydb::Database;
+use crate::{DATABASE, session::AdminSession};
+use anyhow::{Result, anyhow};
+use jellycommon::{
+ api::ApiAdminUsersResponse,
+ user::{User, UserPermission},
+};
-pub fn admin_users(db: &Database, _session: &AdminSession) -> Result<ApiAdminUsersResponse> {
+pub fn admin_users(_session: &AdminSession) -> Result<ApiAdminUsersResponse> {
// TODO dont return useless info like passwords
Ok(ApiAdminUsersResponse {
- users: db.list_users()?,
+ users: DATABASE.list_users()?,
+ })
+}
+pub fn get_user(_session: &AdminSession, username: &str) -> Result<User> {
+ DATABASE
+ .get_user(username)?
+ .ok_or(anyhow!("user not found"))
+}
+pub fn delete_user(_session: &AdminSession, username: &str) -> Result<()> {
+ if !DATABASE.delete_user(&username)? {
+ Err(anyhow!("user did not exist"))?;
+ }
+ Ok(())
+}
+
+pub enum GrantState {
+ Grant,
+ Revoke,
+ Unset,
+}
+pub fn update_user_perms(
+ _session: &AdminSession,
+ username: &str,
+ perm: UserPermission,
+ action: GrantState,
+) -> Result<()> {
+ DATABASE.update_user(username, |user| {
+ match action {
+ GrantState::Grant => drop(user.permissions.0.insert(perm.clone(), true)),
+ GrantState::Revoke => drop(user.permissions.0.insert(perm.clone(), false)),
+ GrantState::Unset => drop(user.permissions.0.remove(&perm)),
+ }
+ Ok(())
})
}
diff --git a/logic/src/assets.rs b/logic/src/assets.rs
new file mode 100644
index 0000000..7be3845
--- /dev/null
+++ b/logic/src/assets.rs
@@ -0,0 +1,131 @@
+/*
+ This file is part of jellything (https://codeberg.org/metamuffin/jellything)
+ which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
+ Copyright (C) 2025 metamuffin <metamuffin.org>
+*/
+
+use crate::{DATABASE, session::Session};
+use anyhow::{Result, anyhow};
+use jellycommon::{Asset, LocalTrack, NodeID, PeopleGroup, SourceTrackKind, TrackSource};
+use jellyimport_asset_token::AssetInner;
+
+pub fn get_node_backdrop(_session: &Session, id: NodeID) -> Result<Asset> {
+ // TODO perm
+ let node = DATABASE
+ .get_node(id)?
+ .ok_or(anyhow!("node does not exist"))?;
+
+ let mut asset = node.backdrop.clone();
+ if asset.is_none() {
+ if let Some(parent) = node.parents.last().copied() {
+ let parent = DATABASE
+ .get_node(parent)?
+ .ok_or(anyhow!("node does not exist"))?;
+ asset = parent.backdrop.clone();
+ }
+ };
+ Ok(asset.unwrap_or_else(|| {
+ AssetInner::Assets(format!("fallback-{:?}.avif", node.kind).into()).ser()
+ }))
+}
+pub fn get_node_poster(_session: &Session, id: NodeID) -> Result<Asset> {
+ // TODO perm
+ let node = DATABASE
+ .get_node(id)?
+ .ok_or(anyhow!("node does not exist"))?;
+
+ let mut asset = node.poster.clone();
+ if asset.is_none() {
+ if let Some(parent) = node.parents.last().copied() {
+ let parent = DATABASE
+ .get_node(parent)?
+ .ok_or(anyhow!("node does not exist"))?;
+ asset = parent.poster.clone();
+ }
+ };
+ Ok(asset.unwrap_or_else(|| {
+ AssetInner::Assets(format!("fallback-{:?}.avif", node.kind).into()).ser()
+ }))
+}
+
+pub fn get_node_person_asset(
+ _session: &Session,
+ id: NodeID,
+ group: PeopleGroup,
+ index: usize,
+) -> Result<Asset> {
+ // TODO perm
+
+ let node = DATABASE
+ .get_node(id)?
+ .ok_or(anyhow!("node does not exist"))?;
+ let app = node
+ .people
+ .get(&group)
+ .ok_or(anyhow!("group has no members"))?
+ .get(index)
+ .ok_or(anyhow!("person does not exist"))?;
+
+ let asset = app
+ .person
+ .headshot
+ .to_owned()
+ .unwrap_or(AssetInner::Assets("fallback-Person.avif".into()).ser());
+
+ Ok(asset)
+}
+
+pub async fn get_node_thumbnail(_session: &Session, id: NodeID, t: f64) -> Result<Asset> {
+ let node = DATABASE
+ .get_node(id)?
+ .ok_or(anyhow!("node does not exist"))?;
+
+ let media = node.media.as_ref().ok_or(anyhow!("no media"))?;
+ let (thumb_track_index, _thumb_track) = media
+ .tracks
+ .iter()
+ .enumerate()
+ .find(|(_i, t)| matches!(t.kind, SourceTrackKind::Video { .. }))
+ .ok_or(anyhow!("no video track to create a thumbnail of"))?;
+ let source = media
+ .tracks
+ .get(thumb_track_index)
+ .ok_or(anyhow!("no source"))?;
+ let thumb_track_source = source.source.clone();
+
+ if t < 0. || t > media.duration {
+ Err(anyhow!("thumbnail instant not within media duration"))?
+ }
+
+ let step = 8.;
+ let t = (t / step).floor() * step;
+
+ let asset = match thumb_track_source {
+ TrackSource::Local(a) => {
+ let AssetInner::LocalTrack(LocalTrack { path, .. }) = AssetInner::deser(&a.0)? else {
+ return Err(anyhow!("track set to wrong asset type").into());
+ };
+ // the track selected might be different from thumb_track
+ jellytranscoder::thumbnail::create_thumbnail(&path, t).await?
+ }
+ TrackSource::Remote(_) => {
+ // // TODO in the new system this is preferrably a property of node ext for regular fed
+ // let session = fed
+ // .get_session(
+ // thumb_track
+ // .federated
+ // .last()
+ // .ok_or(anyhow!("federation broken"))?,
+ // )
+ // .await?;
+
+ // async_cache_file("fed-thumb", (id.0, t as i64), |out| {
+ // session.node_thumbnail(out, id.0.into(), 2048, t)
+ // })
+ // .await?
+ todo!()
+ }
+ };
+
+ Ok(AssetInner::Cache(asset).ser())
+}
diff --git a/logic/src/home.rs b/logic/src/home.rs
index ad3fee5..1957a94 100644
--- a/logic/src/home.rs
+++ b/logic/src/home.rs
@@ -4,7 +4,7 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::{node::DatabaseNodeUserDataExt, session::Session};
+use crate::{DATABASE, node::DatabaseNodeUserDataExt, session::Session};
use anyhow::{Context, Result};
use jellycommon::{
NodeID, NodeKind, Rating, Visibility,
@@ -12,16 +12,15 @@ use jellycommon::{
chrono::{Datelike, Utc},
user::WatchedState,
};
-use jellydb::Database;
-pub fn home(db: &Database, session: &Session) -> Result<ApiHomeResponse> {
- let mut items = db.list_nodes_with_udata(&session.user.name)?;
+pub fn home(session: &Session) -> Result<ApiHomeResponse> {
+ let mut items = DATABASE.list_nodes_with_udata(&session.user.name)?;
- let mut toplevel = db
+ let mut toplevel = DATABASE
.get_node_children(NodeID::from_slug("library"))
.context("root node missing")?
.into_iter()
- .map(|n| db.get_node_with_userdata(n, &session))
+ .map(|n| DATABASE.get_node_with_userdata(n, &session))
.collect::<anyhow::Result<Vec<_>>>()?;
toplevel.sort_by_key(|(n, _)| n.index.unwrap_or(usize::MAX));
diff --git a/logic/src/items.rs b/logic/src/items.rs
index 99fb767..eddfb03 100644
--- a/logic/src/items.rs
+++ b/logic/src/items.rs
@@ -4,21 +4,19 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::{filter_sort::filter_and_sort_nodes, session::Session};
+use crate::{DATABASE, filter_sort::filter_and_sort_nodes, session::Session};
use anyhow::Result;
use jellycommon::{
Visibility,
api::{ApiItemsResponse, NodeFilterSort, SortOrder, SortProperty},
};
-use jellydb::Database;
pub fn all_items(
- db: &Database,
session: &Session,
page: Option<usize>,
filter: NodeFilterSort,
) -> Result<ApiItemsResponse> {
- let mut items = db.list_nodes_with_udata(session.user.name.as_str())?;
+ let mut items = DATABASE.list_nodes_with_udata(session.user.name.as_str())?;
items.retain(|(n, _)| matches!(n.visibility, Visibility::Visible));
diff --git a/logic/src/lib.rs b/logic/src/lib.rs
index 004e008..9988ed2 100644
--- a/logic/src/lib.rs
+++ b/logic/src/lib.rs
@@ -6,6 +6,7 @@
#![feature(duration_constructors, let_chains)]
pub mod admin;
+pub mod assets;
pub mod filter_sort;
pub mod home;
pub mod items;
@@ -14,10 +15,13 @@ pub mod node;
pub mod search;
pub mod session;
pub mod stats;
+pub mod account;
-pub use jellydb::Database;
-
+use anyhow::Context;
+use anyhow::Result;
+use jellydb::Database;
use serde::{Deserialize, Serialize};
+use std::path::PathBuf;
use std::sync::LazyLock;
use std::sync::Mutex;
@@ -28,6 +32,7 @@ pub struct Config {
session_key: Option<String>,
admin_username:Option<String>,
admin_password:Option<String>,
+ database_path: PathBuf,
}
pub static CONF_PRELOAD: Mutex<Option<Config>> = Mutex::new(None);
@@ -47,3 +52,11 @@ static DATABASE: LazyLock<Database> = LazyLock::new(|| {
.take()
.expect("database not preloaded. logic error")
});
+
+pub fn init_database() -> Result<()> {
+ let database = Database::open(&CONF.database_path)
+ .context("opening database")
+ .unwrap();
+ *DATABASE_PRELOAD.lock().unwrap() = Some(database);
+ Ok(())
+}
diff --git a/logic/src/login.rs b/logic/src/login.rs
index 72a5903..5e255a0 100644
--- a/logic/src/login.rs
+++ b/logic/src/login.rs
@@ -3,19 +3,18 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::{CONF, session::create};
+use crate::{CONF, DATABASE, session::create};
use anyhow::{Result, anyhow};
use argon2::{Argon2, PasswordHasher, password_hash::Salt};
use jellycommon::user::UserPermission;
-use jellydb::Database;
use log::info;
use std::{collections::HashSet, time::Duration};
-pub fn create_admin_account(database: &Database) -> Result<()> {
+pub fn create_admin_account() -> Result<()> {
if let Some(username) = &CONF.admin_username
&& let Some(password) = &CONF.admin_password
{
- database
+ DATABASE
.create_admin_user(username, hash_password(username, password))
.unwrap();
} else {
@@ -25,7 +24,6 @@ pub fn create_admin_account(database: &Database) -> Result<()> {
}
pub fn login_logic(
- database: &Database,
username: &str,
password: &str,
expire: Option<i64>,
@@ -34,7 +32,7 @@ pub fn login_logic(
// hashing the password regardless if the accounts exists to better resist timing attacks
let password = hash_password(username, password);
- let mut user = database
+ let mut user = DATABASE
.get_user(username)?
.ok_or(anyhow!("invalid password"))?;
diff --git a/logic/src/node.rs b/logic/src/node.rs
index c8ff820..820116f 100644
--- a/logic/src/node.rs
+++ b/logic/src/node.rs
@@ -3,30 +3,30 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::{filter_sort::filter_and_sort_nodes, session::Session};
+use crate::{DATABASE, filter_sort::filter_and_sort_nodes, session::Session};
use anyhow::{Result, anyhow};
use jellycommon::{
Node, NodeID, NodeKind, Visibility,
api::{ApiNodeResponse, NodeFilterSort, SortOrder, SortProperty},
- user::NodeUserData,
+ user::{NodeUserData, WatchedState},
};
use jellydb::Database;
use std::{cmp::Reverse, collections::BTreeMap, sync::Arc};
pub fn get_node(
- db: &Database,
- id: NodeID,
session: &Session,
+ id: NodeID,
children: bool,
parents: bool,
filter: NodeFilterSort,
) -> Result<ApiNodeResponse> {
- let (node, udata) = db.get_node_with_userdata(id, &session)?;
+ let (node, udata) = DATABASE.get_node_with_userdata(id, &session)?;
let mut children = if children {
- db.get_node_children(id)?
+ DATABASE
+ .get_node_children(id)?
.into_iter()
- .map(|c| db.get_node_with_userdata(c, &session))
+ .map(|c| DATABASE.get_node_with_userdata(c, &session))
.collect::<anyhow::Result<Vec<_>>>()?
} else {
Vec::new()
@@ -35,13 +35,13 @@ pub fn get_node(
let mut parents = if parents {
node.parents
.iter()
- .map(|pid| db.get_node_with_userdata(*pid, &session))
+ .map(|pid| DATABASE.get_node_with_userdata(*pid, &session))
.collect::<anyhow::Result<Vec<_>>>()?
} else {
Vec::new()
};
- let mut similar = get_similar_media(&node, db, &session)?;
+ let mut similar = get_similar_media(&session, &node)?;
similar.retain(|(n, _)| n.visibility >= Visibility::Reduced);
children.retain(|(n, _)| n.visibility >= Visibility::Reduced);
@@ -65,15 +65,11 @@ pub fn get_node(
})
}
-pub fn get_similar_media(
- node: &Node,
- db: &Database,
- session: &Session,
-) -> Result<Vec<(Arc<Node>, NodeUserData)>> {
+pub fn get_similar_media(session: &Session, node: &Node) -> Result<Vec<(Arc<Node>, NodeUserData)>> {
let this_id = NodeID::from_slug(&node.slug);
let mut ranking = BTreeMap::<NodeID, usize>::new();
for tag in &node.tags {
- let nodes = db.get_tag_nodes(tag)?;
+ let nodes = DATABASE.get_tag_nodes(tag)?;
let weight = 1_000_000 / nodes.len();
for n in nodes {
if n != this_id {
@@ -86,7 +82,7 @@ pub fn get_similar_media(
ranking
.into_iter()
.take(32)
- .map(|(pid, _)| db.get_node_with_userdata(pid, session))
+ .map(|(pid, _)| DATABASE.get_node_with_userdata(pid, session))
.collect::<anyhow::Result<Vec<_>>>()
}
@@ -110,3 +106,60 @@ impl DatabaseNodeUserDataExt for Database {
))
}
}
+
+pub fn get_nodes_modified_since(_session: &Session, since: u64) -> Result<Vec<NodeID>> {
+ let mut nodes = DATABASE.get_nodes_modified_since(since)?;
+ nodes.retain(|id| {
+ DATABASE.get_node(*id).is_ok_and(|n| {
+ n.as_ref()
+ .is_some_and(|n| n.visibility >= Visibility::Reduced)
+ })
+ });
+ Ok(nodes)
+}
+
+pub fn get_node_by_eid(_session: &Session, platform: &str, eid: &str) -> Result<Option<NodeID>> {
+ DATABASE.get_node_external_id(platform, eid)
+}
+pub fn node_id_to_slug(_session: &Session, id: NodeID) -> Result<String> {
+ Ok(DATABASE
+ .get_node(id)?
+ .ok_or(anyhow!("node does not exist"))?
+ .slug
+ .to_owned())
+}
+
+pub fn update_node_userdata_watched(
+ session: &Session,
+ node: NodeID,
+ state: WatchedState,
+) -> Result<()> {
+ // TODO perm
+ DATABASE.update_node_udata(node, &session.user.name, |udata| {
+ udata.watched = state;
+ Ok(())
+ })
+}
+pub fn update_node_userdata_watched_progress(
+ session: &Session,
+ node: NodeID,
+ time: f64,
+) -> Result<()> {
+ // TODO perm
+ DATABASE.update_node_udata(node, &session.user.name, |udata| {
+ udata.watched = match udata.watched {
+ WatchedState::None | WatchedState::Pending | WatchedState::Progress(_) => {
+ WatchedState::Progress(time)
+ }
+ WatchedState::Watched => WatchedState::Watched,
+ };
+ Ok(())
+ })
+}
+pub fn update_node_userdata_rating(session: &Session, node: NodeID, rating: i32) -> Result<()> {
+ // TODO perm
+ DATABASE.update_node_udata(node, &session.user.name, |udata| {
+ udata.rating = rating;
+ Ok(())
+ })
+}
diff --git a/logic/src/search.rs b/logic/src/search.rs
index 68975f1..304676b 100644
--- a/logic/src/search.rs
+++ b/logic/src/search.rs
@@ -3,23 +3,17 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::{node::DatabaseNodeUserDataExt, session::Session};
+use crate::{DATABASE, node::DatabaseNodeUserDataExt, session::Session};
use anyhow::Result;
use jellycommon::{Visibility, api::ApiSearchResponse};
-use jellydb::Database;
use std::time::Instant;
-pub fn search(
- db: &Database,
- session: &Session,
- query: &str,
- page: Option<usize>,
-) -> Result<ApiSearchResponse> {
+pub fn search(session: &Session, query: &str, page: Option<usize>) -> Result<ApiSearchResponse> {
let timing = Instant::now();
- let (count, ids) = db.search(query, 32, page.unwrap_or_default() * 32)?;
+ let (count, ids) = DATABASE.search(query, 32, page.unwrap_or_default() * 32)?;
let mut results = ids
.into_iter()
- .map(|id| db.get_node_with_userdata(id, &session))
+ .map(|id| DATABASE.get_node_with_userdata(id, &session))
.collect::<Result<Vec<_>, anyhow::Error>>()?;
results.retain(|(n, _)| n.visibility >= Visibility::Reduced);
let duration = timing.elapsed();
diff --git a/logic/src/session.rs b/logic/src/session.rs
index 72a1089..615694c 100644
--- a/logic/src/session.rs
+++ b/logic/src/session.rs
@@ -3,7 +3,7 @@
which is licensed under the GNU Affero General Public License (version 3); see /COPYING.
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::CONF;
+use crate::{CONF, DATABASE};
use aes_gcm_siv::{
KeyInit,
aead::{Aead, generic_array::GenericArray},
@@ -85,10 +85,27 @@ pub fn validate(token: &str) -> anyhow::Result<String> {
Ok(session_data.username)
}
+pub fn token_to_session(token: &str) -> anyhow::Result<Session> {
+ let username = validate(token)?;
+ let user = DATABASE
+ .get_user(&username)?
+ .ok_or(anyhow!("user does not exist"))?;
+ Ok(Session { user })
+}
+pub fn bypass_auth_session() -> anyhow::Result<Session> {
+ let user = DATABASE
+ .get_user(&CONF.admin_username.as_ref().unwrap())?
+ .ok_or(anyhow!("user does not exist"))?;
+ Ok(Session { user })
+}
+
#[cfg(test)]
fn load_test_config() {
+ use std::path::PathBuf;
+
use crate::{CONF_PRELOAD, Config};
*CONF_PRELOAD.lock().unwrap() = Some(Config {
+ database_path: PathBuf::default(),
login_expire: 10,
session_key: None,
admin_password: None,
diff --git a/logic/src/stats.rs b/logic/src/stats.rs
index 2e962e2..c7464f9 100644
--- a/logic/src/stats.rs
+++ b/logic/src/stats.rs
@@ -4,17 +4,16 @@
Copyright (C) 2025 metamuffin <metamuffin.org>
*/
-use crate::session::Session;
+use crate::{DATABASE, session::Session};
use anyhow::Result;
use jellycommon::{
Node, NodeKind, Visibility,
api::{ApiStatsResponse, StatsBin},
};
-use jellydb::Database;
use std::collections::BTreeMap;
-pub fn stats(db: &Database, session: &Session) -> Result<ApiStatsResponse> {
- let mut items = db.list_nodes_with_udata(session.user.name.as_str())?;
+pub fn stats(session: &Session) -> Result<ApiStatsResponse> {
+ let mut items = DATABASE.list_nodes_with_udata(session.user.name.as_str())?;
items.retain(|(n, _)| n.visibility >= Visibility::Reduced);
trait BinExt {