Basic note fetching

This commit is contained in:
Natty 2023-10-29 01:27:32 +02:00
parent f0e56deca9
commit 3cd43d840a
Signed by: natty
GPG Key ID: BF6CB659ADEE60EC
13 changed files with 599 additions and 35 deletions

19
Cargo.lock generated
View File

@ -1453,6 +1453,7 @@ dependencies = [
"cfg-if",
"chrono",
"dotenvy",
"either",
"headers",
"hyper",
"itertools 0.11.0",
@ -1513,6 +1514,8 @@ dependencies = [
"futures-core",
"futures-util",
"magnetar_common",
"magnetar_sdk",
"once_cell",
"redis",
"sea-orm",
"serde",
@ -2754,9 +2757,9 @@ checksum = "62bb4feee49fdd9f707ef802e22365a35de4b7b299de4763d44bfea899442ff9"
[[package]]
name = "smawk"
version = "0.3.1"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c"
[[package]]
name = "socket2"
@ -3073,9 +3076,9 @@ checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc"
[[package]]
name = "supports-color"
version = "2.0.0"
version = "2.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4950e7174bffabe99455511c39707310e7e9b440364a2fcb1cc21521be57b354"
checksum = "d6398cde53adc3c4557306a96ce67b302968513830a77a95b2b17305d9719a89"
dependencies = [
"is-terminal",
"is_ci",
@ -3621,13 +3624,9 @@ checksum = "301abaae475aa91687eb82514b328ab47a211a533026cb25fc3e519b86adfc3c"
[[package]]
name = "unicode-linebreak"
version = "0.1.4"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5faade31a542b8b35855fff6e8def199853b2da8da256da52f52f1316ee3137"
dependencies = [
"hashbrown 0.12.3",
"regex",
]
checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f"
[[package]]
name = "unicode-normalization"

View File

@ -99,6 +99,7 @@ cfg-if = { workspace = true }
itertools = { workspace = true }
either = { workspace = true }
strum = { workspace = true, features = ["derive"] }
thiserror = { workspace = true }
miette = { workspace = true, features = ["fancy"] }

View File

@ -11,6 +11,7 @@ ck = { path = "./entity_ck" }
ext_calckey_model_migration = { path = "./migration" }
magnetar_common = { path = "../magnetar_common" }
magnetar_sdk = { path = "../magnetar_sdk" }
dotenvy = { workspace = true}
futures-core = { workspace = true }
@ -24,4 +25,5 @@ serde_json = { workspace = true }
strum = { workspace = true }
chrono = { workspace = true }
tracing = { workspace = true }
thiserror = { workspace = true }
thiserror = { workspace = true }
once_cell = "1.18.0"

View File

@ -1,3 +1,5 @@
pub mod note_model;
pub use ck;
use ck::*;
pub use sea_orm;
@ -6,10 +8,11 @@ use chrono::Utc;
use ext_calckey_model_migration::{Migrator, MigratorTrait};
use futures_util::StreamExt;
use redis::IntoConnectionInfo;
use sea_orm::sea_query::IntoIden;
use sea_orm::ActiveValue::Set;
use sea_orm::{
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, QueryFilter, QueryOrder,
TransactionTrait,
ColumnTrait, ConnectOptions, DatabaseConnection, DbErr, EntityTrait, JoinType, QueryFilter,
QueryOrder, RelationDef, RelationTrait, TransactionTrait,
};
use serde::{Deserialize, Serialize};
use std::future::Future;
@ -34,6 +37,18 @@ pub enum CalckeyDbError {
DbError(#[from] DbErr),
}
trait AliasSourceExt {
fn with_alias<I: IntoIden>(&self, alias: I) -> RelationDef;
}
impl<T: RelationTrait> AliasSourceExt for T {
fn with_alias<I: IntoIden>(&self, alias: I) -> RelationDef {
let mut def = self.def();
def.from_tbl = def.from_tbl.alias(alias);
def
}
}
impl CalckeyModel {
pub async fn new(config: ConnectorConfig) -> Result<Self, CalckeyDbError> {
let opt = ConnectOptions::new(config.url)
@ -116,6 +131,66 @@ impl CalckeyModel {
.await?)
}
pub async fn get_follower_status(
&self,
from: &str,
to: &str,
) -> Result<Option<following::Model>, CalckeyDbError> {
Ok(following::Entity::find()
.filter(
following::Column::FollowerId
.eq(from)
.and(following::Column::FolloweeId.eq(to)),
)
.one(&self.0)
.await?)
}
pub async fn get_block_status(
&self,
from: &str,
to: &str,
) -> Result<Option<blocking::Model>, CalckeyDbError> {
Ok(blocking::Entity::find()
.filter(
blocking::Column::BlockerId
.eq(from)
.and(blocking::Column::BlockeeId.eq(to)),
)
.one(&self.0)
.await?)
}
pub async fn get_mute_status(
&self,
from: &str,
to: &str,
) -> Result<Option<muting::Model>, CalckeyDbError> {
Ok(muting::Entity::find()
.filter(
muting::Column::MuterId
.eq(from)
.and(muting::Column::MuteeId.eq(to)),
)
.one(&self.0)
.await?)
}
pub async fn get_renote_mute_status(
&self,
from: &str,
to: &str,
) -> Result<Option<renote_muting::Model>, CalckeyDbError> {
Ok(renote_muting::Entity::find()
.filter(
renote_muting::Column::MuterId
.eq(from)
.and(renote_muting::Column::MuteeId.eq(to)),
)
.one(&self.0)
.await?)
}
pub async fn get_local_emoji(&self) -> Result<Vec<emoji::Model>, CalckeyDbError> {
Ok(emoji::Entity::find()
.filter(emoji::Column::Host.is_null())

View File

@ -0,0 +1,258 @@
use sea_orm::sea_query::{Alias, Expr, IntoIden, SelectExpr, SimpleExpr};
use sea_orm::{
ColumnTrait, DbErr, EntityTrait, FromQueryResult, Iden, Iterable, JoinType, QueryFilter,
QueryResult, QuerySelect, RelationTrait, Select,
};
use serde::{Deserialize, Serialize};
use ck::{drive_file, note, user};
use once_cell::unsync::Lazy;
use crate::{AliasSourceExt, CalckeyDbError, CalckeyModel};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct NoteData {
pub note: note::Model,
pub user: user::Model,
pub avatar: Option<drive_file::Model>,
pub banner: Option<drive_file::Model>,
pub reply: Option<Box<NoteData>>,
pub renote: Option<Box<NoteData>>,
}
const USER: &str = "user";
const USER_AVATAR: &str = "user.avatar";
const USER_BANNER: &str = "user.banner";
const REPLY: &str = "reply";
const REPLY_USER: &str = "reply.user";
const REPLY_USER_AVATAR: &str = "reply.user.avatar";
const REPLY_USER_BANNER: &str = "reply.user.banner";
const RENOTE: &str = "renote";
const RENOTE_USER: &str = "renote.user";
const RENOTE_USER_AVATAR: &str = "renote.user.avatar";
const RENOTE_USER_BANNER: &str = "renote.user.banner";
impl FromQueryResult for NoteData {
fn from_query_result(res: &QueryResult, _pre: &str) -> Result<Self, DbErr> {
let reply = note::Model::from_query_result_optional(res, REPLY)?
.map::<Result<_, DbErr>, _>(|r| {
Ok(Box::new(NoteData {
note: r,
user: user::Model::from_query_result(res, REPLY_USER)?,
avatar: drive_file::Model::from_query_result_optional(res, REPLY_USER_AVATAR)?,
banner: drive_file::Model::from_query_result_optional(res, REPLY_USER_BANNER)?,
reply: None,
renote: None,
}))
})
.transpose()?;
let renote = note::Model::from_query_result_optional(res, RENOTE)?
.map::<Result<_, DbErr>, _>(|r| {
Ok(Box::new(NoteData {
note: r,
user: user::Model::from_query_result(res, RENOTE_USER)?,
avatar: drive_file::Model::from_query_result_optional(res, RENOTE_USER_AVATAR)?,
banner: drive_file::Model::from_query_result_optional(res, RENOTE_USER_BANNER)?,
reply: None,
renote: None,
}))
})
.transpose()?;
Ok(NoteData {
note: note::Model::from_query_result(res, "")?,
user: user::Model::from_query_result(res, USER)?,
avatar: drive_file::Model::from_query_result_optional(res, USER_AVATAR)?,
banner: drive_file::Model::from_query_result_optional(res, USER_BANNER)?,
reply,
renote,
})
}
}
pub struct NoteResolver {
db: CalckeyModel,
}
pub trait NoteVisibilityFilterFactory {
fn with_note_and_user_tables(&self, note: Option<Alias>, user: Option<Alias>) -> SimpleExpr;
}
pub struct NoteResolveOptions {
visibility_filter: Box<dyn NoteVisibilityFilterFactory>,
with_user: bool,
with_reply_target: bool,
with_renote_target: bool,
}
trait SelectColumnsExt {
fn add_aliased_columns<T: EntityTrait>(self, alias: Option<&str>, entity: T) -> Self;
}
impl SelectColumnsExt for Select<note::Entity> {
fn add_aliased_columns<T: EntityTrait>(
mut self: Select<note::Entity>,
alias: Option<&str>,
entity: T,
) -> Select<note::Entity> {
for col in T::Column::iter() {
let column: &T::Column = &col;
let iden = alias.unwrap_or_else(|| entity.table_name());
let alias = format!("{}{}", iden, col.to_string());
let column_ref = Expr::col((Alias::new(iden), column.as_column_ref().1));
QuerySelect::query(&mut self).expr(SelectExpr {
expr: col.select_as(column_ref),
alias: Some(Alias::new(&alias).into_iden()),
window: None,
});
}
self
}
}
const ALIAS_USER: Lazy<Alias> = Lazy::new(|| Alias::new(USER));
const ALIAS_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(USER_AVATAR));
const ALIAS_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(USER_BANNER));
const ALIAS_REPLY: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY));
const ALIAS_REPLY_USER: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER));
const ALIAS_REPLY_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER_AVATAR));
const ALIAS_REPLY_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(REPLY_USER_BANNER));
const ALIAS_RENOTE: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE));
const ALIAS_RENOTE_USER: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER));
const ALIAS_RENOTE_USER_AVATAR: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER_AVATAR));
const ALIAS_RENOTE_USER_BANNER: Lazy<Alias> = Lazy::new(|| Alias::new(RENOTE_USER_BANNER));
impl NoteResolver {
pub async fn get_one(
&self,
options: &NoteResolveOptions,
) -> Result<Option<NoteData>, CalckeyDbError> {
let select = self.resolve(options);
let visibility_filter = options
.visibility_filter
.with_note_and_user_tables(None, Some(ALIAS_USER.clone()));
let notes = select
.filter(visibility_filter)
.into_model::<NoteData>()
.one(self.db.inner())
.await?;
Ok(notes)
}
pub fn resolve(&self, options: &NoteResolveOptions) -> Select<note::Entity> {
let mut select = note::Entity::find().add_aliased_columns(Some(USER), user::Entity);
if options.with_user {
select = select
.add_aliased_columns(Some(USER_AVATAR), drive_file::Entity)
.add_aliased_columns(Some(USER_BANNER), drive_file::Entity);
}
if options.with_reply_target {
select = select
.add_aliased_columns(Some(REPLY), note::Entity)
.add_aliased_columns(Some(REPLY_USER), user::Entity);
if options.with_user {
select = select
.add_aliased_columns(Some(REPLY_USER_AVATAR), drive_file::Entity)
.add_aliased_columns(Some(REPLY_USER_BANNER), drive_file::Entity);
}
}
if options.with_renote_target {
select = select
.add_aliased_columns(Some(RENOTE), note::Entity)
.add_aliased_columns(Some(RENOTE_USER), user::Entity);
if options.with_user {
select = select
.add_aliased_columns(Some(RENOTE_USER_AVATAR), drive_file::Entity)
.add_aliased_columns(Some(RENOTE_USER_BANNER), drive_file::Entity)
}
}
if options.with_reply_target {
select = select
.join_as(
JoinType::LeftJoin,
note::Relation::SelfRef2.def(),
ALIAS_REPLY.clone(),
)
.join_as(
JoinType::LeftJoin,
note::Relation::User.with_alias(ALIAS_REPLY.clone()),
ALIAS_REPLY_USER.clone(),
);
}
if options.with_renote_target {
select = select
.join_as(
JoinType::LeftJoin,
note::Relation::SelfRef1.def(),
ALIAS_RENOTE.clone(),
)
.join_as(
JoinType::InnerJoin,
note::Relation::User.with_alias(ALIAS_RENOTE.clone()),
ALIAS_RENOTE_USER.clone(),
);
}
select = select.join_as(
JoinType::InnerJoin,
note::Relation::User.def(),
ALIAS_USER.clone(),
);
if options.with_user {
select = select
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile2.with_alias(ALIAS_USER.clone()),
ALIAS_USER_AVATAR.clone(),
)
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile1.with_alias(ALIAS_USER.clone()),
ALIAS_USER_BANNER.clone(),
);
if options.with_reply_target {
select = select
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile2.with_alias(ALIAS_REPLY_USER.clone()),
ALIAS_REPLY_USER_AVATAR.clone(),
)
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile1.with_alias(ALIAS_REPLY_USER.clone()),
ALIAS_REPLY_USER_BANNER.clone(),
);
}
if options.with_renote_target {
select = select
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile2.with_alias(ALIAS_RENOTE_USER.clone()),
ALIAS_RENOTE_USER_AVATAR.clone(),
)
.join_as(
JoinType::LeftJoin,
user::Relation::DriveFile1.with_alias(ALIAS_RENOTE_USER.clone()),
ALIAS_RENOTE_USER_BANNER.clone(),
);
}
}
select
}
}

