Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 5x 5x 5x 48x 13x 3x 3x 40x 40x 38x 32x 32x 1x 32x 6x 5x 4x 4x 4x 3x 3x 7x 7x 4x | // Pre-compute and cache enriched project JSON for marketplace reads
import type { Dal } from "../dal";
import type { Services } from "../services";
import type { getDb } from "../db";
import {
enrichProjectsWithTaxonomy,
type ProjectWithTaxonomy,
} from "../routes/marketplace/project-enrichment";
import { cache } from "./cache";
/** Cache key prefix for enriched project by ID */
const PROJECT_CACHE_PREFIX = "project:enriched:";
/** Cache key prefix for enriched project by slug */
const PROJECT_SLUG_CACHE_PREFIX = "project:enriched:slug:";
/** Default TTL for enriched project cache (5 minutes) */
const PROJECT_CACHE_TTL = 300;
/**
* Build the cache key for an enriched project by ID
*/
export function projectCacheKey(projectId: string): string {
return `${PROJECT_CACHE_PREFIX}${projectId}`;
}
/**
* Build the cache key for an enriched project by slug
*/
export function projectSlugCacheKey(slug: string): string {
return `${PROJECT_SLUG_CACHE_PREFIX}${slug}`;
}
/**
* Get a cached enriched project by ID.
* Returns null on cache miss.
*/
export async function getCachedEnrichedProject(
projectId: string,
): Promise<ProjectWithTaxonomy | null> {
return cache.get<ProjectWithTaxonomy>(projectCacheKey(projectId));
}
/**
* Get a cached enriched project by slug.
* Returns null on cache miss.
*/
export async function getCachedEnrichedProjectBySlug(
slug: string,
): Promise<ProjectWithTaxonomy | null> {
return cache.get<ProjectWithTaxonomy>(projectSlugCacheKey(slug));
}
/**
* Pre-compute the enriched project JSON and store in cache.
*
* Called after project mutations (publish, update, photo/room/media changes).
* If the project is not published or doesn't exist, clears stale cache entries.
*
* Designed to be called via `c.executionCtx.waitUntil()` for non-blocking execution.
*/
export async function precomputeEnrichedProject(
projectId: string,
db: ReturnType<typeof getDb>,
dal: Dal,
services: Services,
): Promise<void> {
try {
const project = await dal.projects.findById(projectId);
if (!project || project.status !== "published") {
// Remove stale cache entries
await cache.delete(projectCacheKey(projectId));
if (project?.slug) {
await cache.delete(projectSlugCacheKey(project.slug));
}
return;
}
// Use existing enrichment logic with rooms and media included
const enriched = await enrichProjectsWithTaxonomy(
db,
services,
dal,
[project],
true,
);
if (enriched.length > 0) {
const enrichedProject = enriched[0];
// Cache by project ID
await cache.put(projectCacheKey(project.id), enrichedProject, {
expirationTtl: PROJECT_CACHE_TTL,
});
// Also cache by slug for slug-based lookups
if (project.slug) {
await cache.put(projectSlugCacheKey(project.slug), enrichedProject, {
expirationTtl: PROJECT_CACHE_TTL,
});
}
}
} catch (err) {
// Log but don't throw - precompute failures should not break mutations
console.error("Failed to precompute enriched project:", projectId, err);
}
}
/**
* Invalidate cached enriched project data (by ID and slug).
* Useful when a project is deleted or unpublished.
*/
export async function invalidateProjectCache(
projectId: string,
slug?: string | null,
): Promise<void> {
await cache.delete(projectCacheKey(projectId));
if (slug) {
await cache.delete(projectSlugCacheKey(slug));
}
}
|