Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
DATOCMS_READONLY_API_TOKEN="<Read-only API token>"
DATOCMS_API_TOKEN="<Full-access API token>"
DATOCMS_PREVIEW_API_TOKEN="<Custom API token for preview>"
HEAD_START_PREVIEW_SECRET=""
64 changes: 64 additions & 0 deletions config/datocms/migrations/1734806654_previewLinks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import { Client } from '@datocms/cli/lib/cma-client-node';

export default async function (client: Client) {
console.log('Manage upload filters');

console.log('Install plugin "Model Deployment Links"');
await client.plugins.create({
id: 'MKba9NT5QBKZaeI4HcERwA',
package_name: 'datocms-plugin-model-deployment-links',
});
await client.plugins.update('MKba9NT5QBKZaeI4HcERwA', {
parameters: { datoApiToken: process.env.DATOCMS_PREVIEW_API_TOKEN },
});

console.log('Creating new fields/fieldsets');

console.log(
'Create JSON field "Preview" (`preview`) in model "\uD83D\uDCD1 Page" (`page`)'
);
await client.fields.create('LjXdkuCdQxCFT4hv8_ayew', {
id: 'XF2LuFVWSrmu7Lle8xlhTg',
label: 'Preview',
field_type: 'json',
api_key: 'preview',
localized: true,
appearance: {
addons: [],
editor: 'MKba9NT5QBKZaeI4HcERwA',
parameters: { urlPattern: '/{ locale }/{ slug }/' },
},
});

console.log(
'Create JSON field "Preview" (`preview`) in model "\uD83C\uDFE0 Home" (`home_page`)'
);
await client.fields.create('X_tZn3TxQY28ltSyjZUGHQ', {
id: 'YPXZOMoWRdKTHLUkN9ytfw',
label: 'Preview',
field_type: 'json',
api_key: 'preview',
localized: true,
appearance: {
addons: [],
editor: 'MKba9NT5QBKZaeI4HcERwA',
parameters: { urlPattern: '/{ locale }/' },
},
});

console.log(
'Create JSON field "Preview" (`preview`) in model "\uD83E\uDD37 Not found" (`not_found_page`)'
);
await client.fields.create('d_AvMVoMSqmNbMqx-NdqIw', {
id: 'O_DCfpaDSzq1MX4DaRlMpQ',
label: 'Preview',
field_type: 'json',
api_key: 'preview',
localized: true,
appearance: {
addons: [],
editor: 'MKba9NT5QBKZaeI4HcERwA',
parameters: { urlPattern: '/{ locale }/404' },
},
});
}
2 changes: 1 addition & 1 deletion datocms-environment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
* @see docs/getting-started.md on how to use this file
* @see docs/decision-log/2023-10-24-datocms-env-file.md on why file is preferred over env vars
*/
export const datocmsEnvironment = 'action-block';
export const datocmsEnvironment = 'preview-links';
export const datocmsBuildTriggerId = '30535';
11 changes: 5 additions & 6 deletions src/pages/404.astro
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
---
import type { SiteLocale } from '@lib/i18n/types';
import type {
NotFoundPageQuery,
NotFoundPageRecord,
} from '@lib/datocms/types';
import type { NotFoundPageQuery, NotFoundPageRecord } from '@lib/datocms/types';
import { datocmsRequest } from '@lib/datocms';
import { noIndexTag, titleTag } from '@lib/seo';
import Layout from '@layouts/Default.astro';
Expand All @@ -15,14 +12,16 @@ export const prerender = false;

Astro.response.status = 404;

const { locale } = Astro.params as { locale: SiteLocale };
const localeFromPath = Astro.params.locale as SiteLocale;
const localeFromQuery = Astro.url.searchParams.get('locale') as SiteLocale;
const locale = localeFromQuery || localeFromPath;
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is now needed to have the Astro.rewrite('/404/') work in [locale]/[...path]/index.astro. It's the tradeoff for no longer needing to cast the response from the page query. Worth it? Or rather have that one fail as it did before?

const { page } = (await datocmsRequest<NotFoundPageQuery>({
query,
variables: { locale },
})) as { page: NotFoundPageRecord };
---