View File

@ -5,7 +5,6 @@ use crate::web::auth::{AuthenticatedUser, MaybeUser};
use crate::web::{ApiError, ObjectNotFound};
use axum::extract::{Path, Query, State};
use axum::Json;
use magnetar_calckey_model::ck;
use magnetar_sdk::endpoints::user::{GetUserById, GetUserSelf, UserByIdReq, UserSelfReq};
use magnetar_sdk::endpoints::{Req, Res};
use std::sync::Arc;

View File

@ -1,7 +1,11 @@
use crate::model::data::id::BaseId;
use crate::model::processing::PackResult;
use crate::service::MagnetarService;
use magnetar_calckey_model::ck;
use either::Either;
use magnetar_calckey_model::{ck, CalckeyDbError};
use std::collections::HashMap;
use std::sync::Arc;
use tokio::sync::Mutex;
pub mod data;
pub mod processing;
@ -17,12 +21,28 @@ impl Default for ProcessingLimits {
}
}
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
enum UserRelationship {
Follow,
Mute,
Block,
RenoteMute,
}
#[derive(Clone, Debug, Hash, PartialEq, Eq)]
struct UserRelationshipLink {
from: String,
to: String,
rel_type: UserRelationship,
}
#[derive(Clone, Debug)]
pub struct PackingContext {
instance_meta: Arc<ck::meta::Model>,
self_user: Option<Arc<ck::user::Model>>,
service: Arc<MagnetarService>,
limits: ProcessingLimits,
relationships: Arc<Mutex<HashMap<UserRelationshipLink, bool>>>,
}
pub trait PackType<I>: 'static {
@ -39,6 +59,7 @@ impl PackingContext {
self_user,
service,
limits: Default::default(),
relationships: Arc::new(Mutex::new(HashMap::new())),
})
}
@ -46,8 +67,81 @@ impl PackingContext {
self.self_user.as_deref()
}
fn is_self(&self, user: &ck::user::Model) -> bool {
fn is_id_self(&self, user_id: &str) -> bool {
self.self_user()
.is_some_and(|self_user| self_user.id == user.id)
.is_some_and(|self_user| self_user.id == user_id)
}
fn is_self(&self, user: &ck::user::Model) -> bool {
self.is_id_self(&user.id)
}
async fn has_relationship_with(
&self,
user: &ck::user::Model,
rel_type: UserRelationship,
) -> Result<bool, CalckeyDbError> {
let Some(self_user) = self.self_user.as_deref() else {
return Ok(false);
};
self.is_relationship_between(
Either::Right(self_user.into()),
Either::Right(user.into()),
rel_type,
)
.await
}
async fn is_relationship_between(
&self,
from: Either<&str, &ck::user::Model>,
to: Either<&str, &ck::user::Model>,
rel_type: UserRelationship,
) -> Result<bool, CalckeyDbError> {
let link = UserRelationshipLink {
from: from.left_or_else(ck::user::Model::get_id).to_string(),
to: to.left_or_else(ck::user::Model::get_id).to_string(),
rel_type,
};
let read = self.relationships.lock().await;
if let Some(relationship) = read.get(&link) {
return Ok(*relationship);
}
drop(read);
let relationship = match rel_type {
UserRelationship::Block => self
.service
.db
.get_block_status(&link.from, &link.to)
.await?
.is_some(),
UserRelationship::Follow => self
.service
.db
.get_follower_status(&link.from, &link.to)
.await?
.is_some(),
UserRelationship::Mute => self
.service
.db
.get_mute_status(&link.from, &link.to)
.await?
.is_some(),
UserRelationship::RenoteMute => self
.service
.db
.get_renote_mute_status(&link.from, &link.to)
.await?
.is_some(),
};
let mut write = self.relationships.lock().await;
write.insert(link, relationship);
drop(write);
return Ok(relationship);
}
}

