Skip to content
Draft
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
15 changes: 8 additions & 7 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
.git
.github
.next
node_modules
coverage
dist
node_modules
playwright-report
test-results
.env
.env*.local
npm-debug.log*
yarn-debug.log*
pnpm-debug.log*
.DS_Store
.env.*
!.env.example
*.log
*.tsbuildinfo
11 changes: 9 additions & 2 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,14 +1,21 @@
# Required Fleetbase Storefront settings
FLEETBASE_HOST=https://api.fleetbase.io
STOREFRONT_KEY=store_xxx_or_network_xxx
STOREFRONT_KEY=store_xxx

# Public website settings
NEXT_PUBLIC_SITE_URL=http://localhost:3000
NEXT_PUBLIC_DEFAULT_LOCALE=en
NEXT_PUBLIC_DEFAULT_THEME=default
NEXT_PUBLIC_IMAGE_HOSTS=api.fleetbase.io
NEXT_PUBLIC_IMAGE_HOSTS=api.fleetbase.io,flb-assets.s3.ap-southeast-1.amazonaws.com
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_xxx
NEXT_PUBLIC_PROMO_BANNER=
NEXT_PUBLIC_CHECKOUT_MESSAGE=
STOREFRONT_ANALYTICS_WEBHOOK_URL=

# Required for rate limiting when production runs more than one replica.
# The service must accept the JSON contract documented in docs/operations.md.
RATE_LIMIT_REST_URL=
RATE_LIMIT_REST_TOKEN=

# Optional real-time tracking
NEXT_PUBLIC_SOCKETCLUSTER_HOST=
Expand Down
118 changes: 107 additions & 11 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,120 @@ name: CI

on:
pull_request:
branches: [main, 'dev-v*']
push:
branches:
- main
branches: [main, 'dev-v*']
workflow_dispatch:

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read
id-token: write

env:
PNPM_VERSION: 10.15.1
NODE_VERSION: 22

jobs:
verify:
install:
name: install-reproducibility
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
with: { version: '${{ env.PNPM_VERSION }}' }
- uses: actions/setup-node@v4
with: { node-version: '${{ env.NODE_VERSION }}', cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: git diff --exit-code -- package.json pnpm-lock.yaml pnpm-workspace.yaml

quality:
name: ${{ matrix.check }}
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
check: [lint, typecheck, unit-tests, production-build]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '${{ env.PNPM_VERSION }}' }
- uses: actions/setup-node@v4
with: { node-version: '${{ env.NODE_VERSION }}', cache: pnpm }
- run: pnpm install --frozen-lockfile
- if: matrix.check == 'lint'
run: pnpm lint
- if: matrix.check == 'typecheck'
run: pnpm typecheck
- if: matrix.check == 'unit-tests'
run: pnpm test
- if: matrix.check == 'production-build'
env:
STOREFRONT_KEY: store_ci_fixture
FLEETBASE_HOST: http://127.0.0.1:4010
NEXT_PUBLIC_SITE_URL: http://127.0.0.1:3000
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY: pk_test_fixture
run: pnpm build

coverage:
name: coverage-100
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '${{ env.PNPM_VERSION }}' }
- uses: actions/setup-node@v4
with: { node-version: '${{ env.NODE_VERSION }}', cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm coverage:check
- uses: actions/upload-artifact@v4
if: always()
with: { name: coverage-report, path: coverage, if-no-files-found: error }
- uses: codecov/codecov-action@v5
with:
node-version: 22
cache: pnpm
use_oidc: true
files: coverage/lcov.info
flags: web
disable_search: true
fail_ci_if_error: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}

e2e:
name: playwright-e2e
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with: { version: '${{ env.PNPM_VERSION }}' }
- uses: actions/setup-node@v4
with: { node-version: '${{ env.NODE_VERSION }}', cache: pnpm }
- run: pnpm install --frozen-lockfile
- run: pnpm lint
- run: pnpm typecheck
- run: pnpm test
- run: pnpm build
- run: pnpm exec playwright install --with-deps chromium
- run: pnpm test:e2e
- uses: actions/upload-artifact@v4
if: always()
with: { name: playwright-report, path: playwright-report, if-no-files-found: ignore }

docker:
name: docker-smoke
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- run: docker build --build-arg NEXT_PUBLIC_SITE_URL=https://storefront.example --build-arg NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=pk_test_fixture --tag storefront-web:ci .
- run: test "$(docker image inspect storefront-web:ci --format '{{.Config.User}}')" = storefront
- run: docker run --detach --name storefront-web-ci --publish 3000:3000 --env STOREFRONT_KEY=store_ci_fixture --env FLEETBASE_HOST=https://api.fleetbase.io --env NEXT_PUBLIC_SITE_URL=https://storefront.example storefront-web:ci
- name: Wait for container health
run: |
for _ in {1..30}; do
curl --fail --silent --show-error http://127.0.0.1:3000/api/health && exit 0
sleep 1
done
docker logs storefront-web-ci
exit 1
22 changes: 22 additions & 0 deletions .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
name: CodeQL
on:
pull_request:
branches: [main, 'dev-v*']
push:
branches: [main, 'dev-v*']
schedule:
- cron: '23 4 * * 1'
workflow_dispatch:
permissions:
contents: read
security-events: write
jobs:
analyze:
name: codeql-javascript-typescript
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- uses: actions/checkout@v4
- uses: github/codeql-action/init@v3
with: { languages: javascript-typescript }
- uses: github/codeql-action/analyze@v3
24 changes: 24 additions & 0 deletions .github/workflows/create-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
name: Create Release
on:
push:
tags: ['v*']
permissions:
contents: write
jobs:
create:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- name: Determine prerelease status
id: version
env: { TAG: '${{ github.ref_name }}' }
run: if [[ "$TAG" == *-* ]]; then echo 'prerelease=true' >> "$GITHUB_OUTPUT"; else echo 'prerelease=false' >> "$GITHUB_OUTPUT"; fi
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ github.ref_name }}
name: ${{ github.ref_name }}
body_path: RELEASE.md
generate_release_notes: true
draft: false
prerelease: ${{ steps.version.outputs.prerelease }}
16 changes: 16 additions & 0 deletions .github/workflows/dependency-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
name: Dependency Review
on:
pull_request:
permissions:
contents: read
jobs:
dependency-review:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v4
- uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
deny-licenses: GPL-2.0-only, GPL-3.0-only, AGPL-3.0-only, SSPL-1.0
allow-dependencies-licenses: pkg:githubactions/fleetbase/fleetbase/.github/workflows/release-tag.yml@main
34 changes: 34 additions & 0 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: Container Security
on:
pull_request:
branches: [main, 'dev-v*']
push:
branches: [main, 'dev-v*']
workflow_dispatch:
permissions:
contents: read
security-events: write
jobs:
scan:
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- uses: docker/build-push-action@v6
with:
context: .
load: true
push: false
tags: storefront-web:scan
- uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: storefront-web:scan
format: sarif
output: trivy-results.sarif
severity: HIGH,CRITICAL
ignore-unfixed: true
exit-code: '1'
- uses: github/codeql-action/upload-sarif@v3
if: always()
with: { sarif_file: trivy-results.sarif }
20 changes: 20 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Release Tag
on:
pull_request:
types: [closed]
branches: [main]
workflow_dispatch:
inputs:
version:
description: 'Version to tag, e.g. 0.1.0 (recovery only)'
required: true
permissions:
contents: read
jobs:
tag:
if: github.event_name == 'workflow_dispatch' || (github.event.pull_request.merged == true && startsWith(github.event.pull_request.head.ref, 'dev-v'))
uses: fleetbase/fleetbase/.github/workflows/release-tag.yml@main
with:
version-files: package.json
version: ${{ github.event.inputs.version || '' }}
secrets: inherit
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
node_modules
dist
coverage
playwright-report
test-results
.env
.env*.local
!.env.example
Expand Down
9 changes: 9 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,12 @@
- Added self-hosting documentation and Docker packaging.
- Added health checks, secure cookie defaults, constrained image host configuration, and plugin hook tests.
- Added a stores page, cart quantity updates, SMS sign-in UI, logout route, and example promo banner plugin.
- Added session awareness, order detail/tracking pages, catalog filters, category/store pages, plugin/theme/deployment docs, and security headers.
- Refactored customer authentication into dedicated login, SMS login, registration, and account dashboard surfaces.
- Corrected backend authentication and SMS contracts, validated runtime API payloads, and added stale-session clearing, rate limits, body limits, and safe redirects.
- Added customer-facing delivery selectors and Stripe Payment Element confirmation.
- Added 100% coverage gates, Codecov, Playwright, CodeQL, dependency review, Docker security checks, and Fleetbase `dev-v*` release workflows.
- Disabled incomplete marketplace mode for the single-store v0.1 scope and documented upstream checkout blockers.
- Enabled authenticated profile editing and cash pickup after the Storefront API added token-owner authorization and quote-free cash pickup support.
- Verified Stripe capture against the checkout-owned PaymentIntent and documented idempotent concurrent capture behavior.
- Corrected Fleetbase asset image allowlisting, sale-price selection, and canonical product links to use API-resolvable public IDs.
26 changes: 25 additions & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,38 @@ RUN pnpm install --frozen-lockfile