<Layout pageUrls={[]} seoMetaTags={[noIndexTag, titleTag(page.title)]}>
<h1>{page.title} {Astro.params.locale}</h1>
<h1>{page.title}</h1>
<Blocks blocks={page.bodyBlocks as AnyBlock[]} />
</Layout>
31 changes: 21 additions & 10 deletions src/pages/[locale]/[...path]/index.astro
Original file line number Diff line number Diff line change
Expand Up @@ -53,19 +53,30 @@ type Params = {

const { locale, path } = Astro.params as Params;
const variables = { locale, slug: getPageSlugFromPath(path) };
const { page } = (await datocmsRequest<PageQuery>({ query, variables })) as {
page: NonNullable<PageQuery['page']>; // Only NonNullable when statically generated. Handle as a 404 when this is a server route!
};
const breadcrumbs = [...getParentPages(page), page].map((page) =>
const { page } = await datocmsRequest<PageQuery>({ query, variables });

if (!page) {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This check ensures this type casting is no longer needed:

as {
  page: NonNullable<PageQuery['page']>; // Only NonNullable when statically generated. Handle as a 404 when this is a server route!
};

But it makes the 404 rewrite and locale setting in that file more complex. Worth it?

return Astro.rewrite(`/404/?locale=${locale}`);
}

const canonicalUrl = getPageHref({ locale, record: page });
if (Astro.url.pathname !== canonicalUrl) {
return Astro.redirect(canonicalUrl);
}

const breadcrumbs = [...getParentPages(page), page].map((record) =>
formatBreadcrumb({
text: page.title,
href: getPageHref({ locale, record: page }),
text: record.title,
href: getPageHref({ locale, record }),
})
);
const pageUrls = (page._allSlugLocales || []).map(({ locale }) => ({
locale: locale as SiteLocale,
pathname: getPageHref({ locale: locale as SiteLocale, record: page }),
})) as PageUrl[];

const pageLocales = (page._allSlugLocales?.map(({ locale }) => locale) ??
[]) as SiteLocale[];
const pageUrls = pageLocales.map((locale) => ({
locale,
pathname: getPageHref({ locale, record: page }),
})) satisfies PageUrl[];
---

<Layout
Expand Down
7 changes: 7 additions & 0 deletions src/pages/api/reroute/_page.query.graphql
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#import '@lib/routing/PageRoute.fragment.graphql'

query ReroutePage($locale: SiteLocale!, $slug: String!) {
page(locale: $locale, filter: { slug: { eq: $slug } }) {
...PageRoute
}
}
38 changes: 38 additions & 0 deletions src/pages/api/reroute/page.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import type { APIRoute } from 'astro';
import { datocmsRequest } from '@lib/datocms';
import type { ReroutePageQuery, SiteLocale } from '@lib/datocms/types';
import { getPageHref } from '@lib/routing';
import query from './_page.query.graphql';

export const prerender = false;

const jsonResponse = (data: object, status: number = 200) => {
return new Response(JSON.stringify(data), {
status,
headers: {
'Content-Type': 'application/json',
},
});
};

export const GET: APIRoute = async ({ request }) => {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a generic alternate approach that I used in the nododos-website. But I think the canonical url check in [locale]/[...path]/index.astro is easier and probably the way to go.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. I guess the canonical check works on localhost and preview as the generic page route is then always handled dynamically on the server. However the production environment has prerendered pages and will throw a 404 I suppose. So I guess we do need this reroute API handler.

In that case the config of the Preview field of the generic page will need to use that one instead.

const locale = new URL(request.url).searchParams.get('locale') as SiteLocale;
if (!locale) {
return jsonResponse({ error: 'Missing \'locale\' parameter' }, 400);
}

const slug = new URL(request.url).searchParams.get('slug');
if (!slug) {
return jsonResponse({ error: 'Missing \'slug\' parameter' }, 400);
}

const { page } = (await datocmsRequest<ReroutePageQuery>({ query, variables: { slug, locale } }));
if (!page) {
return jsonResponse({ error: 'Page not found' }, 404);
}

return new Response('',{
status: 307,
headers: { 'Location': getPageHref({ locale, record: page }) },
});
};
Loading