View File

@ -8,6 +8,7 @@ use thiserror::Error;
pub mod drive;
pub mod emoji;
pub mod note;
pub mod user;
#[derive(Debug, Error, strum::IntoStaticStr)]

View File

@ -0,0 +1,137 @@
use crate::model::{PackingContext, UserRelationship};
use either::Either;
use magnetar_calckey_model::ck::sea_orm_active_enums::NoteVisibilityEnum;
use magnetar_calckey_model::note_model::NoteVisibilityFilterFactory;
use magnetar_calckey_model::sea_orm::prelude::Expr;
use magnetar_calckey_model::sea_orm::sea_query::{Alias, IntoIden, PgFunc, Query, SimpleExpr};
use magnetar_calckey_model::sea_orm::{ColumnTrait, IntoSimpleExpr};
use magnetar_calckey_model::{ck, CalckeyDbError};
#[derive(Debug, Clone)]
pub struct NoteVisibilityFilterSimple(Option<String>);
impl NoteVisibilityFilterFactory for NoteVisibilityFilterSimple {
fn with_note_and_user_tables(
&self,
note_tbl: Option<Alias>,
user_tbl: Option<Alias>,
) -> SimpleExpr {
let note_tbl_name =
note_tbl.map_or_else(|| ck::note::Entity.into_iden(), |a| a.into_iden());
let user_tbl_name =
user_tbl.map_or_else(|| ck::user::Entity.into_iden(), |a| a.into_iden());
let note_visibility = Expr::col((note_tbl_name.clone(), ck::note::Column::Visibility));
let note_mentions = Expr::col((note_tbl_name.clone(), ck::note::Column::Mentions));
let note_reply_user_id = Expr::col((note_tbl_name.clone(), ck::note::Column::ReplyUserId));
let note_visible_user_ids =
Expr::col((note_tbl_name.clone(), ck::note::Column::VisibleUserIds));
let note_user_id = Expr::col((user_tbl_name, ck::note::Column::UserId));
let is_public = note_visibility
.clone()
.eq(NoteVisibilityEnum::Public)
.or(note_visibility.clone().eq(NoteVisibilityEnum::Home));
let Some(user_id_str) = &self.0 else {
return is_public;
};
let self_user_id = SimpleExpr::Constant(user_id_str.into());
let is_self = note_user_id.clone().eq(self_user_id.clone());
let is_visible_specified = {
let either_specified_or_followers = note_visibility
.clone()
.eq(NoteVisibilityEnum::Specified)
.or(note_visibility.clone().eq(NoteVisibilityEnum::Followers))
.into_simple_expr();
let mentioned_or_specified = self_user_id
.clone()
.eq(PgFunc::any(note_mentions.into_simple_expr()))
.or(self_user_id.eq(PgFunc::any(note_visible_user_ids)));
either_specified_or_followers.and(mentioned_or_specified)
};
let is_visible_followers = {
note_visibility
.eq(NoteVisibilityEnum::Followers)
.and(
note_user_id.in_subquery(
Query::select()
.column(ck::following::Column::FolloweeId)
.from(ck::following::Entity)
.cond_where(ck::following::Column::FollowerId.eq(user_id_str))
.to_owned(),
),
)
.or(note_reply_user_id.eq(user_id_str))
};
is_self
.or(is_public)
.or(is_visible_followers)
.or(is_visible_specified)
}
}
pub struct NoteVisibilityFilterModel;
impl NoteVisibilityFilterModel {
pub async fn is_note_visible(
&self,
ctx: &PackingContext,
user: Option<&ck::user::Model>,
note: &ck::note::Model,
) -> Result<bool, CalckeyDbError> {
if user.is_some_and(|user| user.id == note.user_id) {
return Ok(true);
}
if matches!(
note.visibility,
NoteVisibilityEnum::Public | NoteVisibilityEnum::Home
) {
return Ok(true);
}
if matches!(
note.visibility,
NoteVisibilityEnum::Followers | NoteVisibilityEnum::Specified
) {
let Some(user) = user else {
return Ok(false);
};
if note.mentions.contains(&user.id) || note.visible_user_ids.contains(&user.id) {
return Ok(true);
}
if matches!(note.visibility, NoteVisibilityEnum::Specified) {
return Ok(false);
}
let following = ctx
.is_relationship_between(
Either::Right(user),
Either::Left(&note.user_id),
UserRelationship::Follow,
)
.await?;
// The second condition generally will not happen in the API,
// however it allows some AP processing, with activities
// between two foreign objects
return Ok(following || user.host.is_some() && note.user_host.is_some());
}
return Ok(false);
}
pub fn new_note_visibility_filter(&self, user: Option<&str>) -> NoteVisibilityFilterSimple {
NoteVisibilityFilterSimple(user.map(str::to_string))
}
}

