Skip to content

Instantly share code, notes, and snippets.

@gonecase
Last active June 18, 2020 01:44
Show Gist options
  • Save gonecase/82ab2ddb1603b0d2ece872d8ebf2e590 to your computer and use it in GitHub Desktop.
Save gonecase/82ab2ddb1603b0d2ece872d8ebf2e590 to your computer and use it in GitHub Desktop.
import createApi from './helpers/api';
import _ from 'lodash';
const siteMapItemFromContentId = (sitemap, contentID) => {
const paths = Object.keys(sitemap).filter(path => !!sitemap[path].contentID && `${contentID}` === `${sitemap[path].contentID}`);
if (paths.length > 0) {
return sitemap[paths[0]];
}
return false;
}
const createAgilityMiddleware = (config, options) => {
if (!config) {
return (req, res, next) => {
next();
}
}
// create agility API
const prodApi = createApi(config.guid, config.productionKey);
const previewApi = createApi(config.guid, config.previewKey, true);
return async (req, res, next) => {
// to make sure it wont redirect to 404 when request favicon.ico
if(req.url.toLowerCase() == '/favicon.ico') {
next();
}
// check if there is a url param in the req agilitypreviewkey has length of over 10 characters
const isPreview = !!req.query.agilitypreviewkey;
const api = (isPreview) ? previewApi : prodApi;
let sitemap = await api.getSitemap();
let sitemapNested = await api.getSitemapNested();
// handle redirects with contentID in query param
const contentIdRedirect = siteMapItemFromContentId(sitemap, req.query.contentID);
if (!!contentIdRedirect) {
res.redirect(contentIdRedirect.path);
return;
}
// get all content items we need beforehand
let prefetchedContentItems = await Promise.all(options.prefetch.contentItems.map( i => api.getContentItem(i) ));
// get all content lists we need beforehand
let prefetchedContentLists = await Promise.all(options.prefetch.contentLists.map( i => api.getContentListAll(i) ));
let currentUrlSlug = req ? req.path.toLowerCase() : pathname;
currentUrlSlug = (currentUrlSlug === '/' || currentUrlSlug === '') ? '/home' : currentUrlSlug;
if (currentUrlSlug[currentUrlSlug.length - 1] == '/') {
currentUrlSlug = currentUrlSlug.substring(0, currentUrlSlug.length - 1);
}
let thisSiteMapItem = sitemap[currentUrlSlug];
// REDIRECT TO 404 - look into withRouter
if (typeof thisSiteMapItem == 'undefined') {
thisSiteMapItem = {
redirect: {url: options.error404}
}
}
// HANDLE REDIRECTS and Do not process if not needed
if (!!thisSiteMapItem.redirect) {
let redirectLocation = thisSiteMapItem.redirect.url.replace('~', '');
res.redirect(redirectLocation);
return;
}
var currentPage = await api.getPage(thisSiteMapItem.pageID);
// HANDLE Dynamic Content
if (!!currentPage.dynamic && !!thisSiteMapItem.contentID) {
//console.log(thisSiteMapItem.contentID);
currentPage.thisDynamicItem = await api.getContentItem(thisSiteMapItem.contentID);
}
// get the handlers working
if (!!currentPage.zones) {
Object.keys(currentPage.zones).map((zoneKey, zoneIndex) => {
let zone = currentPage.zones[zoneKey];
if (typeof options.zoneHandlers[zoneKey] == 'function') {
zone = options.zoneHandlers[zoneKey]({zone, api, sitemapNested, thisSiteMapItem, url:req.url, query: req.query});
}
zone.map( agilityModule => {
if (options.moduleHandlers[agilityModule.module]) {
options.moduleHandlers[agilityModule.module]({agilityModule, api, url:req.url, query: req.query});
}
});
});
} else {
}
// Handle unset titles
if (!!!thisSiteMapItem.title || thisSiteMapItem.title == '[empty]') {
thisSiteMapItem.title = false;
}
//console.log('all done on the server');
// setup Pagedata
res.pagedata = {
currentPage,
sitemap,
thisSiteMapItem,
sitemapNested,
isPreview: isPreview,
prefetchedContentItems,
prefetchedContentLists
};
next();
}
}
export default createAgilityMiddleware;
import _ from 'lodash';
import axios from 'axios';
const AgilityOptions = {
agilityMiddlewareOptions : {
error500: '/errors/500',
error404: '/errors/404',
prefetch: {
contentLists: ['timeline'], // referenceNames of content lists where you want all the data
contentItems: [15741], // ids of content items
},
zoneHandlers: {
Masthead: ({zone, api, sitemapNested, thisSiteMapItem}) => {
zone.push({module: "Breadcrumbs", item: {contentID: 'breadcumbItem', fields: thisSiteMapItem}});
return zone;
}
},
moduleHandlers: {
Search: ({agilityModule, api, url, query}) => {
if (!!req.query.s) {
return agilityModule;
}
const startIndex = !!req.query.startIndex ? req.query.startIndex : 1;
const searchResults = await axios.get(`https://www.googleapis.com/customsearch/v1?key=SEARCH_KEY&cx=SEARCH_CX&q=${req.query.s}&start=${startIndex}`)
.then(r => r.data)
.catch( err=> err.data);
agilityModule.searchResults = searchResults;
return agilityModule;
}
},
},
agilitycmsConfig: {
guid: 'XXXX',
previewKey: 'XXXX',
productionKey: 'XXXXX'
}
}
export default AgilityOptions;
import agility from '@agility/content-fetch';
import _ from 'lodash';
const createApi = (guid, apiKey, isPreview=false) => {
const api = agility.getApi({
guid: guid,
apiKey: apiKey,
isPreview: isPreview
});
return {
api : api,
getSitemap : items => {
return Promise.resolve(
api.getSitemapFlat({
channelName: 'website',
languageCode: 'en-us'
})
.then(sitemap => sitemap)
.catch(error => error.data)
);
},
getSitemapNested : () => {
return Promise.resolve(
api.getSitemapNested({
channelName: 'website',
languageCode: 'en-us'
})
.then(sitemap => sitemap)
.catch(error => error.data)
)
},
getPage : (pageID) => {
return Promise.resolve(
api.getPage({
pageID: pageID,
languageCode: 'en-us',
contentLinkDepth: 6
})
.then(page => page)
.catch(error => error.data)
)
},
getContentItem : (contentID) => {
return Promise.resolve(
api.getContentItem({
contentID: parseInt(contentID),
languageCode: 'en-us',
contentLinkDepth: 6
})
.then(contentItem => contentItem)
.catch(error => error.data)
)
},
getContentList : (referenceName, skip=0) => {
return Promise.resolve(
api.getContentList({
referenceName: referenceName,
languageCode: 'en-us',
contentLinkDepth: 5,
take: 50,
skip: skip,
sort: 'properties.created',
direction: api.types.SortDirections.ASC
})
.then( contentList => contentList )
.catch(error => error.data)
);
},
getContentListAll : async (referenceName) => {
let listDefinition = {
referenceName: referenceName,
languageCode: 'en-us',
contentLinkDepth: 5,
take: 50,
skip: 0,
sort: 'properties.created',
direction: api.types.SortDirections.ASC
}
let items = await Promise.resolve(
api.getContentList(listDefinition)
.then( contentList => contentList )
.catch(error => error.data)
);
let xitems;
if (items.totalCount > items.items.length) {
let diff = items.totalCount - items.items.length;
let numPages = Math.ceil(diff/50);
xitems = await Promise.all(Array.from(Array(numPages), (x,i) => {
return api.getContentList({
...listDefinition,
skip: 50*(i+1)
})
.then( contentList => contentList )
.catch(error => error.data)
}))
return {
items: _.flatten([items.items, _.flatten(xitems.map(i => i.items))]),
totalCount: items.totalCount
};
} else {
return {items};
}
},
getTimeLine : (page=0) => {
return Promise.resolve(
api.getContentList({
referenceName: 'news',
languageCode: 'en-us',
contentLinkDepth: 5,
take: 50,
skip: 0,
sort: 'properties.created',
filters: [
{property: 'fields.timelineHidden', operator: api.types.FilterOperators.NOT_EQUAL_TO, value: true},
],
direction: api.types.SortDirections.DESC
})
.then( contentList => contentList )
.catch(error => error.data)
);
}
}
}
export default createApi;
import React from 'react';
import { hydrate } from 'react-dom';
import App from './components/App';
import createApi from './apiHelpers';
import config from './options/agility';
hydrate(<App {...window.___appProps} />, document.getElementById('root'));
if (module.hot) {
module.hot.accept();
}
import App from './components/App';
import React from 'react';
import express from 'express';
import { renderToString } from 'react-dom/server';
const server = express();
server.use(agilityMiddleware)
.get('*', (req, res) => {
res.pagedata = res.pagedata || {};
const pagedata = {...res.pagedata}
const markup = renderToString(<App {...pagedata}/>);
const host = req.get('host');
res.send(
// prettier-ignore
`<!doctype html>
<html lang="en">
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta charSet='utf-8' />
<title>${res.pagedata.isPreview ? 'Preview : ' : ''}${!!res.pagedata.thisSiteMapItem.title ? res.pagedata.thisSiteMapItem.title + ' | ' : ''} First Canadian Place | Exchange Tower</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<base href="/" />
<link href="/favicon.png" rel="shortcut icon" />
<link rel="stylesheet" href="pathtocompliledclient.css">
</head>
<body class="${host}">
<div id="root">${markup}</div>
<script src="pathtoclient.js" defer crossorigin></script>
<script>var ___appProps = ${JSON.stringify(pagedata)}</script>
</body>
</html>`);
});
export default server;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment