UNPKG

@aziontech/opennextjs-azion

Version:
124 lines (123 loc) 5.47 kB
/** * This code was originally copied and modified from the @opennextjs/aws repository. * Significant changes have been made to adapt it for use with Azion. */ import { IgnorableError, RecoverableError } from "@opennextjs/aws/utils/error.js"; import { getAzionContext } from "../../../api/azion-context.js"; import CacheApi from "../../../api/cache-api/index.js"; import { debugCache, FALLBACK_BUILD_ID } from "../internal.js"; // Assets inside `data-cache/...` are only accessible by the worker. export const CACHE_DIR = "data-cache/_next_cache"; export const NAME = "azion-storage-incremental-cache"; const BUILD_ID = process.env.NEXT_BUILD_ID ?? FALLBACK_BUILD_ID; /** * This cache uses Workers storage. * */ class StorageIncrementalCache { name = NAME; storageCachePrefix = `${BUILD_ID}`; async get(key, cacheType) { debugCache(`StorageIncrementalCache - Get ${key}`, cacheType); try { const cacheContent = await this.getCacheApiOrStorage(key, cacheType ?? "cache"); if (!cacheContent) { debugCache(`StorageIncrementalCache - MISS for key: ${key}`); return null; } const cacheValueParsed = JSON.parse(cacheContent); return { value: cacheValueParsed, // __BUILD_TIMESTAMP_MS__ is injected by ESBuild. lastModified: cacheValueParsed.lastModified ? Number(cacheValueParsed.lastModified) : globalThis.__BUILD_TIMESTAMP_MS__, }; } catch (e) { debugCache("Failed to get from cache", e?.message); return null; } } async set(key, value, cacheType) { try { debugCache(`StorageIncrementalCache - Set ${key}`, cacheType); await this.setCacheApiOrStorage(key, value, cacheType ?? "cache"); // eslint-disable-next-line @typescript-eslint/no-unused-vars } catch (e) { throw new RecoverableError(`Failed to set cache [${key}]`); } } async delete(key) { debugCache(`StorageIncrementalCache - Delete ${key}`); try { debugCache(`StorageIncrementalCache - Deleting cache for key: ${key}`); } catch { throw new RecoverableError(`Failed to delete cache [${key}]`); } } getCacheURL(key, cacheType, bucketPrefix) { const buildId = process.env.NEXT_BUILD_ID ?? FALLBACK_BUILD_ID; const name = (cacheType === "fetch" ? `${bucketPrefix}/${CACHE_DIR}/__fetch/${buildId}/${key}` : `${bucketPrefix}/${CACHE_DIR}/${buildId}/${key}.cache`).replace(/\/+/g, "/"); return name; } async getCacheApiOrStorage(key, cacheType) { const azionContext = getAzionContext(); const resCacheAPI = await CacheApi.getCacheAPI(`${this.storageCachePrefix}_${azionContext.env.AZION?.CACHE_API_STORAGE_NAME}`, key).catch((e) => { debugCache(e.message); return null; }); if (resCacheAPI) { debugCache("StorageIncrementalCache - HIT by CACHE_API for key:", key); return resCacheAPI; } debugCache("StorageIncrementalCache - MISS by CACHE_API for key:", key, "falling back to storage"); const keyURL = this.getCacheURL(key, cacheType, azionContext.env.AZION?.BUCKET_PREFIX); const fileValue = await azionContext.env.AZION?.Storage.get(keyURL).catch(() => { debugCache(`StorageIncrementalCache - Cache file not exist on storage`); return null; }); if (!fileValue) throw new IgnorableError(`StorageIncrementalCache - Cache file not found at ${key}`); const fileValueArray = await fileValue.arrayBuffer(); const decoder = new TextDecoder(); const cacheContent = decoder.decode(fileValueArray); debugCache("StorageIncrementalCache - HIT by Storage for key:", key); // set cache API because ssg pages are not stored in first run if (!resCacheAPI) { debugCache("StorageIncrementalCache - Setting CACHE_API FIRST TIME:", key); this.setCacheApiOrStorage(key, JSON.parse(cacheContent), cacheType, true).catch((e) => { debugCache("Failed to set cache API:", e.message); }); } return cacheContent; } async setCacheApiOrStorage(key, value, cacheType, onlyCacheApi = false) { const azionContext = getAzionContext(); const newCacheValue = JSON.stringify({ ...value, lastModified: Date.now(), }); // Cache API await CacheApi.putCacheAPIkey(`${this.storageCachePrefix}_${azionContext.env.AZION?.CACHE_API_STORAGE_NAME}`, key, newCacheValue).catch((e) => { debugCache(e.message); return null; }); // Storage API if (onlyCacheApi) { return; } const encoder = new TextEncoder(); const newCacheValueBuffer = encoder.encode(newCacheValue); const keyURL = this.getCacheURL(key, cacheType, azionContext.env.AZION?.BUCKET_PREFIX); await azionContext.env.AZION?.Storage.put(keyURL, newCacheValueBuffer, { metadata: { id: `${BUILD_ID}` }, }); debugCache("StorageIncrementalCache - written to storage for key:", key); } } export default new StorageIncrementalCache();