import axios from 'axios' import { scaffoldProducts } from '@/src/trpc/apis/common-apis/common' import { scaffoldEssentialConsts } from '@/src/trpc/apis/common-apis/common-trpc-index' import { scaffoldStores } from '@/src/trpc/apis/user-apis/apis/stores' import { scaffoldSlotsWithProducts } from '@/src/trpc/apis/user-apis/apis/slots' import { scaffoldBanners } from '@/src/trpc/apis/user-apis/apis/banners' import { scaffoldStoreWithProducts } from '@/src/trpc/apis/user-apis/apis/stores' import { storeInfo } from '../db/schema' import { db } from '../db/db_index' import { imageUploadS3 } from '@/src/lib/s3-client' import { apiCacheKey, cloudflareApiToken, cloudflareZoneId, assetsDomain } from '@/src/lib/env-exporter' import { CACHE_FILENAMES } from '@packages/shared' import { retryWithExponentialBackoff } from '@/src/lib/retry' function constructCacheUrl(path: string): string { return `${assetsDomain}${apiCacheKey}/${path}` } export async function createProductsFile(): Promise { // Get products data from the API method const productsData = await scaffoldProducts() // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(productsData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.products}`) // Purge cache with retry const url = constructCacheUrl(CACHE_FILENAMES.products) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createEssentialConstsFile(): Promise { // Get essential consts data from the API method const essentialConstsData = await scaffoldEssentialConsts() // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(essentialConstsData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.essentialConsts}`) // Purge cache with retry const url = constructCacheUrl(CACHE_FILENAMES.essentialConsts) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createStoresFile(): Promise { // Get stores data from the API method const storesData = await scaffoldStores() // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(storesData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.stores}`) // Purge cache with retry const url = constructCacheUrl(CACHE_FILENAMES.stores) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createSlotsFile(): Promise { // Get slots data from the API method const slotsData = await scaffoldSlotsWithProducts() // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(slotsData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.slots}`) // Purge cache with retry const url = constructCacheUrl(CACHE_FILENAMES.slots) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createBannersFile(): Promise { // Get banners data from the API method const bannersData = await scaffoldBanners() // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(bannersData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.banners}`) // Purge cache with retry const url = constructCacheUrl(CACHE_FILENAMES.banners) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createStoreFile(storeId: number): Promise { // Get store data from the API method const storeData = await scaffoldStoreWithProducts(storeId) // Convert to JSON string with pretty formatting const jsonContent = JSON.stringify(storeData, null, 2) // Convert to Buffer for S3 upload const buffer = Buffer.from(jsonContent, 'utf-8') // Upload to S3 at the specified path using apiCacheKey const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/stores/${storeId}.json`) // Purge cache with retry const url = constructCacheUrl(`stores/${storeId}.json`) try { await retryWithExponentialBackoff(() => clearUrlCache([url])) console.log(`Cache purged for ${url}`) } catch (error) { console.error(`Failed to purge cache for ${url} after 3 retries:`, error) } return s3Key } export async function createAllStoresFiles(): Promise { // Fetch all store IDs from database const stores = await db.select({ id: storeInfo.id }).from(storeInfo) // Create cache files for all stores and collect URLs const results: string[] = [] const urls: string[] = [] for (const store of stores) { const s3Key = await createStoreFile(store.id) results.push(s3Key) urls.push(constructCacheUrl(`stores/${store.id}.json`)) } console.log(`Created ${results.length} store cache files`) // Purge all store caches in one batch with retry try { await retryWithExponentialBackoff(() => clearUrlCache(urls)) console.log(`Cache purged for ${urls.length} store files`) } catch (error) { console.error(`Failed to purge cache for store files after 3 retries. URLs: ${urls.join(', ')}`, error) } return results } export interface CreateAllCacheFilesResult { products: string essentialConsts: string stores: string slots: string banners: string individualStores: string[] } export async function createAllCacheFiles(): Promise { console.log('Starting creation of all cache files...') // Create all global cache files in parallel const [ productsKey, essentialConstsKey, storesKey, slotsKey, bannersKey, individualStoreKeys, ] = await Promise.all([ createProductsFileInternal(), createEssentialConstsFileInternal(), createStoresFileInternal(), createSlotsFileInternal(), createBannersFileInternal(), createAllStoresFilesInternal(), ]) // Collect all URLs for batch cache purge const urls = [ constructCacheUrl(CACHE_FILENAMES.products), constructCacheUrl(CACHE_FILENAMES.essentialConsts), constructCacheUrl(CACHE_FILENAMES.stores), constructCacheUrl(CACHE_FILENAMES.slots), constructCacheUrl(CACHE_FILENAMES.banners), ...individualStoreKeys.map((_, index) => constructCacheUrl(`stores/${index + 1}.json`)), ] // Purge all caches in one batch with retry try { await retryWithExponentialBackoff(() => clearUrlCache(urls)) console.log(`Cache purged for all ${urls.length} files`) } catch (error) { console.error(`Failed to purge cache for all files after 3 retries`, error) } console.log('All cache files created successfully') return { products: productsKey, essentialConsts: essentialConstsKey, stores: storesKey, slots: slotsKey, banners: bannersKey, individualStores: individualStoreKeys, } } // Internal versions that skip cache purging (for batch operations) async function createProductsFileInternal(): Promise { const productsData = await scaffoldProducts() const jsonContent = JSON.stringify(productsData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') return await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.products}`) } async function createEssentialConstsFileInternal(): Promise { const essentialConstsData = await scaffoldEssentialConsts() const jsonContent = JSON.stringify(essentialConstsData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') return await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.essentialConsts}`) } async function createStoresFileInternal(): Promise { const storesData = await scaffoldStores() const jsonContent = JSON.stringify(storesData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') return await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.stores}`) } async function createSlotsFileInternal(): Promise { const slotsData = await scaffoldSlotsWithProducts() const jsonContent = JSON.stringify(slotsData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') return await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.slots}`) } async function createBannersFileInternal(): Promise { const bannersData = await scaffoldBanners() const jsonContent = JSON.stringify(bannersData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') return await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/${CACHE_FILENAMES.banners}`) } async function createAllStoresFilesInternal(): Promise { const stores = await db.select({ id: storeInfo.id }).from(storeInfo) const results: string[] = [] for (const store of stores) { const storeData = await scaffoldStoreWithProducts(store.id) const jsonContent = JSON.stringify(storeData, null, 2) const buffer = Buffer.from(jsonContent, 'utf-8') const s3Key = await imageUploadS3(buffer, 'application/json', `${apiCacheKey}/stores/${store.id}.json`) results.push(s3Key) } console.log(`Created ${results.length} store cache files`) return results } export async function clearUrlCache(urls: string[]): Promise<{ success: boolean; errors?: string[] }> { if (!cloudflareApiToken || !cloudflareZoneId) { console.warn('Cloudflare credentials not configured, skipping cache clear') return { success: false, errors: ['Cloudflare credentials not configured'] } } try { const response = await axios.post( `https://api.cloudflare.com/client/v4/zones/${cloudflareZoneId}/purge_cache`, { files: urls }, { headers: { 'Authorization': `Bearer ${cloudflareApiToken}`, 'Content-Type': 'application/json', }, } ) const result = response.data as { success: boolean; errors?: { message: string }[] } if (!result.success) { const errorMessages = result.errors?.map(e => e.message) || ['Unknown error'] console.error(`Cloudflare cache purge failed for URLs: ${urls.join(', ')}`, errorMessages) return { success: false, errors: errorMessages } } console.log(`Successfully purged ${urls.length} URLs from Cloudflare cache: ${urls.join(', ')}`) return { success: true } } catch (error) { console.log(error) const errorMessage = error instanceof Error ? error.message : 'Unknown error' console.error(`Error clearing Cloudflare cache for URLs: ${urls.join(', ')}`, errorMessage) return { success: false, errors: [errorMessage] } } } export async function clearAllCache(): Promise<{ success: boolean; errors?: string[] }> { if (!cloudflareApiToken || !cloudflareZoneId) { console.warn('Cloudflare credentials not configured, skipping cache clear') return { success: false, errors: ['Cloudflare credentials not configured'] } } try { const response = await axios.post( `https://api.cloudflare.com/client/v4/zones/${cloudflareZoneId}/purge_cache`, { purge_everything: true }, { headers: { 'Authorization': `Bearer ${cloudflareApiToken}`, 'Content-Type': 'application/json', }, } ) const result = response.data as { success: boolean; errors?: { message: string }[] } if (!result.success) { const errorMessages = result.errors?.map(e => e.message) || ['Unknown error'] console.error('Cloudflare cache purge failed:', errorMessages) return { success: false, errors: errorMessages } } console.log('Successfully purged all cache from Cloudflare') return { success: true } } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error' console.error('Error clearing Cloudflare cache:', errorMessage) return { success: false, errors: [errorMessage] } } }