View File

@ -3,13 +3,11 @@ use crate::model::processing::emoji::EmojiModel;
use crate::model::processing::{get_mm_token_emoji, PackResult};
use crate::model::{PackType, PackingContext};
use magnetar_calckey_model::ck;
use magnetar_calckey_model::sea_orm::EntityTrait;
use magnetar_sdk::mmm::Token;
use magnetar_sdk::types::emoji::EmojiContext;
use magnetar_sdk::types::user::{PackUserBase, UserBase};
use magnetar_sdk::types::{Id, MmXml};
use magnetar_sdk::{mmm, Packed, Required};
use std::sync::Arc;
pub struct UserModel;

View File

@ -27,20 +27,20 @@ impl From<UserCacheError> for ApiError {
UserCacheError::RedisError(err) => err.into(),
};
api_error.message = format!("User cache error: {}", api_error.message);
api_error.message = format!("Local user cache error: {}", api_error.message);
api_error
}
}
struct UserCache {
struct LocalUserCache {
lifetime: TimedCache<String, ()>,
id_to_user: HashMap<String, Arc<ck::user::Model>>,
token_to_user: HashMap<String, Arc<ck::user::Model>>,
uri_to_user: HashMap<String, Arc<ck::user::Model>>,
}
impl UserCache {
impl LocalUserCache {
fn purge(&mut self, user: impl AsRef<ck::user::Model>) {
let user = user.as_ref();
@ -120,20 +120,20 @@ impl UserCache {
}
}
pub struct UserCacheService {
pub struct LocalUserCacheService {
db: CalckeyModel,
#[allow(dead_code)]
token_watch: CalckeySub,
cache: Arc<Mutex<UserCache>>,
cache: Arc<Mutex<LocalUserCache>>,
}
impl UserCacheService {
impl LocalUserCacheService {
pub(super) async fn new(
config: &MagnetarConfig,
db: CalckeyModel,
redis: CalckeyCache,
) -> Result<Self, UserCacheError> {
let cache = Arc::new(Mutex::new(UserCache {
let cache = Arc::new(Mutex::new(LocalUserCache {
lifetime: TimedCache::with_lifespan(60 * 5),
id_to_user: HashMap::new(),
token_to_user: HashMap::new(),

View File

@ -7,13 +7,13 @@ use thiserror::Error;
pub mod emoji_cache;
pub mod generic_id_cache;
pub mod instance_meta_cache;
pub mod user_cache;
pub mod local_user_cache;
pub struct MagnetarService {
pub db: CalckeyModel,
pub cache: CalckeyCache,
pub config: &'static MagnetarConfig,
pub auth_cache: user_cache::UserCacheService,
pub local_user_cache: local_user_cache::LocalUserCacheService,
pub instance_meta_cache: instance_meta_cache::InstanceMetaCacheService,
pub emoji_cache: emoji_cache::EmojiCacheService,
pub drive_file_cache: generic_id_cache::GenericIdCacheService<ck::drive_file::Entity>,
@ -32,7 +32,7 @@ impl Debug for MagnetarService {
#[derive(Debug, Error)]
pub enum ServiceInitError {
#[error("Authentication cache initialization error: {0}")]
AuthCacheError(#[from] user_cache::UserCacheError),
AuthCacheError(#[from] local_user_cache::UserCacheError),
}
impl MagnetarService {
@ -41,8 +41,8 @@ impl MagnetarService {
db: CalckeyModel,
cache: CalckeyCache,
) -> Result<Self, ServiceInitError> {
let auth_cache =
user_cache::UserCacheService::new(config, db.clone(), cache.clone()).await?;
let local_user_cache =
local_user_cache::LocalUserCacheService::new(config, db.clone(), cache.clone()).await?;
let instance_meta_cache = instance_meta_cache::InstanceMetaCacheService::new(db.clone());
let emoji_cache = emoji_cache::EmojiCacheService::new(db.clone());
let drive_file_cache =
@ -52,7 +52,7 @@ impl MagnetarService {
db,
cache,
config,
auth_cache,
local_user_cache,
instance_meta_cache,
emoji_cache,
drive_file_cache,

View File

@ -1,4 +1,4 @@
use crate::service::user_cache::UserCacheError;
use crate::service::local_user_cache::UserCacheError;
use crate::service::MagnetarService;
use crate::web::{ApiError, IntoErrorCode};
use axum::async_trait;
@ -175,7 +175,7 @@ impl AuthState {
let token = token.token();
if is_user_token(token) {
let user_cache = &self.service.auth_cache;
let user_cache = &self.service.local_user_cache;
let user = user_cache.get_by_token(token).await?;
if let Some(user) = user {
@ -194,7 +194,7 @@ impl AuthState {
let user = self
.service
.auth_cache
.local_user_cache
.get_by_id(&access_token.user_id)
.await?;