import fs from "fs" import crypto from 'crypto' import { asyncForEach } from "./utils" import { ModuleWithInit } from "@agility/types" //Agility API stuff import { agilityConfig, getSyncClient, prepIncrementalMode } from './agility.config' import GlobalFooter from 'components/agility-global/GlobalFooter' import GlobalHeader from 'components/agility-global/GlobalHeader' import moduleInitializers from "framework/module-data" const securityKey = agilityConfig.securityKey const channelName = agilityConfig.channelName const languageCode = agilityConfig.languageCode const isDevelopmentMode = process.env.NODE_ENV === "development" interface AgilityPageProps { sitemapNode?: any, page?: any, dynamicPageItem?: any, pageTemplateName?:string|null, globalHeaderProps?:any, globalFooterProps?:any, languageCode?:string|null, channelName?:string|null, isPreview?:boolean, isDevelopmentMode?:boolean, notFound?:boolean } const getAgilityPageProps = async ({ params, preview, locale }:any):Promise => { let path = '/'; if (params) { //build path by iterating through slugs path = ''; params.slug.map((slug: string) => { path += '/' + slug }) } //determine if we've already done a full build yet const buildFilePath = `${process.cwd()}/.next/cache/agility/build.log` const isBuildComplete = fs.existsSync(buildFilePath) //TODO: use locale to determin LANGUAGECODE (pulled from config at this point...) //determine if we are in preview mode const isPreview:boolean = (preview || isDevelopmentMode); const agilitySyncClient = getSyncClient({ isPreview: isPreview, isDevelopmentMode, isIncremental: isBuildComplete }); if (! agilitySyncClient) { console.log("AgilityCMS => Sync client could not be accessed.") return { notFound: true }; } if (preview || isBuildComplete) { //only do on-demand sync in next's preview mode or incremental build... console.log(`AgilityCMS => Sync On-demand ${isPreview ? "Preview" : "Live"} Mode`) await prepIncrementalMode() await agilitySyncClient.runSync(); } console.log(`AgilityCMS => Getting page props for '${path}'...`); //get sitemap const sitemap = await agilitySyncClient.store.getSitemap({ channelName, languageCode }); if (sitemap === null) { console.warn("No sitemap found after sync."); } let pageInSitemap = sitemap[path]; let page: any = null; let dynamicPageItem: any = null; if (path === '/') { let firstPagePathInSitemap = Object.keys(sitemap)[0]; pageInSitemap = sitemap[firstPagePathInSitemap]; } if (pageInSitemap) { //get the page page = await agilitySyncClient.store.getPage({ pageID: pageInSitemap.pageID, languageCode: languageCode }); } else { //Could not find page console.warn('page [' + path + '] not found in sitemap.'); return handlePageNotFound(); } if (!page) { console.warn('page [' + path + '] not found in getpage method.'); } //if there is a dynamic page content id on this page, grab it... if (pageInSitemap.contentID > 0) { dynamicPageItem = await agilitySyncClient.store.getContentItem({ contentID: pageInSitemap.contentID, languageCode: languageCode }); } //resolve the page template const pageTemplateName = page.templateName.replace(/[^0-9a-zA-Z]/g, ''); //resolve the modules per content zone await asyncForEach(Object.keys(page.zones), async (zoneName: string) => { let modules: { moduleName: string; item: any }[] = []; //grab the modules for this content zone const modulesForThisContentZone = page.zones[zoneName]; //loop through the zone's modules await asyncForEach(modulesForThisContentZone, async (moduleItem: { module: string, item: any }) => { //find the react component to use for the module const moduleInitializer = moduleInitializers(moduleItem.module) if (moduleInitializer) { //resolve any additional data for the modules //we have some additional data in the module we'll need, execute that method now, so it can be included in SSG if (isDevelopmentMode) { console.log(`AgilityCMS => Fetching additional data via getCustomInitialProps for ${moduleItem.module}...`); } const moduleData = await moduleInitializer({ item: moduleItem.item, agility: agilitySyncClient.store, languageCode, channelName, pageInSitemap, dynamicPageItem }); //if we have additional module data, then add it to the module props using 'customData' if (moduleData != null) { moduleItem.item.customData = moduleData; } } modules.push({ moduleName: moduleItem.module, item: moduleItem.item, }) }) //store as dictionary page.zones[zoneName] = modules; }) //resolve data for other shared components const globalHeaderProps = await GlobalHeader.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName }); const globalFooterProps = await GlobalFooter.getCustomInitialProps({ agility: agilitySyncClient.store, languageCode: languageCode, channelName: channelName }); return { sitemapNode: pageInSitemap, page, dynamicPageItem, pageTemplateName, globalHeaderProps, globalFooterProps, languageCode, channelName, isPreview, isDevelopmentMode } } const getAgilityPaths = async (preview:boolean|undefined) => { console.log(`AgilityCMS => Fetching sitemap for getAgilityPaths...`); //determine if we are in preview mode const isPreview = isDevelopmentMode || preview; //determine if we've already done a full build yet const buildFilePath = `${process.cwd()}/.next/cache/agility/build.log` const isBuildComplete = fs.existsSync(buildFilePath) const agilitySyncClient = getSyncClient({ isPreview, isDevelopmentMode, isIncremental: isBuildComplete }); if (! agilitySyncClient) { console.log("AgilityCMS => Sync client could not be accessed.") return []; } const sitemapFlat = await agilitySyncClient.store.getSitemap({ channelName, languageCode }) if (!sitemapFlat) { console.warn("AgilityCMS => No Site map found. Make sure your environment variables are setup correctly.") return [] } //returns an array of paths as a string (i.e. ['/home', '/posts'] ) const paths = Object.keys(sitemapFlat) .filter(path => { const sitemapNode = sitemapFlat[path] return !sitemapNode.redirect && !sitemapNode.isFolder }) return paths } const validatePreview = async({ agilityPreviewKey, slug }: any) => { //Validate the preview key if (!agilityPreviewKey) { return { error: true, message: `Missing agilitypreviewkey.` } } //sanitize incoming key (replace spaces with '+') if (agilityPreviewKey.indexOf(` `) > -1) { agilityPreviewKey = agilityPreviewKey.split(` `).join(`+`); } //compare the preview key being used const correctPreviewKey = generatePreviewKey(); if (agilityPreviewKey !== correctPreviewKey) { return { error: true, message: `Invalid agilitypreviewkey.` //message: `Invalid agilitypreviewkey. Incoming key is=${agilityPreviewKey} compared to=${correctPreviewKey}...` } } //return success return { error: false, message: null } } const generatePreviewKey =() => { //the string we want to encode const str = `-1_${securityKey}_Preview`; //build our byte array let data = []; for (var i = 0; i < str.length; ++i) { data.push(str.charCodeAt(i)); data.push(0); } //convert byte array to buffer const strBuffer = Buffer.from(data); //encode it! const previewKey = crypto.createHash('sha512').update(strBuffer).digest('base64'); return previewKey; } function handlePageNotFound() { return { notFound: true } } export { getAgilityPageProps, getAgilityPaths, validatePreview, generatePreviewKey }