FROM base AS builder
RUN corepack enable
ARG NEXT_PUBLIC_SITE_URL=http://localhost:3000
ARG NEXT_PUBLIC_DEFAULT_LOCALE=en
ARG NEXT_PUBLIC_DEFAULT_THEME=default
ARG NEXT_PUBLIC_IMAGE_HOSTS=api.fleetbase.io,flb-assets.s3.ap-southeast-1.amazonaws.com
ARG NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY
ARG NEXT_PUBLIC_PROMO_BANNER
ARG NEXT_PUBLIC_CHECKOUT_MESSAGE
ARG NEXT_PUBLIC_SOCKETCLUSTER_HOST
ARG NEXT_PUBLIC_SOCKETCLUSTER_PORT
ARG NEXT_PUBLIC_SOCKETCLUSTER_SECURE=true
ENV NEXT_PUBLIC_SITE_URL=${NEXT_PUBLIC_SITE_URL} \
NEXT_PUBLIC_DEFAULT_LOCALE=${NEXT_PUBLIC_DEFAULT_LOCALE} \
NEXT_PUBLIC_DEFAULT_THEME=${NEXT_PUBLIC_DEFAULT_THEME} \
NEXT_PUBLIC_IMAGE_HOSTS=${NEXT_PUBLIC_IMAGE_HOSTS} \
NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY=${NEXT_PUBLIC_STRIPE_PUBLISHABLE_KEY} \
NEXT_PUBLIC_PROMO_BANNER=${NEXT_PUBLIC_PROMO_BANNER} \
NEXT_PUBLIC_CHECKOUT_MESSAGE=${NEXT_PUBLIC_CHECKOUT_MESSAGE} \
NEXT_PUBLIC_SOCKETCLUSTER_HOST=${NEXT_PUBLIC_SOCKETCLUSTER_HOST} \
NEXT_PUBLIC_SOCKETCLUSTER_PORT=${NEXT_PUBLIC_SOCKETCLUSTER_PORT} \
NEXT_PUBLIC_SOCKETCLUSTER_SECURE=${NEXT_PUBLIC_SOCKETCLUSTER_SECURE}
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN pnpm build

FROM base AS runner
ENV NODE_ENV=production
ENV PORT=3000
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 storefront
RUN apk upgrade --no-cache \
&& rm -rf /usr/local/lib/node_modules/npm /usr/local/lib/node_modules/corepack \
&& rm -f /usr/local/bin/npm /usr/local/bin/npx /usr/local/bin/corepack /usr/local/bin/pnpm /usr/local/bin/pnpx /usr/local/bin/yarn /usr/local/bin/yarnpkg \
&& addgroup --system --gid 1001 nodejs \
&& adduser --system --uid 1001 storefront
COPY --from=builder /app/public ./public
COPY --from=builder /app/.next/standalone ./
COPY --from=builder /app/.next/static ./.next/static
Expand Down
Loading
Loading