Skip to content

Commit

Permalink
DS-918: Storyblok Redirects. (#357)
Browse files Browse the repository at this point in the history
  • Loading branch information
sherakama authored Oct 15, 2024
1 parent a571ba6 commit 2c2ce2b
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 2 deletions.
11 changes: 10 additions & 1 deletion app/(storyblok)/[[...slug]]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,16 @@ export async function generateStaticParams() {

// Use the `cdn/links` endpoint to get a list of all stories without all the extra data.
const response = await storyblokApi.getAll('cdn/links', sbParams);
const stories = response.filter((link) => link.is_folder === false);

// Filters
let stories = response;
// Filter out folders.
stories = response.filter((link) => link.is_folder === false);
// Filter out test content by filtering out the `test` folder.
stories = stories.filter((link) => !link.slug.startsWith(getSlugPrefix() + '/test'));
// Filter out globals by filtering out the `global-components` folder.
stories = stories.filter((link) => !link.slug.startsWith(getSlugPrefix() + '/global-components'));

let paths: PathsType[] = [];

stories.forEach((story) => {
Expand Down
11 changes: 10 additions & 1 deletion next.config.js → next.config.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { getStoryblokRedirects } from './utilities/data/getStoryblokRedirects.mjs';

/** @type {import('next').NextConfig} */
const nextConfig = {
eslint: {
dirs: ['app', 'components', 'contexts', 'hooks', 'pages', 'services', 'utilities'],
},
images: {
remotePatterns: [
{
Expand All @@ -13,6 +18,10 @@ const nextConfig = {
CONTEXT: process.env.CONTEXT,
STORYBLOK_SLUG_PREFIX: process.env.STORYBLOK_SLUG_PREFIX,
},
async redirects() {
const storyblokRedirects = await getStoryblokRedirects();
return storyblokRedirects;
},
};

module.exports = nextConfig;
export default nextConfig;
111 changes: 111 additions & 0 deletions utilities/data/getStoryblokRedirects.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import { apiPlugin, getStoryblokApi, storyblokInit } from '@storyblok/react/rsc';

storyblokInit({
accessToken: process.env.STORYBLOK_ACCESS_TOKEN,
use: [apiPlugin],
apiOptions: {
region: 'us',
},
});

/**
* Get the slug prefix for Storyblok.
* @returns {string}
*/
const getSlugPrefix = () => {
const prefix = process.env.STORYBLOK_SLUG_PREFIX || 'momentum';
// Ensure there is no trailing slash.
if (prefix.slice(-1) === '/') {
return prefix.slice(-1);
}
return prefix;
};

/**
* Check if the current environment is production.
* @returns {boolean}
*/
const isProduction = () => {
return process.env.CONTEXT === 'production';
};

/**
* Sanitize the redirect code to ensure it is a valid HTTP status code.
* @param {string} code
* @returns {number}
*/
const sanitizeRedirectCode = (code) => {
// Ensure code is a number and in one of 301, 302, 303, 307, 308
const statusCode = parseInt(code, 10);
if ([301, 302, 303, 307, 308].includes(statusCode)) {
return statusCode;
}
return 301;
};

/**
* Double-slash escape characters in a string.
* @param {*} str
* @returns {string}
*/
const sanitizeSourcePath = (str) => {
const parts = str.split('?');
const path = parts[0];
return path;
};

/**
* Extract query parameters from a URL.
* @param {string} url
* @returns {Array}
*/
const extractQueryParameters = (url) => {
// Parse the URL and get the query string
const urlObj = new URL(url, 'http://example.com'); // base URL needed for relative paths
const queryParams = new URLSearchParams(urlObj.search);

// Create an array to hold the query parameter objects
const queryArray = [];

// Iterate through each query parameter and add it to the array in the required format
queryParams.forEach((value, key) => {
queryArray.push({
type: 'query',
key: key,
value: value,
});
});

return queryArray;
};

/**
* Get redirects from Storyblok.
* @returns {Promise<Array>}
*/
export const getStoryblokRedirects = async () => {
const storyblokApi = getStoryblokApi();
const isProd = isProduction();
const sbParams = {
version: isProd ? 'published' : 'draft',
resolve_links: '0',
resolve_assets: 0,
per_page: 100,
starts_with: getSlugPrefix() + '/global-components/redirects',
};

const stories = await storyblokApi.getAll(`cdn/stories`, sbParams);

if (!stories || stories.length === 0) {
return [];
}

const redirects = stories.map(entry => ({
source: sanitizeSourcePath(entry.content.from),
has: extractQueryParameters(entry.content.from),
destination: entry.content.to,
statusCode: sanitizeRedirectCode(entry.content.statusCode),
}));

return redirects;
};

0 comments on commit 2c2ce2b

Please sign in to comment.