mirror of
https://github.com/lennart-k/rustical.git
synced 2025-12-13 22:52:22 +00:00
implement principal types
This commit is contained in:
@@ -10,7 +10,7 @@ use calendar_set::CalendarSetResourceService;
|
|||||||
use principal::{PrincipalResource, PrincipalResourceService};
|
use principal::{PrincipalResource, PrincipalResourceService};
|
||||||
use rustical_dav::resource::{NamedRoute, ResourceService, ResourceServiceRoute};
|
use rustical_dav::resource::{NamedRoute, ResourceService, ResourceServiceRoute};
|
||||||
use rustical_dav::resources::RootResourceService;
|
use rustical_dav::resources::RootResourceService;
|
||||||
use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider};
|
use rustical_store::auth::{AuthenticationMiddleware, AuthenticationProvider, UserStore};
|
||||||
use rustical_store::{AddressbookStore, CalendarStore, ContactBirthdayStore, SubscriptionStore};
|
use rustical_store::{AddressbookStore, CalendarStore, ContactBirthdayStore, SubscriptionStore};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use subscription::subscription_resource;
|
use subscription::subscription_resource;
|
||||||
@@ -25,11 +25,13 @@ mod subscription;
|
|||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
|
|
||||||
pub fn caldav_service<
|
pub fn caldav_service<
|
||||||
|
US: UserStore,
|
||||||
AP: AuthenticationProvider,
|
AP: AuthenticationProvider,
|
||||||
AS: AddressbookStore,
|
AS: AddressbookStore,
|
||||||
C: CalendarStore,
|
C: CalendarStore,
|
||||||
S: SubscriptionStore,
|
S: SubscriptionStore,
|
||||||
>(
|
>(
|
||||||
|
user_store: Arc<US>,
|
||||||
auth_provider: Arc<AP>,
|
auth_provider: Arc<AP>,
|
||||||
store: Arc<C>,
|
store: Arc<C>,
|
||||||
addr_store: Arc<AS>,
|
addr_store: Arc<AS>,
|
||||||
@@ -66,9 +68,9 @@ pub fn caldav_service<
|
|||||||
.service(
|
.service(
|
||||||
web::scope("/principal").service(
|
web::scope("/principal").service(
|
||||||
web::scope("/{principal}")
|
web::scope("/{principal}")
|
||||||
.service(PrincipalResourceService(&[
|
.service(PrincipalResourceService{store: user_store, home_set: &[
|
||||||
("calendar", false), ("birthdays", true)
|
("calendar", false), ("birthdays", true)
|
||||||
]).actix_resource().name(PrincipalResource::route_name()))
|
]}.actix_resource().name(PrincipalResource::route_name()))
|
||||||
.service(web::scope("/calendar")
|
.service(web::scope("/calendar")
|
||||||
.service(CalendarSetResourceService::new(store.clone()).actix_resource())
|
.service(CalendarSetResourceService::new(store.clone()).actix_resource())
|
||||||
.service(
|
.service(
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
use crate::calendar_set::CalendarSetResource;
|
use crate::calendar_set::CalendarSetResource;
|
||||||
use crate::Error;
|
use crate::Error;
|
||||||
use actix_web::dev::ResourceMap;
|
use actix_web::dev::ResourceMap;
|
||||||
@@ -7,12 +9,12 @@ use rustical_dav::privileges::UserPrivilegeSet;
|
|||||||
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::user::PrincipalType;
|
use rustical_store::auth::user::PrincipalType;
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::{User, UserStore};
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct PrincipalResource {
|
pub struct PrincipalResource {
|
||||||
principal: String,
|
principal: User,
|
||||||
home_set: &'static [(&'static str, bool)],
|
home_set: &'static [(&'static str, bool)],
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,9 +79,8 @@ impl Resource for PrincipalResource {
|
|||||||
user: &User,
|
user: &User,
|
||||||
prop: &PrincipalPropWrapperName,
|
prop: &PrincipalPropWrapperName,
|
||||||
) -> Result<Self::Prop, Self::Error> {
|
) -> Result<Self::Prop, Self::Error> {
|
||||||
let principal_url = Self::get_url(rmap, vec![&self.principal]).unwrap();
|
let principal_url = Self::get_url(rmap, vec![&self.principal.id]).unwrap();
|
||||||
|
|
||||||
// BUG: We need to read the properties of the principal, not the requesting user
|
|
||||||
let home_set = CalendarHomeSet(
|
let home_set = CalendarHomeSet(
|
||||||
user.memberships()
|
user.memberships()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
@@ -96,10 +97,10 @@ impl Resource for PrincipalResource {
|
|||||||
PrincipalPropWrapperName::Principal(prop) => {
|
PrincipalPropWrapperName::Principal(prop) => {
|
||||||
PrincipalPropWrapper::Principal(match prop {
|
PrincipalPropWrapper::Principal(match prop {
|
||||||
PrincipalPropName::CalendarUserType => {
|
PrincipalPropName::CalendarUserType => {
|
||||||
PrincipalProp::CalendarUserType(user.user_type.to_owned())
|
PrincipalProp::CalendarUserType(self.principal.user_type.to_owned())
|
||||||
}
|
}
|
||||||
PrincipalPropName::Displayname => {
|
PrincipalPropName::Displayname => {
|
||||||
PrincipalProp::Displayname(self.principal.to_owned())
|
PrincipalProp::Displayname(self.principal.id.to_owned())
|
||||||
}
|
}
|
||||||
PrincipalPropName::PrincipalUrl => {
|
PrincipalPropName::PrincipalUrl => {
|
||||||
PrincipalProp::PrincipalUrl(principal_url.into())
|
PrincipalProp::PrincipalUrl(principal_url.into())
|
||||||
@@ -117,20 +118,23 @@ impl Resource for PrincipalResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_owner(&self) -> Option<&str> {
|
fn get_owner(&self) -> Option<&str> {
|
||||||
Some(&self.principal)
|
Some(&self.principal.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||||
Ok(UserPrivilegeSet::owner_read(
|
Ok(UserPrivilegeSet::owner_read(
|
||||||
user.is_principal(&self.principal),
|
user.is_principal(&self.principal.id),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct PrincipalResourceService(pub &'static [(&'static str, bool)]);
|
pub struct PrincipalResourceService<US: UserStore> {
|
||||||
|
pub store: Arc<US>,
|
||||||
|
pub home_set: &'static [(&'static str, bool)],
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl ResourceService for PrincipalResourceService {
|
impl<US: UserStore> ResourceService for PrincipalResourceService<US> {
|
||||||
type PathComponents = (String,);
|
type PathComponents = (String,);
|
||||||
type MemberType = CalendarSetResource;
|
type MemberType = CalendarSetResource;
|
||||||
type Resource = PrincipalResource;
|
type Resource = PrincipalResource;
|
||||||
@@ -140,9 +144,14 @@ impl ResourceService for PrincipalResourceService {
|
|||||||
&self,
|
&self,
|
||||||
(principal,): &Self::PathComponents,
|
(principal,): &Self::PathComponents,
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
|
let user = self
|
||||||
|
.store
|
||||||
|
.get_user(principal)
|
||||||
|
.await?
|
||||||
|
.ok_or(crate::Error::NotFound)?;
|
||||||
Ok(PrincipalResource {
|
Ok(PrincipalResource {
|
||||||
principal: principal.to_owned(),
|
principal: user,
|
||||||
home_set: self.0,
|
home_set: self.home_set,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -151,7 +160,7 @@ impl ResourceService for PrincipalResourceService {
|
|||||||
(principal,): &Self::PathComponents,
|
(principal,): &Self::PathComponents,
|
||||||
) -> Result<Vec<(String, Self::MemberType)>, Self::Error> {
|
) -> Result<Vec<(String, Self::MemberType)>, Self::Error> {
|
||||||
Ok(self
|
Ok(self
|
||||||
.0
|
.home_set
|
||||||
.iter()
|
.iter()
|
||||||
.map(|&(set_name, read_only)| {
|
.map(|&(set_name, read_only)| {
|
||||||
(
|
(
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ use principal::{PrincipalResource, PrincipalResourceService};
|
|||||||
use rustical_dav::resource::{NamedRoute, ResourceService};
|
use rustical_dav::resource::{NamedRoute, ResourceService};
|
||||||
use rustical_dav::resources::RootResourceService;
|
use rustical_dav::resources::RootResourceService;
|
||||||
use rustical_store::{
|
use rustical_store::{
|
||||||
auth::{AuthenticationMiddleware, AuthenticationProvider},
|
auth::{AuthenticationMiddleware, AuthenticationProvider, UserStore},
|
||||||
AddressbookStore, SubscriptionStore,
|
AddressbookStore, SubscriptionStore,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
@@ -27,6 +27,7 @@ pub mod principal;
|
|||||||
|
|
||||||
pub fn carddav_service<AP: AuthenticationProvider, A: AddressbookStore, S: SubscriptionStore>(
|
pub fn carddav_service<AP: AuthenticationProvider, A: AddressbookStore, S: SubscriptionStore>(
|
||||||
auth_provider: Arc<AP>,
|
auth_provider: Arc<AP>,
|
||||||
|
user_store: Arc<impl UserStore>,
|
||||||
store: Arc<A>,
|
store: Arc<A>,
|
||||||
subscription_store: Arc<S>,
|
subscription_store: Arc<S>,
|
||||||
) -> impl HttpServiceFactory {
|
) -> impl HttpServiceFactory {
|
||||||
@@ -59,7 +60,7 @@ pub fn carddav_service<AP: AuthenticationProvider, A: AddressbookStore, S: Subsc
|
|||||||
web::scope("/principal").service(
|
web::scope("/principal").service(
|
||||||
web::scope("/{principal}")
|
web::scope("/{principal}")
|
||||||
.service(
|
.service(
|
||||||
PrincipalResourceService::new(store.clone())
|
PrincipalResourceService::new(store.clone(), user_store)
|
||||||
.actix_resource()
|
.actix_resource()
|
||||||
.name(PrincipalResource::route_name()),
|
.name(PrincipalResource::route_name()),
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -6,24 +6,28 @@ use rustical_dav::extensions::{CommonPropertiesExtension, CommonPropertiesProp};
|
|||||||
use rustical_dav::privileges::UserPrivilegeSet;
|
use rustical_dav::privileges::UserPrivilegeSet;
|
||||||
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
use rustical_dav::resource::{NamedRoute, Resource, ResourceService};
|
||||||
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
use rustical_dav::xml::{HrefElement, Resourcetype, ResourcetypeInner};
|
||||||
use rustical_store::auth::User;
|
use rustical_store::auth::{User, UserStore};
|
||||||
use rustical_store::AddressbookStore;
|
use rustical_store::AddressbookStore;
|
||||||
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
use rustical_xml::{EnumUnitVariants, EnumVariants, XmlDeserialize, XmlSerialize};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
pub struct PrincipalResourceService<A: AddressbookStore> {
|
pub struct PrincipalResourceService<A: AddressbookStore, US: UserStore> {
|
||||||
addr_store: Arc<A>,
|
addr_store: Arc<A>,
|
||||||
|
user_store: Arc<US>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<A: AddressbookStore> PrincipalResourceService<A> {
|
impl<A: AddressbookStore, US: UserStore> PrincipalResourceService<A, US> {
|
||||||
pub fn new(addr_store: Arc<A>) -> Self {
|
pub fn new(addr_store: Arc<A>, user_store: Arc<US>) -> Self {
|
||||||
Self { addr_store }
|
Self {
|
||||||
|
addr_store,
|
||||||
|
user_store,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PrincipalResource {
|
pub struct PrincipalResource {
|
||||||
principal: String,
|
principal: User,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
#[derive(XmlDeserialize, XmlSerialize, PartialEq, Clone)]
|
||||||
@@ -84,7 +88,7 @@ impl Resource for PrincipalResource {
|
|||||||
user: &User,
|
user: &User,
|
||||||
prop: &PrincipalPropWrapperName,
|
prop: &PrincipalPropWrapperName,
|
||||||
) -> Result<Self::Prop, Self::Error> {
|
) -> Result<Self::Prop, Self::Error> {
|
||||||
let principal_href = HrefElement::new(Self::get_principal_url(rmap, &self.principal));
|
let principal_href = HrefElement::new(Self::get_principal_url(rmap, &self.principal.id));
|
||||||
|
|
||||||
let home_set = AddressbookHomeSet(
|
let home_set = AddressbookHomeSet(
|
||||||
user.memberships()
|
user.memberships()
|
||||||
@@ -98,7 +102,7 @@ impl Resource for PrincipalResource {
|
|||||||
PrincipalPropWrapperName::Principal(prop) => {
|
PrincipalPropWrapperName::Principal(prop) => {
|
||||||
PrincipalPropWrapper::Principal(match prop {
|
PrincipalPropWrapper::Principal(match prop {
|
||||||
PrincipalPropName::Displayname => {
|
PrincipalPropName::Displayname => {
|
||||||
PrincipalProp::Displayname(self.principal.to_owned())
|
PrincipalProp::Displayname(self.principal.id.to_owned())
|
||||||
}
|
}
|
||||||
PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href),
|
PrincipalPropName::PrincipalUrl => PrincipalProp::PrincipalUrl(principal_href),
|
||||||
PrincipalPropName::AddressbookHomeSet => {
|
PrincipalPropName::AddressbookHomeSet => {
|
||||||
@@ -115,18 +119,18 @@ impl Resource for PrincipalResource {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_owner(&self) -> Option<&str> {
|
fn get_owner(&self) -> Option<&str> {
|
||||||
Some(&self.principal)
|
Some(&self.principal.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
fn get_user_privileges(&self, user: &User) -> Result<UserPrivilegeSet, Self::Error> {
|
||||||
Ok(UserPrivilegeSet::owner_only(
|
Ok(UserPrivilegeSet::owner_only(
|
||||||
user.is_principal(&self.principal),
|
user.is_principal(&self.principal.id),
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[async_trait(?Send)]
|
#[async_trait(?Send)]
|
||||||
impl<A: AddressbookStore> ResourceService for PrincipalResourceService<A> {
|
impl<A: AddressbookStore, US: UserStore> ResourceService for PrincipalResourceService<A, US> {
|
||||||
type PathComponents = (String,);
|
type PathComponents = (String,);
|
||||||
type MemberType = AddressbookResource;
|
type MemberType = AddressbookResource;
|
||||||
type Resource = PrincipalResource;
|
type Resource = PrincipalResource;
|
||||||
@@ -136,9 +140,12 @@ impl<A: AddressbookStore> ResourceService for PrincipalResourceService<A> {
|
|||||||
&self,
|
&self,
|
||||||
(principal,): &Self::PathComponents,
|
(principal,): &Self::PathComponents,
|
||||||
) -> Result<Self::Resource, Self::Error> {
|
) -> Result<Self::Resource, Self::Error> {
|
||||||
Ok(PrincipalResource {
|
let user = self
|
||||||
principal: principal.to_owned(),
|
.user_store
|
||||||
})
|
.get_user(principal)
|
||||||
|
.await?
|
||||||
|
.ok_or(crate::Error::NotFound)?;
|
||||||
|
Ok(PrincipalResource { principal: user })
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_members(
|
async fn get_members(
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
pub mod middleware;
|
pub mod middleware;
|
||||||
pub mod static_user_store;
|
pub mod static_user_store;
|
||||||
pub mod user;
|
pub mod user;
|
||||||
|
mod user_store;
|
||||||
use crate::error::Error;
|
use crate::error::Error;
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
|
|
||||||
@@ -12,3 +13,4 @@ pub trait AuthenticationProvider: 'static {
|
|||||||
pub use middleware::AuthenticationMiddleware;
|
pub use middleware::AuthenticationMiddleware;
|
||||||
pub use static_user_store::{StaticUserStore, StaticUserStoreConfig};
|
pub use static_user_store::{StaticUserStore, StaticUserStoreConfig};
|
||||||
pub use user::User;
|
pub use user::User;
|
||||||
|
pub use user_store::UserStore;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use async_trait::async_trait;
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
|
||||||
use super::AuthenticationProvider;
|
use super::{AuthenticationProvider, UserStore};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
|
||||||
pub struct StaticUserStoreConfig {
|
pub struct StaticUserStoreConfig {
|
||||||
@@ -23,11 +23,18 @@ impl StaticUserStore {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl UserStore for StaticUserStore {
|
||||||
|
async fn get_user(&self, id: &str) -> Result<Option<User>, crate::Error> {
|
||||||
|
Ok(self.users.get(id).cloned())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[async_trait]
|
#[async_trait]
|
||||||
impl AuthenticationProvider for StaticUserStore {
|
impl AuthenticationProvider for StaticUserStore {
|
||||||
async fn validate_user_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error> {
|
async fn validate_user_token(&self, user_id: &str, token: &str) -> Result<Option<User>, Error> {
|
||||||
let user: User = match self.users.get(user_id) {
|
let user: User = match self.get_user(user_id).await? {
|
||||||
Some(user) => user.clone(),
|
Some(user) => user,
|
||||||
None => return Ok(None),
|
None => return Ok(None),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
7
crates/store/src/auth/user_store.rs
Normal file
7
crates/store/src/auth/user_store.rs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
use super::User;
|
||||||
|
use async_trait::async_trait;
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait UserStore: 'static {
|
||||||
|
async fn get_user(&self, id: &str) -> Result<Option<User>, crate::Error>;
|
||||||
|
}
|
||||||
@@ -5,7 +5,7 @@ use actix_web::{web, App};
|
|||||||
use rustical_caldav::caldav_service;
|
use rustical_caldav::caldav_service;
|
||||||
use rustical_carddav::carddav_service;
|
use rustical_carddav::carddav_service;
|
||||||
use rustical_frontend::{configure_frontend, FrontendConfig};
|
use rustical_frontend::{configure_frontend, FrontendConfig};
|
||||||
use rustical_store::auth::AuthenticationProvider;
|
use rustical_store::auth::{AuthenticationProvider, UserStore};
|
||||||
use rustical_store::{AddressbookStore, CalendarStore, SubscriptionStore};
|
use rustical_store::{AddressbookStore, CalendarStore, SubscriptionStore};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use tracing_actix_web::TracingLogger;
|
use tracing_actix_web::TracingLogger;
|
||||||
@@ -15,6 +15,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
|
|||||||
cal_store: Arc<CS>,
|
cal_store: Arc<CS>,
|
||||||
subscription_store: Arc<S>,
|
subscription_store: Arc<S>,
|
||||||
auth_provider: Arc<impl AuthenticationProvider>,
|
auth_provider: Arc<impl AuthenticationProvider>,
|
||||||
|
user_store: Arc<impl UserStore>,
|
||||||
frontend_config: FrontendConfig,
|
frontend_config: FrontendConfig,
|
||||||
) -> App<
|
) -> App<
|
||||||
impl ServiceFactory<
|
impl ServiceFactory<
|
||||||
@@ -30,6 +31,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
|
|||||||
.wrap(TracingLogger::default())
|
.wrap(TracingLogger::default())
|
||||||
.wrap(NormalizePath::trim())
|
.wrap(NormalizePath::trim())
|
||||||
.service(web::scope("/caldav").service(caldav_service(
|
.service(web::scope("/caldav").service(caldav_service(
|
||||||
|
user_store.clone(),
|
||||||
auth_provider.clone(),
|
auth_provider.clone(),
|
||||||
cal_store.clone(),
|
cal_store.clone(),
|
||||||
addr_store.clone(),
|
addr_store.clone(),
|
||||||
@@ -37,6 +39,7 @@ pub fn make_app<AS: AddressbookStore, CS: CalendarStore, S: SubscriptionStore>(
|
|||||||
)))
|
)))
|
||||||
.service(web::scope("/carddav").service(carddav_service(
|
.service(web::scope("/carddav").service(carddav_service(
|
||||||
auth_provider.clone(),
|
auth_provider.clone(),
|
||||||
|
user_store.clone(),
|
||||||
addr_store.clone(),
|
addr_store.clone(),
|
||||||
subscription_store,
|
subscription_store,
|
||||||
)))
|
)))
|
||||||
|
|||||||
@@ -100,6 +100,7 @@ async fn main() -> Result<()> {
|
|||||||
cal_store.clone(),
|
cal_store.clone(),
|
||||||
subscription_store.clone(),
|
subscription_store.clone(),
|
||||||
user_store.clone(),
|
user_store.clone(),
|
||||||
|
user_store.clone(),
|
||||||
config.frontend.clone(),
|
config.frontend.clone(),
|
||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user