diff --git a/apps/frontend/app/lib/utilities.server.ts b/apps/frontend/app/lib/utilities.server.ts index b23f134d56..0532daad47 100644 --- a/apps/frontend/app/lib/utilities.server.ts +++ b/apps/frontend/app/lib/utilities.server.ts @@ -174,13 +174,10 @@ export const getDecodedJwt = (request: Request) => { }>(token); }; -export const getCachedCoreDetails = async () => { - return await queryClient.ensureQueryData({ - queryKey: queryFactory.miscellaneous.coreDetails().queryKey, - staleTime: dayjsLib.duration({ minutes: 5 }).asMilliseconds(), - queryFn: () => - serverGqlService.request(CoreDetailsDocument).then((d) => d.coreDetails), - }); +export const getCoreDetails = async () => { + return await serverGqlService + .request(CoreDetailsDocument) + .then((d) => d.coreDetails); }; const getCachedUserDetails = async (request: Request) => { @@ -353,7 +350,7 @@ export const getCookiesForApplication = async ( token: string, tokenValidForDays?: number, ) => { - const [coreDetails] = await Promise.all([getCachedCoreDetails()]); + const [coreDetails] = await Promise.all([getCoreDetails()]); const maxAge = (tokenValidForDays || coreDetails.tokenValidForDays) * 24 * 60 * 60; const options = { maxAge, path: "/" } satisfies SerializeOptions; @@ -400,7 +397,7 @@ export const redirectToFirstPageIfOnInvalidPage = async ( totalResults: number, currentPage: number, ) => { - const coreDetails = await getCachedCoreDetails(); + const coreDetails = await getCoreDetails(); const { searchParams } = new URL(request.url); const totalPages = Math.ceil(totalResults / coreDetails.pageSize); if (currentPage > totalPages && currentPage !== 1) { diff --git a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx index 2bc566eb06..bb825446bd 100644 --- a/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx +++ b/apps/frontend/app/routes/_dashboard.media.$action.$lot.tsx @@ -80,7 +80,7 @@ import { useMetadataProgressUpdate, } from "~/lib/state/media"; import { - getCachedCoreDetails, + getCoreDetails, getEnhancedCookieName, redirectToFirstPageIfOnInvalidPage, redirectUsingEnhancedCookieSearchParams, @@ -153,7 +153,7 @@ export const loader = async ({ request, params }: LoaderFunctionArgs) => { ] as const; }) .with(Action.Search, async () => { - const coreDetails = await getCachedCoreDetails(); + const coreDetails = await getCoreDetails(); const metadataSourcesForLot = coreDetails.metadataLotSourceMappings.find( (m) => m.lot === lot, ); diff --git a/apps/frontend/app/routes/_dashboard.tsx b/apps/frontend/app/routes/_dashboard.tsx index 4985db0f91..aba2196ebe 100644 --- a/apps/frontend/app/routes/_dashboard.tsx +++ b/apps/frontend/app/routes/_dashboard.tsx @@ -140,10 +140,10 @@ import { useReviewEntity, } from "~/lib/state/media"; import { - getCachedCoreDetails, getCachedUserCollectionsList, getCachedUserPreferences, getCookieValue, + getCoreDetails, getDecodedJwt, redirectIfNotAuthenticatedOrUpdated, } from "~/lib/utilities.server"; @@ -164,7 +164,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { const [userPreferences, userCollections, coreDetails] = await Promise.all([ getCachedUserPreferences(request), getCachedUserCollectionsList(request), - getCachedCoreDetails(), + getCoreDetails(), ]); const desktopSidebarCollapsed = getCookieValue( request, diff --git a/apps/frontend/app/routes/api.auth.tsx b/apps/frontend/app/routes/api.auth.tsx index 6887c11f83..bb750a4252 100644 --- a/apps/frontend/app/routes/api.auth.tsx +++ b/apps/frontend/app/routes/api.auth.tsx @@ -10,8 +10,8 @@ import { $path } from "remix-routes"; import { z } from "zod"; import { zx } from "zodix"; import { - getCachedCoreDetails, getCookiesForApplication, + getCoreDetails, redirectWithToast, serverGqlService, } from "~/lib/utilities.server"; @@ -49,7 +49,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { type: "error", }); } - await getCachedCoreDetails(); + await getCoreDetails(); const { loginUser } = await serverGqlService.request(LoginUserDocument, { input: { oidc: oidcInput }, }); diff --git a/apps/frontend/app/routes/auth.tsx b/apps/frontend/app/routes/auth.tsx index e2576456fb..f95a86e382 100644 --- a/apps/frontend/app/routes/auth.tsx +++ b/apps/frontend/app/routes/auth.tsx @@ -38,8 +38,8 @@ import { dayjsLib, redirectToQueryParam } from "~/lib/generals"; import { createToastHeaders, getAuthorizationCookie, - getCachedCoreDetails, getCookiesForApplication, + getCoreDetails, redirectWithToast, serverGqlService, } from "~/lib/utilities.server"; @@ -68,7 +68,7 @@ export const loader = async ({ request }: LoaderFunctionArgs) => { }, ); } - const [coreDetails] = await Promise.all([getCachedCoreDetails()]); + const [coreDetails] = await Promise.all([getCoreDetails()]); return { intent: query.intent || "login", oidcEnabled: coreDetails.oidcEnabled, diff --git a/crates/config/src/lib.rs b/crates/config/src/lib.rs index 6aee69e09d..23a3651b07 100644 --- a/crates/config/src/lib.rs +++ b/crates/config/src/lib.rs @@ -279,16 +279,16 @@ impl IsFeatureEnabled for FileStorageConfig { /// The configuration related to Umami analytics. More information /// [here](https://umami.is/docs/tracker-configuration). -#[derive(Debug, Serialize, Deserialize, Clone, Config, SimpleObject)] +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Config, SimpleObject)] #[config(rename_all = "snake_case", env_prefix = "FRONTEND_UMAMI_")] pub struct FrontendUmamiConfig { + pub domains: String, /// For example: https://umami.is/script.js. pub script_url: String, pub website_id: String, - pub domains: String, } -#[derive(Debug, Serialize, Deserialize, Clone, Config, SimpleObject)] +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, Clone, Config, SimpleObject)] #[config(rename_all = "snake_case", env_prefix = "FRONTEND_")] pub struct FrontendConfig { /// Used as the base URL when generating item links for the frontend. diff --git a/crates/models/common/src/lib.rs b/crates/models/common/src/lib.rs index db06d89960..83350a9260 100644 --- a/crates/models/common/src/lib.rs +++ b/crates/models/common/src/lib.rs @@ -320,6 +320,7 @@ pub struct ProgressUpdateCacheInput { #[skip_serializing_none] #[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Eq, Serialize, Deserialize)] pub enum ApplicationCacheKey { + CoreDetails, IgdbSettings, TmdbSettings, ServerKeyValidated, diff --git a/crates/models/dependent/src/lib.rs b/crates/models/dependent/src/lib.rs index d0a655ecde..8aa47ff775 100644 --- a/crates/models/dependent/src/lib.rs +++ b/crates/models/dependent/src/lib.rs @@ -159,46 +159,46 @@ pub struct MetadataBaseData { pub creators: Vec, } -#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, SimpleObject, Clone)] pub struct ExerciseParametersLotMapping { pub lot: ExerciseLot, pub bests: Vec, } -#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, SimpleObject, Clone)] pub struct ExerciseFilters { #[graphql(name = "type")] pub lot: Vec, pub level: Vec, pub force: Vec, + pub muscle: Vec, pub mechanic: Vec, pub equipment: Vec, - pub muscle: Vec, } -#[derive(Debug, Serialize, Deserialize, SimpleObject, Clone)] +#[derive(PartialEq, Eq, Debug, Serialize, Deserialize, SimpleObject, Clone)] pub struct ExerciseParameters { + pub download_required: bool, /// All filters applicable to an exercises query. pub filters: ExerciseFilters, - pub download_required: bool, /// Exercise type mapped to the personal bests possible. pub lot_mapping: Vec, } -#[derive(Debug, SimpleObject, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Debug, SimpleObject, Serialize, Deserialize, Clone)] pub struct ProviderLanguageInformation { pub source: MediaSource, pub supported: Vec, pub default: String, } -#[derive(Debug, SimpleObject, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Debug, SimpleObject, Serialize, Deserialize, Clone)] pub struct MetadataLotSourceMappings { pub lot: MediaLot, pub sources: Vec, } -#[derive(Debug, SimpleObject, Serialize, Deserialize)] +#[derive(PartialEq, Eq, Clone, Debug, SimpleObject, Serialize, Deserialize)] pub struct CoreDetails { pub page_size: i32, pub version: String, @@ -430,6 +430,7 @@ pub type MetadataGroupSearchResponse = SearchResults; #[derive(Clone, Debug, PartialEq, FromJsonQueryResult, Serialize, Deserialize, Eq)] pub enum ApplicationCacheValue { Empty(EmptyCacheValue), + CoreDetails(CoreDetails), TmdbSettings(TmdbSettings), IgdbSettings(IgdbSettings), UserAnalytics(UserAnalytics), diff --git a/crates/services/cache/src/lib.rs b/crates/services/cache/src/lib.rs index de6492d171..18ea61ea69 100644 --- a/crates/services/cache/src/lib.rs +++ b/crates/services/cache/src/lib.rs @@ -33,7 +33,8 @@ impl CacheService { | ApplicationCacheKey::ServerKeyValidated | ApplicationCacheKey::TmdbSettings => None, - ApplicationCacheKey::MetadataRecentlyConsumed { .. } + ApplicationCacheKey::CoreDetails + | ApplicationCacheKey::MetadataRecentlyConsumed { .. } | ApplicationCacheKey::MetadataSearch { .. } | ApplicationCacheKey::PeopleSearch { .. } | ApplicationCacheKey::MetadataGroupSearch { .. } => Some(1), diff --git a/crates/services/miscellaneous/src/lib.rs b/crates/services/miscellaneous/src/lib.rs index 94aec50d72..56da9c7a5e 100644 --- a/crates/services/miscellaneous/src/lib.rs +++ b/crates/services/miscellaneous/src/lib.rs @@ -252,12 +252,20 @@ ORDER BY RANDOM() LIMIT 10; } pub async fn core_details(&self) -> Result { + if let Some(cached) = self + .0 + .cache_service + .get_value(ApplicationCacheKey::CoreDetails) + .await? + { + return Ok(cached); + } let mut files_enabled = self.0.config.file_storage.is_enabled(); if files_enabled && !self.0.file_storage_service.is_enabled().await { files_enabled = false; } let download_required = Exercise::find().count(&self.0.db).await? == 0; - Ok(CoreDetails { + let core_details = CoreDetails { page_size: PAGE_SIZE, version: APP_VERSION.to_owned(), file_storage_enabled: files_enabled, @@ -357,7 +365,15 @@ ORDER BY RANDOM() LIMIT 10; } }) .collect(), - }) + }; + self.0 + .cache_service + .set_key( + ApplicationCacheKey::CoreDetails, + ApplicationCacheValue::CoreDetails(core_details.clone()), + ) + .await?; + Ok(core_details) } async fn metadata_assets(&self, meta: &metadata::Model) -> Result {