Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
16 changes: 12 additions & 4 deletions .github/workflows/express-checkout-cd.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
name: EXPRESS-CHECKOUT GCP CD
name: EXPRESS-CHECKOUT CD

on:
push:
branches:
- main
- feature-express-checkout
- feature*
- hotfix*
paths:
- "web/express-checkout/**"
- ".github/workflows/express-checkout-cd.yml"
Expand All @@ -19,10 +20,17 @@ on:
- test
- sandbox
- prod

redeploy:
description: "Redeploy Application"
required: true
type: choice
options:
- "false"
- "true"

jobs:
express-checkout-cd:
uses: bcgov/bcregistry-sre/.github/workflows/frontend-cd.yaml@main
uses: bcgov/bcregistry-sre/.github/workflows/backend-cd.yaml@main
with:
target: ${{ inputs.target }}
app_name: "express-checkout"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/express-checkout-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches:
- main
- feature*
- hotfix*
paths:
- "web/express-checkout/**"
- ".github/workflows/express-checkout-ci.yml"
Expand Down
13 changes: 11 additions & 2 deletions .github/workflows/pay-ui-cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ on:
push:
branches:
- main
- feature*
- hotfix*
paths:
- "web/pay-ui/**"
- ".github/workflows/pay-ui-cd.yml"
Expand All @@ -18,10 +20,17 @@ on:
- test
- sandbox
- prod

redeploy:
description: "Redeploy Application"
required: true
type: choice
options:
- "false"
- "true"

jobs:
pay-ui-cd:
uses: bcgov/bcregistry-sre/.github/workflows/frontend-cd.yaml@main
uses: bcgov/bcregistry-sre/.github/workflows/backend-cd.yaml@main
with:
target: ${{ inputs.target }}
app_name: "pay-ui"
Expand Down
1 change: 1 addition & 0 deletions .github/workflows/pay-ui-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches:
- main
- feature*
- hotfix*
paths:
- "web/pay-ui/**"
- ".github/workflows/pay-ui-ci.yml"
Expand Down
11 changes: 11 additions & 0 deletions web/express-checkout/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
node_modules
.nuxt
.output
.data
.git
dist
coverage
tests
Dockerfile
.dockerignore
.env*
30 changes: 30 additions & 0 deletions web/express-checkout/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
# syntax=docker/dockerfile:1

# ---- Dependencies ----
FROM node:24-slim AS deps
ENV PNPM_HOME=/pnpm PATH=/pnpm:$PATH
RUN corepack enable
WORKDIR /app
COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
# postinstall runs `nuxt prepare`, which needs the sources, so skip scripts here
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile --ignore-scripts

# ---- Build ----
FROM deps AS build
COPY . .
RUN pnpm exec nuxt prepare
# `nuxt build` produces the self-contained Nitro server in .output
RUN pnpm exec nuxt build

# ---- Runtime ----
FROM gcr.io/distroless/nodejs24-debian13:nonroot AS runtime
WORKDIR /app
ENV NODE_ENV=production \
HOST=0.0.0.0 \
PORT=8080
COPY --from=build --chown=nonroot:nonroot /app/.output ./

EXPOSE 8080
# The distroless image entrypoint is already `node`
CMD ["--enable-source-maps", "server/index.mjs"]
59 changes: 0 additions & 59 deletions web/express-checkout/devops/cloudbuild-pr.yaml

This file was deleted.

78 changes: 78 additions & 0 deletions web/express-checkout/devops/gcp/clouddeploy.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# Copyright 2022 Google LLC
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

apiVersion: deploy.cloud.google.com/v1
kind: DeliveryPipeline
metadata:
name: express-checkout-pipeline
description: Deployment pipeline
serialPipeline:
stages:
- targetId: gtksf3-dev
profiles: [dev]
strategy:
standard:
verify: false
deployParameters:
- values:
deploy-env: "development"
deploy-project-id: "gtksf3-dev"
service-name: "express-checkout-dev"
container-name: "express-checkout-dev"
cloudsql-instances: ""
service-account: "sa-api@gtksf3-dev.iam.gserviceaccount.com"
- targetId: gtksf3-test
profiles: [test]
strategy:
standard:
verify: false
deployParameters:
- values:
deploy-env: "test"
deploy-project-id: "gtksf3-test"
service-name: "express-checkout-test"
container-name: "express-checkout-test"
app-env: "test"
cloudsql-instances: ""
service-account: "sa-api@gtksf3-test.iam.gserviceaccount.com"
- targetId: gtksf3-sandbox
profiles: [sandbox]
strategy:
standard:
verify: false
deployParameters:
- values:
deploy-env: "sandbox"
deploy-project-id: "gtksf3-tools"
service-name: "express-checkout-sandbox"
container-name: "express-checkout-sandbox"
app-env: "sandbox"
cloudsql-instances: ""
service-account: "sa-api@gtksf3-tools.iam.gserviceaccount.com"
- targetId: gtksf3-prod
profiles: [prod]
strategy:
standard:
verify: false
deployParameters:
- values:
deploy-env: "production"
deploy-project-id: "gtksf3-prod"
service-name: "express-checkout-prod"
container-name: "express-checkout-prod"
app-env: "production"
cloudsql-instances: ""
service-account: "sa-api@gtksf3-prod.iam.gserviceaccount.com"
max-scale: "10"
container-concurrency: "20"
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ NUXT_PUBLIC_BASE_URL="op://web-url/$APP_ENV/express-checkout/EXPRESS_CHECKOUT_WE
NUXT_PUBLIC_AUTH_WEB_URL="op://web-url/$APP_ENV/auth-web/AUTH_WEB_URL"

