From 9b6ead73eafa7a584b608b708df62aeb00d4f24a Mon Sep 17 00:00:00 2001 From: Clemence Kyara Date: Wed, 11 Mar 2026 13:41:13 +0300 Subject: [PATCH 01/14] chore(docker): migrate trustlab to bake and make app images env-neutral --- .github/workflows/bake-and-push.yml | 22 +- .github/workflows/push-to-dokku.yml | 4 + .github/workflows/techlabblog.yml | 18 +- .github/workflows/trustlab.yml | 87 + Makefile | 2 +- apps/trustlab/.env | 23 +- apps/trustlab/.env.template | 14 +- apps/trustlab/eslint.webpack.config.js | 1 - apps/trustlab/instrumentation-client.js | 4 +- apps/trustlab/jest.setup.js | 2 +- apps/trustlab/next.config.mjs | 12 +- apps/trustlab/payload.config.ts | 23 +- apps/trustlab/sentry.edge.config.js | 4 +- apps/trustlab/sentry.server.config.js | 4 +- .../src/app/(payload)/admin/importMap.js | 162 +- .../src/components/PayloadAdminBar/index.js | 4 +- .../components/PayloadLivePreview/index.js | 7 +- apps/trustlab/src/lib/data/rest/index.js | 12 +- apps/trustlab/src/next-seo.config.js | 9 +- apps/trustlab/src/pages/[[...slugs]].page.js | 5 +- apps/trustlab/src/pages/_document.page.js | 10 + .../src/pages/api/v1/revalidate.page.js | 33 +- .../trustlab/src/payload/collections/Pages.js | 3 +- apps/trustlab/src/payload/utils/locales.js | 6 +- .../src/payload/utils/revalidateCache.js | 20 +- apps/trustlab/src/utils/site.js | 32 +- apps/trustlab/turbo.json | 6 + docker-bake.hcl | 25 +- docker-compose.yml | 26 +- docker/README.md | 17 +- .../Dockerfile} | 14 +- docker/apps/techlabblog/app.json | 15 + docker/apps/trustlab/Dockerfile | 57 + docker/apps/trustlab/app.json | 15 + docker/base.Dockerfile | 2 +- pnpm-lock.yaml | 14312 ++++++++-------- scripts/bake-up.sh | 69 +- scripts/revalidate.mjs | 101 + 38 files changed, 7946 insertions(+), 7236 deletions(-) create mode 100644 .github/workflows/trustlab.yml rename docker/apps/{techlabblog.Dockerfile => techlabblog/Dockerfile} (81%) create mode 100644 docker/apps/techlabblog/app.json create mode 100644 docker/apps/trustlab/Dockerfile create mode 100644 docker/apps/trustlab/app.json create mode 100644 scripts/revalidate.mjs diff --git a/.github/workflows/bake-and-push.yml b/.github/workflows/bake-and-push.yml index b76aa51050..b53b3a0979 100644 --- a/.github/workflows/bake-and-push.yml +++ b/.github/workflows/bake-and-push.yml @@ -8,8 +8,11 @@ permissions: # Replaces build-docker-image.yml for apps migrated to docker-bake.hcl. # # Callers pass app-specific build args via the `set` input using GitHub Variables -# (vars.*) for NEXT_PUBLIC_* values — these are public by design and belong in -# vars, not secrets. Sentry secrets are declared explicitly below. +# (vars.*) — public config that is safe to expose in workflow logs. Secrets that +# are genuinely needed at build time (e.g. Sentry source map upload credentials, +# DB access if the framework initialises at build) are declared explicitly below. +# Everything else (APP_URL, SMTP_PASS, PAYLOAD_CORS, etc.) belongs in runtime +# config (e.g. Dokku config:set) and should never be passed here. on: workflow_call: @@ -44,17 +47,19 @@ on: description: > Extra bake --set overrides (newline-separated target.field=value pairs). Use this to inject app-specific build args, e.g.: - techlabblog.args.NEXT_PUBLIC_APP_URL=${{ vars.TECHLABBLOG_APP_URL }} + techlabblog.args.SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }} Note: GitHub Variables (vars.*) are available here; secrets are not — pass truly secret build args via the declared secrets inputs below. + # Mark required: false so apps without optional secrets can use this workflow. secrets: + DATABASE_URL: + required: false DOCKER_HUB_USERNAME: required: true DOCKER_HUB_ACCESS_TOKEN: required: true - # Sentry secrets: sourced from env by BuildKit secret mounts in Dockerfiles - # (--mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN). - # Mark required: false so apps without Sentry can use this workflow too. + PAYLOAD_SECRET: + required: false SENTRY_AUTH_TOKEN: required: false SENTRY_ORG: @@ -93,9 +98,8 @@ jobs: BASE_TAG: ${{ inputs.base_tag }} GIT_REVISION: ${{ github.sha }} BUILD_DATE: ${{ steps.meta.outputs.date }} - # Sentry secrets: exposed to BuildKit as secret mounts (not build args). - # See --mount=type=secret,id=sentry_auth_token,env=SENTRY_AUTH_TOKEN - # in app Dockerfiles. + DATABASE_URL: ${{ secrets.DATABASE_URL }} + PAYLOAD_SECRET: ${{ secrets.PAYLOAD_SECRET }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ secrets.SENTRY_ORG }} SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} diff --git a/.github/workflows/push-to-dokku.yml b/.github/workflows/push-to-dokku.yml index 89ce365863..1487a6061d 100644 --- a/.github/workflows/push-to-dokku.yml +++ b/.github/workflows/push-to-dokku.yml @@ -11,10 +11,14 @@ on: required: true type: string description: "The name of the image to push" + secrets: + SSH_PRIVATE_KEY: + required: true jobs: push: runs-on: ubuntu-latest + permissions: {} steps: - name: Push diff --git a/.github/workflows/techlabblog.yml b/.github/workflows/techlabblog.yml index fe69042eea..61a57d7cf9 100644 --- a/.github/workflows/techlabblog.yml +++ b/.github/workflows/techlabblog.yml @@ -6,7 +6,7 @@ on: - main paths: - "apps/techlabblog/**" - - "docker/apps/techlabblog.Dockerfile" + - "docker/apps/techlabblog/**" - "docker/base.Dockerfile" - "docker-bake.hcl" - ".github/workflows/techlabblog.yml" @@ -61,13 +61,8 @@ jobs: # codeforafrica/techlabblog: — version bump only (immutable, for releases) # codeforafrica/techlabblog:latest — version bump only (mutable, for convenience) # - # NEXT_PUBLIC_* vars are baked into the JS bundle at build time and cannot - # be changed by restarting the container. Configure them as GitHub Variables - # (Settings > Variables > Actions) rather than secrets since they are public - # by definition (they ship to the browser). - # # Required GitHub Variables: - # TECHLABBLOG_SENTRY_DSN — public Sentry DSN (safe to use vars, not secrets) + # TECHLABBLOG_SENTRY_DSN # # Required GitHub Secrets (for Sentry source map upload during build): # SENTRY_AUTH_TOKEN, SENTRY_ORG, TECHLABBLOG_SENTRY_PROJECT @@ -85,7 +80,8 @@ jobs: tag: ${{ github.sha }} # base_tag: v3 set: | - techlabblog.args.NEXT_PUBLIC_SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }} + techlabblog.args.SENTRY_DSN=${{ vars.TECHLABBLOG_SENTRY_DSN }} + techlabblog.args.SENTRY_ENVIRONMENT=production ${{ needs.version-check.outputs.changed == 'true' && format('techlabblog.tags[]=codeforafrica/techlabblog:{0}', needs.version-check.outputs.version) || '' }} ${{ needs.version-check.outputs.changed == 'true' && 'techlabblog.tags[]=codeforafrica/techlabblog:latest' || '' }} secrets: @@ -106,7 +102,8 @@ jobs: with: git_remote_url: "ssh://azureuser@ui-1.dev.codeforafrica.org/techlabblog-ui" deploy_docker_image: "codeforafrica/techlabblog:${{ github.sha }}" - secrets: inherit + secrets: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} # Deploys to production when the package.json version is bumped. # Both version-check and build must pass before this job runs. @@ -118,4 +115,5 @@ jobs: with: git_remote_url: "ssh://dokku@ui-2.prod.codeforafrica.org/techlabblog-ui" deploy_docker_image: "codeforafrica/techlabblog:${{ needs.version-check.outputs.version }}" - secrets: inherit + secrets: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} diff --git a/.github/workflows/trustlab.yml b/.github/workflows/trustlab.yml new file mode 100644 index 0000000000..1650f54eb7 --- /dev/null +++ b/.github/workflows/trustlab.yml @@ -0,0 +1,87 @@ +name: TrustLab + +on: + push: + branches: + - main + paths: + - "apps/trustlab/**" + - "docker/apps/trustlab/**" + - "docker/base.Dockerfile" + - "docker-bake.hcl" + - ".github/workflows/trustlab.yml" + - ".github/workflows/bake-and-push.yml" + +permissions: + contents: read + +concurrency: + group: "${{ github.workflow }} @ ${{ github.ref }}" + cancel-in-progress: true + +jobs: + version-check: + runs-on: ubuntu-latest + permissions: + contents: read + outputs: + changed: ${{ steps.check.outputs.changed }} + version: ${{ steps.check.outputs.version }} + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Check if version is bumped + id: check + uses: EndBug/version-check@v2 + with: + diff-search: true + file-name: apps/trustlab/package.json + + # Build and push image on every push. + build: + needs: version-check + permissions: + contents: read + uses: ./.github/workflows/bake-and-push.yml + with: + target: trustlab + tag: ${{ github.sha }} + set: | + ${{ needs.version-check.outputs.changed == 'true' && format('trustlab.tags[]=codeforafrica/trustlab:{0}', needs.version-check.outputs.version) || '' }} + ${{ needs.version-check.outputs.changed == 'true' && 'trustlab.tags[]=codeforafrica/trustlab:latest' || '' }} + secrets: + DOCKER_HUB_USERNAME: ${{ secrets.DOCKER_HUB_USERNAME }} + DOCKER_HUB_ACCESS_TOKEN: ${{ secrets.DOCKER_HUB_ACCESS_TOKEN }} + DATABASE_URL: ${{ secrets.TRUSTLAB_MONGO_URL }} + PAYLOAD_SECRET: ${{ secrets.TRUSTLAB_PAYLOAD_SECRET }} + # We need these sentry vars at build time to send source maps. + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.TRUSTLAB_SENTRY_PROJECT }} + + deploy-dev: + needs: build + permissions: {} + uses: ./.github/workflows/push-to-dokku.yml + with: + git_remote_url: "ssh://azureuser@ui-1.dev.codeforafrica.org/trustlab-ui" + deploy_docker_image: "codeforafrica/trustlab:${{ github.sha }}" + secrets: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} + + deploy-prod: + needs: [version-check, build] + if: needs.version-check.outputs.changed == 'true' + permissions: {} + uses: ./.github/workflows/push-to-dokku.yml + with: + git_remote_url: "ssh://dokku@ui-2.prod.codeforafrica.org/trustlab-ui" + deploy_docker_image: "codeforafrica/trustlab:${{ needs.version-check.outputs.version }}" + secrets: + SSH_PRIVATE_KEY: ${{ secrets.SSH_PRIVATE_KEY }} diff --git a/Makefile b/Makefile index f24d058229..403437d026 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ techlabblog: ./scripts/bake-up.sh techlabblog trustlab: - ./scripts/dc.sh trustlab + ./scripts/bake-up.sh trustlab twoopstracker: ./scripts/dc.sh twoopstracker diff --git a/apps/trustlab/.env b/apps/trustlab/.env index 6a1ac47af0..a155b0b187 100644 --- a/apps/trustlab/.env +++ b/apps/trustlab/.env @@ -1,13 +1,20 @@ +# Args and Envs MIGRATIONS_DIR=./migrations -SENTRY_ENVIRONMENT=local +APP_NAME=TrustLab +APP_URL=http://localhost:3000 +IMAGE_UNOPTIMIZED="true" +SEO_DISABLED="true" -NEXT_PUBLIC_APP_URL=http://localhost:3000 -NEXT_PUBLIC_IMAGE_UNOPTIMIZED="true" -NEXT_PUBLIC_SEO_DISABLED="true" -PROJECT_ROOT=../.. - -NEXT_PUBLIC_DEFAULT_LOCALE=en -NEXT_PUBLIC_LOCALES="en" +DEFAULT_LOCALE=en +LOCALES="en" NEXT_TELEMETRY_DISABLED=1 + +# Secrets +## Optional - App will work fine without these but must be +# declared for docker image to build +SENTRY_AUTH_TOKEN= +SENTRY_ORG= +SENTRY_PROJECT= +SMTP_PASS= diff --git a/apps/trustlab/.env.template b/apps/trustlab/.env.template index d07218944d..f940980f05 100644 --- a/apps/trustlab/.env.template +++ b/apps/trustlab/.env.template @@ -1,8 +1,13 @@ # Next -NEXT_PUBLIC_APP_URL= +APP_NAME= +APP_URL= +SEO_DISABLED= +IMAGE_UNOPTIMIZED= +DEFAULT_LOCALE= +LOCALES= # Payload -MONGO_URL= +DATABASE_URL= PAYLOAD_SECRET= PREVIEW_SECRET= @@ -16,7 +21,8 @@ S3_MAX_SOCKETS=1000 S3_CONNECTION_TIMEOUT=5000 # Sentry -NEXT_PUBLIC_SENTRY_DSN= +SENTRY_DSN= +SENTRY_AUTH_TOKEN= SENTRY_ENVIRONMENT=local|development|staging|production SENTRY_ORG= SENTRY_PROJECT= @@ -27,5 +33,3 @@ SMTP_HOST= SMTP_PASS= SMTP_FROM_ADDRESS= SMTP_FROM_NAME= - - diff --git a/apps/trustlab/eslint.webpack.config.js b/apps/trustlab/eslint.webpack.config.js index 1c1eedbbcb..13a5439c42 100644 --- a/apps/trustlab/eslint.webpack.config.js +++ b/apps/trustlab/eslint.webpack.config.js @@ -19,7 +19,6 @@ module.exports = { resolve: { alias: { "@/trustlab": path.resolve(__dirname, "src/"), - content: path.resolve(__dirname, "content/"), }, extensions: [".js"], }, diff --git a/apps/trustlab/instrumentation-client.js b/apps/trustlab/instrumentation-client.js index 75113be50d..73d8b21387 100644 --- a/apps/trustlab/instrumentation-client.js +++ b/apps/trustlab/instrumentation-client.js @@ -4,10 +4,10 @@ import * as Sentry from "@sentry/nextjs"; -const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; +import { site } from "@/trustlab/utils"; Sentry.init({ - dsn: SENTRY_DSN, + dsn: site.sentryDsn, // Add optional integrations for additional features integrations: [Sentry.replayIntegration()], diff --git a/apps/trustlab/jest.setup.js b/apps/trustlab/jest.setup.js index e1e8623037..897b30d08c 100644 --- a/apps/trustlab/jest.setup.js +++ b/apps/trustlab/jest.setup.js @@ -21,7 +21,7 @@ global.Element.prototype.scrollIntoView = jest.fn(); // NOTE: Since we use Jest for component testing i.e. unit testing, it's not // recommended to load external env vars (since outcome will not be // predictable) -process.env.NEXT_PUBLIC_APP_URL = "http://localhost:3000"; +process.env.APP_URL = "http://localhost:3000"; jest.mock("next/router", () => ({ useRouter: jest.fn().mockImplementation(() => ({ diff --git a/apps/trustlab/next.config.mjs b/apps/trustlab/next.config.mjs index 35fcfffa1a..702b95e25d 100644 --- a/apps/trustlab/next.config.mjs +++ b/apps/trustlab/next.config.mjs @@ -5,20 +5,20 @@ import path from "path"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); -const PROJECT_ROOT = process.env.PROJECT_ROOT?.trim(); -const outputFileTracingRoot = PROJECT_ROOT - ? path.resolve(__dirname, PROJECT_ROOT) + +const TRACING_ROOT = process.env.TRACING_ROOT?.trim(); +const outputFileTracingRoot = TRACING_ROOT + ? path.join(__dirname, TRACING_ROOT) : undefined; /** @type {import('next').NextConfig} */ const nextConfig = { images: { - domains: process.env.NEXT_PUBLIC_IMAGE_DOMAINS?.split(",") + domains: process.env.IMAGE_DOMAINS?.split(",") .map((d) => d.trim()) .filter(Boolean), unoptimized: - process.env.NEXT_PUBLIC_IMAGE_UNOPTIMIZED?.trim()?.toLowerCase() === - "true", + process.env.IMAGE_UNOPTIMIZED?.trim()?.toLowerCase() === "true", }, output: "standalone", outputFileTracingRoot, diff --git a/apps/trustlab/payload.config.ts b/apps/trustlab/payload.config.ts index db933ee7be..64c267183a 100644 --- a/apps/trustlab/payload.config.ts +++ b/apps/trustlab/payload.config.ts @@ -20,17 +20,19 @@ import { } from "@/trustlab/payload/collections"; import plugins from "@/trustlab/payload/plugins"; import SiteSettings from "@/trustlab/payload/globals"; +import { site } from "@/trustlab/utils"; import { defaultLocale, locales } from "@/trustlab/payload/utils/locales"; -const filename = fileURLToPath(import.meta.url); -const dirname = path.dirname(filename); + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); const cors = - process?.env?.PAYLOAD_CORS?.split(",") + process.env.PAYLOAD_CORS?.split(",") .map((d) => d.trim()) .filter(Boolean) ?? []; const csrf = - process?.env?.PAYLOAD_CSRF?.split(",") + process.env.PAYLOAD_CSRF?.split(",") .map((d) => d.trim()) .filter(Boolean) ?? []; @@ -40,7 +42,7 @@ if (process.env.SMTP_HOST && process.env.SMTP_PASS) { nodemailerAdapterArgs = { defaultFromAddress: process.env.SMTP_FROM_ADDRESS || "noreply@trustlab.africa", - defaultFromName: process.env.SENDGRID_FROM_NAME || "TrustLab CMS", + defaultFromName: process.env.SENDGRID_FROM_NAME || site.name, // Any Nodemailer transport can be used transportOptions: { host: process.env.SMTP_HOST, @@ -58,10 +60,10 @@ const email = nodemailerAdapter(nodemailerAdapterArgs); export default buildConfig({ admin: { importMap: { - baseDir: path.resolve(dirname), + baseDir: path.resolve(__dirname), }, livePreview: { - url: process.env.NEXT_PUBLIC_APP_URL, + url: site.url, collections: ["pages"], globals: ["site-settings"], }, @@ -84,7 +86,7 @@ export default buildConfig({ cors, csrf, db: mongooseAdapter({ - url: process.env.MONGO_URL || "", + url: process.env.DATABASE_URL ?? false, }), email, editor: lexicalEditor(), @@ -100,10 +102,11 @@ export default buildConfig({ : undefined), plugins: [...plugins], secret: process.env.PAYLOAD_SECRET || "", - serverURL: process.env.NEXT_PUBLIC_APP_URL, + // Just the origin, without trailing slash. + serverURL: site.url.slice(0, -1), sharp, telemetry: process.env.NEXT_TELEMETRY_DISABLED === "0", typescript: { - outputFile: path.resolve(dirname, "payload-types.ts"), + outputFile: path.resolve(__dirname, "payload-types.ts"), }, }); diff --git a/apps/trustlab/sentry.edge.config.js b/apps/trustlab/sentry.edge.config.js index f2b84a7574..e9c6756160 100644 --- a/apps/trustlab/sentry.edge.config.js +++ b/apps/trustlab/sentry.edge.config.js @@ -5,10 +5,10 @@ import * as Sentry from "@sentry/nextjs"; -const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; +import { site } from "@/trustlab/utils"; Sentry.init({ - dsn: SENTRY_DSN, + dsn: site.sentryDsn, // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. tracesSampleRate: 1, diff --git a/apps/trustlab/sentry.server.config.js b/apps/trustlab/sentry.server.config.js index faeb9cea04..7813a7cd00 100644 --- a/apps/trustlab/sentry.server.config.js +++ b/apps/trustlab/sentry.server.config.js @@ -4,10 +4,10 @@ import * as Sentry from "@sentry/nextjs"; -const SENTRY_DSN = process.env.NEXT_PUBLIC_SENTRY_DSN; +import { site } from "@/trustlab/utils"; Sentry.init({ - dsn: SENTRY_DSN, + dsn: site.sentryDsn, // Define how likely traces are sampled. Adjust this value in production, or use tracesSampler for greater control. tracesSampleRate: 1, diff --git a/apps/trustlab/src/app/(payload)/admin/importMap.js b/apps/trustlab/src/app/(payload)/admin/importMap.js index bd6babeed8..62de021e16 100644 --- a/apps/trustlab/src/app/(payload)/admin/importMap.js +++ b/apps/trustlab/src/app/(payload)/admin/importMap.js @@ -1,99 +1,67 @@ -import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from "@payloadcms/richtext-lexical/rsc"; -import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from "@payloadcms/richtext-lexical/rsc"; -import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from "@payloadcms/richtext-lexical/rsc"; -import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from "@payloadcms/richtext-lexical/client"; -import { ColourTextComponent as ColourTextComponent_5eca6d89486e7fecc5e5204e28af0de5 } from "@nouance/payload-better-fields-plugin/ColourText/client"; -import { OverviewComponent as OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from "@payloadcms/plugin-seo/client"; -import { MetaTitleComponent as MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860 } from "@payloadcms/plugin-seo/client"; -import { MetaDescriptionComponent as MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860 } from "@payloadcms/plugin-seo/client"; -import { MetaImageComponent as MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860 } from "@payloadcms/plugin-seo/client"; -import { PreviewComponent as PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from "@payloadcms/plugin-seo/client"; -import { default as default_f5238f1b70ea6cf94e5a15e7dfec6dac } from "@/trustlab/payload/components/RowLabel"; -import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from "@payloadcms/storage-s3/client"; -import { AdminErrorBoundary as AdminErrorBoundary_e5a9e14bdbe97e70ba60697217fe7688 } from "@payloadcms/plugin-sentry/client"; +import { RscEntryLexicalCell as RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { RscEntryLexicalField as RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { LexicalDiffComponent as LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e } from '@payloadcms/richtext-lexical/rsc' +import { InlineToolbarFeatureClient as InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { HorizontalRuleFeatureClient as HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UploadFeatureClient as UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { BlockquoteFeatureClient as BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { RelationshipFeatureClient as RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { LinkFeatureClient as LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ChecklistFeatureClient as ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { OrderedListFeatureClient as OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UnorderedListFeatureClient as UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { IndentFeatureClient as IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { AlignFeatureClient as AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { HeadingFeatureClient as HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ParagraphFeatureClient as ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { InlineCodeFeatureClient as InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { SuperscriptFeatureClient as SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { SubscriptFeatureClient as SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { StrikethroughFeatureClient as StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { UnderlineFeatureClient as UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { BoldFeatureClient as BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ItalicFeatureClient as ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864 } from '@payloadcms/richtext-lexical/client' +import { ColourTextComponent as ColourTextComponent_5eca6d89486e7fecc5e5204e28af0de5 } from '@nouance/payload-better-fields-plugin/ColourText/client' +import { OverviewComponent as OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' +import { MetaTitleComponent as MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' +import { MetaDescriptionComponent as MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' +import { MetaImageComponent as MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' +import { PreviewComponent as PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860 } from '@payloadcms/plugin-seo/client' +import { default as default_f5238f1b70ea6cf94e5a15e7dfec6dac } from '@/trustlab/payload/components/RowLabel' +import { S3ClientUploadHandler as S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24 } from '@payloadcms/storage-s3/client' +import { AdminErrorBoundary as AdminErrorBoundary_e5a9e14bdbe97e70ba60697217fe7688 } from '@payloadcms/plugin-sentry/client' export const importMap = { - "@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": - RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e, - "@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": - RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e, - "@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": - LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e, - "@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": - InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": - HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#UploadFeatureClient": - UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": - BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#RelationshipFeatureClient": - RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#LinkFeatureClient": - LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#ChecklistFeatureClient": - ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#OrderedListFeatureClient": - OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": - UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#IndentFeatureClient": - IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#AlignFeatureClient": - AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#HeadingFeatureClient": - HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#ParagraphFeatureClient": - ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": - InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": - SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#SubscriptFeatureClient": - SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": - StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#UnderlineFeatureClient": - UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#BoldFeatureClient": - BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@payloadcms/richtext-lexical/client#ItalicFeatureClient": - ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, - "@nouance/payload-better-fields-plugin/ColourText/client#ColourTextComponent": - ColourTextComponent_5eca6d89486e7fecc5e5204e28af0de5, - "@payloadcms/plugin-seo/client#OverviewComponent": - OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860, - "@payloadcms/plugin-seo/client#MetaTitleComponent": - MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860, - "@payloadcms/plugin-seo/client#MetaDescriptionComponent": - MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860, - "@payloadcms/plugin-seo/client#MetaImageComponent": - MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860, - "@payloadcms/plugin-seo/client#PreviewComponent": - PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860, - "@/trustlab/payload/components/RowLabel#default": - default_f5238f1b70ea6cf94e5a15e7dfec6dac, - "@payloadcms/storage-s3/client#S3ClientUploadHandler": - S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24, - "@payloadcms/plugin-sentry/client#AdminErrorBoundary": - AdminErrorBoundary_e5a9e14bdbe97e70ba60697217fe7688, -}; + "@payloadcms/richtext-lexical/rsc#RscEntryLexicalCell": RscEntryLexicalCell_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/rsc#RscEntryLexicalField": RscEntryLexicalField_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/rsc#LexicalDiffComponent": LexicalDiffComponent_44fe37237e0ebf4470c9990d8cb7b07e, + "@payloadcms/richtext-lexical/client#InlineToolbarFeatureClient": InlineToolbarFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#HorizontalRuleFeatureClient": HorizontalRuleFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UploadFeatureClient": UploadFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#BlockquoteFeatureClient": BlockquoteFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#RelationshipFeatureClient": RelationshipFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#LinkFeatureClient": LinkFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ChecklistFeatureClient": ChecklistFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#OrderedListFeatureClient": OrderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UnorderedListFeatureClient": UnorderedListFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#IndentFeatureClient": IndentFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#AlignFeatureClient": AlignFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#HeadingFeatureClient": HeadingFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ParagraphFeatureClient": ParagraphFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#InlineCodeFeatureClient": InlineCodeFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#SuperscriptFeatureClient": SuperscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#SubscriptFeatureClient": SubscriptFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#StrikethroughFeatureClient": StrikethroughFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#UnderlineFeatureClient": UnderlineFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#BoldFeatureClient": BoldFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@payloadcms/richtext-lexical/client#ItalicFeatureClient": ItalicFeatureClient_e70f5e05f09f93e00b997edb1ef0c864, + "@nouance/payload-better-fields-plugin/ColourText/client#ColourTextComponent": ColourTextComponent_5eca6d89486e7fecc5e5204e28af0de5, + "@payloadcms/plugin-seo/client#OverviewComponent": OverviewComponent_a8a977ebc872c5d5ea7ee689724c0860, + "@payloadcms/plugin-seo/client#MetaTitleComponent": MetaTitleComponent_a8a977ebc872c5d5ea7ee689724c0860, + "@payloadcms/plugin-seo/client#MetaDescriptionComponent": MetaDescriptionComponent_a8a977ebc872c5d5ea7ee689724c0860, + "@payloadcms/plugin-seo/client#MetaImageComponent": MetaImageComponent_a8a977ebc872c5d5ea7ee689724c0860, + "@payloadcms/plugin-seo/client#PreviewComponent": PreviewComponent_a8a977ebc872c5d5ea7ee689724c0860, + "@/trustlab/payload/components/RowLabel#default": default_f5238f1b70ea6cf94e5a15e7dfec6dac, + "@payloadcms/storage-s3/client#S3ClientUploadHandler": S3ClientUploadHandler_f97aa6c64367fa259c5bc0567239ef24, + "@payloadcms/plugin-sentry/client#AdminErrorBoundary": AdminErrorBoundary_e5a9e14bdbe97e70ba60697217fe7688 +} diff --git a/apps/trustlab/src/components/PayloadAdminBar/index.js b/apps/trustlab/src/components/PayloadAdminBar/index.js index e826f1d95e..f5bb1d9df4 100644 --- a/apps/trustlab/src/components/PayloadAdminBar/index.js +++ b/apps/trustlab/src/components/PayloadAdminBar/index.js @@ -6,6 +6,8 @@ import { PayloadAdminBar } from "@payloadcms/admin-bar"; import { useSelectedLayoutSegments, useRouter } from "next/navigation"; import React, { useState } from "react"; +import { site } from "@/trustlab/utils"; + const collectionLabels = { pages: { plural: "Pages", @@ -56,7 +58,7 @@ function AdminBar(props) { router.refresh()} - serverURL={process.env.NEXT_PUBLIC_APP_URL} - /> + router.refresh()} serverURL={site.url} /> ); } export default RefreshRouteOnSave; diff --git a/apps/trustlab/src/lib/data/rest/index.js b/apps/trustlab/src/lib/data/rest/index.js index b950b85c8f..48de54f80c 100644 --- a/apps/trustlab/src/lib/data/rest/index.js +++ b/apps/trustlab/src/lib/data/rest/index.js @@ -1,6 +1,6 @@ import { getErrorPageProps as getProps } from "@/trustlab/lib/data/common"; +import { site } from "@/trustlab/utils"; -const BASE_URL = process.env.NEXT_PUBLIC_APP_URL; export const fetchJson = { get: async (url, { params } = {}) => { // Build query string from params object @@ -24,21 +24,16 @@ export const fetchJson = { `Fetch failed: ${response.status} ${response.statusText}`, ); } - return response.json(); }, }; const findGlobal = async (slug, params) => { - return fetchJson.get(`${BASE_URL}/api/globals/${slug}`, { - params, - }); + return fetchJson.get(`${site.url}api/globals/${slug}`, { params }); }; const findCollection = async (slug, params) => { - return fetchJson.get(`${BASE_URL}/api/${slug}`, { - params, - }); + return fetchJson.get(`${site.url}api/${slug}`, { params }); }; const findPage = async (slug, options) => { @@ -53,6 +48,7 @@ const findPage = async (slug, options) => { }; return findCollection("pages", pageOptions); }; + export const api = { findCollection, findGlobal, diff --git a/apps/trustlab/src/next-seo.config.js b/apps/trustlab/src/next-seo.config.js index 1e302ee7ea..e81785c3ef 100644 --- a/apps/trustlab/src/next-seo.config.js +++ b/apps/trustlab/src/next-seo.config.js @@ -1,12 +1,9 @@ -import site from "@/trustlab/utils/site"; - -const isSeoDisabled = - process.env.NEXT_PUBLIC_SEO_DISABLED?.trim()?.toLowerCase() === "true"; +import { site } from "@/trustlab/utils"; const config = { // Disable indexing while in development - dangerouslySetAllPagesToNoFollow: isSeoDisabled, - dangerouslySetAllPagesToNoIndex: isSeoDisabled, + dangerouslySetAllPagesToNoFollow: site.seoDisabled, + dangerouslySetAllPagesToNoIndex: site.seoDisabled, openGraph: { type: "website", locale: "en_GB", diff --git a/apps/trustlab/src/pages/[[...slugs]].page.js b/apps/trustlab/src/pages/[[...slugs]].page.js index 65e8ee8a27..44df8c9119 100644 --- a/apps/trustlab/src/pages/[[...slugs]].page.js +++ b/apps/trustlab/src/pages/[[...slugs]].page.js @@ -26,7 +26,7 @@ import Resources from "@/trustlab/components/Resources"; import Spotlight from "@/trustlab/components/Spotlight"; import ToolkitList from "@/trustlab/components/ToolkitList"; import WhereWeWork from "@/trustlab/components/WhereWeWork"; -import { getPageStaticPaths, getPageStaticProps } from "@/trustlab/lib/data"; +import { getPageStaticProps } from "@/trustlab/lib/data"; const componentsBySlugs = { "action-banner": ActionBanner, @@ -84,7 +84,8 @@ function Page({ blocks, fallback }) { } export async function getStaticPaths() { - return getPageStaticPaths(); + // Different environments will have different pages + return { paths: [], fallback: "blocking" }; } export async function getStaticProps(context) { diff --git a/apps/trustlab/src/pages/_document.page.js b/apps/trustlab/src/pages/_document.page.js index 3ff8170608..52e5154163 100644 --- a/apps/trustlab/src/pages/_document.page.js +++ b/apps/trustlab/src/pages/_document.page.js @@ -35,6 +35,11 @@ class MyDocument extends Document { } render() { + const runtimeConfig = JSON.stringify({ + SENTRY_DSN: process.env.SENTRY_DSN, + SEO_DISABLED: process.env.SEO_DISABLED, + }); + return ( @@ -61,6 +66,11 @@ class MyDocument extends Document { {this.props.emotionStyleTags} +