# APIGEE key
NUXT_PUBLIC_X_API_KEY="op://API/$APP_ENV/business-api/BUSINESS_API_KEY" # TODO: confirm same key or dedicated express-checkout entry
NUXT_X_API_KEY="op://API/$APP_ENV/business-api/BUSINESS_API_KEY" # TODO: confirm same key or dedicated express-checkout entry

# App name (header sent with every API call, distinguishes traffic in logs)
NUXT_PUBLIC_APP_NAME="express-checkout"
Expand All @@ -17,7 +17,7 @@ NUXT_PUBLIC_AUTH_API_URL="op://API/$APP_ENV/auth-api/AUTH_API_URL"
NUXT_PUBLIC_AUTH_API_VERSION="op://API/$APP_ENV/auth-api/AUTH_API_VERSION"

# Pay API
NUXT_PUBLIC_PAY_API_URL="op://API/$APP_ENV/pay-api/PAY_API_URL"
NUXT_PAY_API_URL="op://API/$APP_ENV/pay-api/PAY_API_URL"
NUXT_PUBLIC_PAY_API_VERSION="op://API/$APP_ENV/pay-api/PAY_API_VERSION"

# LaunchDarkly
Expand All @@ -29,3 +29,7 @@ NUXT_PUBLIC_IDP_URL="op://keycloak/$APP_ENV/base/KEYCLOAK_AUTH_BASE_URL"
NUXT_PUBLIC_IDP_REALM="op://keycloak/$APP_ENV/base/KEYCLOAK_REALMNAME"
NUXT_PUBLIC_IDP_CLIENTID="op://keycloak/$APP_ENV/express-checkout/UI_KEYCLOAK_RESOURCE_NAME" # TODO: DevOps to provision Keycloak client
NUXT_PUBLIC_SITEMINDER_LOGOUT_URL="op://web-url/$APP_ENV/siteminder/SITEMINDER_LOGOUT_URL"


NUXT_CSP_EXTRA="op://CD/$APP_ENV/base/CSP_EXTRA"
LOG_LEVEL="op://CD/$APP_ENV/express-checkout/LOG_LEVEL"
80 changes: 57 additions & 23 deletions web/express-checkout/nuxt.config.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,68 @@
// https://nuxt.com/docs/api/configuration/nuxt-config
// Lowercase keys so these merge with Nuxt's built-in route rules instead of duplicating headers.
// Content-Security-Policy is set at runtime from NUXT_CSP_EXTRA by server/middleware/csp.ts.
const securityHeaders = {
'access-control-allow-origin': '*',
'x-frame-options': 'DENY',
'x-content-type-options': 'nosniff',
'x-xss-protection': '1; mode=block',
'cache-control': 'private, no-cache, no-store, must-revalidate',
'pragma': 'no-cache',
'referrer-policy': 'no-referrer',
'feature-policy': "microphone 'self'",
'strict-transport-security': 'max-age=31536000;'
}

// Nitro route rules match paths, not extensions, so the long-lived cache is applied per static asset folder
const longCache = { headers: { 'cache-control': 'public,max-age=31536000' } }

// https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({
ssr: false,
app: {
head: {
title: 'Registry Digital Services Express Checkout'
}
},

ssr: true,

// Auth is browser-only (keycloak-js, token in memory, no cookie), so the server cannot render
// authenticated pages; they are served as an SPA shell. Server routes (/pay-proxy) are unaffected.
// Opt a route into SSR here only if it renders without the user's token.
// Only the landing pages qualify today ('/en-CA' and '/fr-CA' are static and safe to set to
// `ssr: true`); the /pay/** pages need the browser session, so they must stay SPA.
// Headers mirror the previous Firebase Hosting config (firebase.json).
routeRules: {
'/': { redirect: '/en-CA' },
'/**': { ssr: false, headers: securityHeaders },
'/_nuxt/**': longCache,
'/css/**': longCache,
'/js/**': longCache,
'/fonts/**': longCache,
'/img/**': longCache,
'/BCGovLogo/**': longCache,
'/favicon.ico': longCache,
// Build manifest (json) must not be cached, so clients pick up new deployments
'/_nuxt/builds/**': {
headers: { 'cache-control': 'no-cache, no-store, must-revalidate' }
}
},

devtools: { enabled: false },

modules: [
'@nuxt/test-utils/module'
],
modules: ['@nuxt/test-utils/module'],

extends: [
'@sbc-connect/nuxt-pay'
],
extends: ['@sbc-connect/nuxt-pay'],

css: [
'~/assets/css/main.css'
],
css: ['~/assets/css/main.css'],

router: {
options: {
scrollBehaviorType: 'smooth'
}
},

routeRules: {
'/': { redirect: '/en-CA' }
},

imports: {
dirs: [
'stores',
'composables',
'enums',
'interfaces',
'types',
'utils'
]
dirs: ['stores', 'composables', 'enums', 'interfaces', 'types', 'utils']
},

i18n: {
Expand All @@ -61,7 +88,14 @@ export default defineNuxtConfig({
compatibilityDate: '2025-12-09',

runtimeConfig: {
// Server-only (NUXT_PAY_API_URL, NUXT_X_API_KEY); never serialized to the browser
payApiUrl: '',
xApiKey: '',
// Full Content-Security-Policy header value (NUXT_CSP_EXTRA)
cspExtra: '',
public: {
// Browser calls pay-api through the server proxy so the API key stays on the server
payApiUrl: '/pay-proxy',
version: `Pay UI v${process.env.npm_package_version || ''}`,
playwright: process.env.playwright === 'true'
}
Expand Down
Loading
Loading