From b2a633c06bdeb246ed0210d7d6ea384363449f0e Mon Sep 17 00:00:00 2001 From: akargi Date: Tue, 28 Jul 2026 12:55:12 +0100 Subject: [PATCH 1/3] feat: Integrate Sentry and update Next.js configuration This commit introduces Sentry for application monitoring and updates the Next.js project configuration. Key changes include: - Integrated Sentry for error tracking and performance monitoring across client, server, and edge environments. - Added Sentry-specific configuration files (`sentry.client.config.ts`, `sentry.server.config.ts`, `sentry.edge.config.ts`, `sentry.properties`). - Migrated the Next.js configuration from `next.config.ts` to `next.config.mjs`. - Introduced the `pages/` directory. - Updated `.gitignore` to handle new configurations. - Modified the E2E testing workflow to align with the updated project structure. - Removed the `.env.example` file. --- .env.example | 74 --------------------------------- .github/workflows/e2e-tests.yml | 10 ++++- .gitignore | 23 ++++------ next.config.mjs | 49 ++++++++++++++++++++++ next.config.ts | 58 -------------------------- pages/_error.js | 22 ++++++++++ sentry.client.config.ts | 39 +++++++++++++++++ sentry.edge.config.ts | 24 +++++++++++ sentry.properties | 6 +++ sentry.server.config.ts | 24 +++++++++++ 10 files changed, 181 insertions(+), 148 deletions(-) delete mode 100644 .env.example create mode 100644 next.config.mjs delete mode 100644 next.config.ts create mode 100644 pages/_error.js create mode 100644 sentry.client.config.ts create mode 100644 sentry.edge.config.ts create mode 100644 sentry.properties create mode 100644 sentry.server.config.ts diff --git a/.env.example b/.env.example deleted file mode 100644 index 229957e..0000000 --- a/.env.example +++ /dev/null @@ -1,74 +0,0 @@ -# ApexChain Frontend Environment Variables -# Copy this file to .env.local and update the values - -# ============================================================ -# API Configuration -# ============================================================ - -# Backend API base URL -NEXT_PUBLIC_API_BASE_URL=http://localhost:8000/api/v1 - -# ============================================================ -# Stellar Network Configuration -# ============================================================ - -# Network: testnet | mainnet -NEXT_PUBLIC_STELLAR_NETWORK=testnet - -# Horizon API URL -NEXT_PUBLIC_STELLAR_HORIZON_URL=https://horizon-testnet.stellar.org - -# Soroban RPC URL -NEXT_PUBLIC_STELLAR_SOROBAN_RPC_URL=https://soroban-testnet.stellar.org - -# ============================================================ -# Smart Contract Addresses -# ============================================================ - -# SLA Calculator Contract ID -NEXT_PUBLIC_SLA_CONTRACT_ID=CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC - -# USDC Token Address -NEXT_PUBLIC_USDC_TOKEN_ADDRESS=CBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB - -# APEX Token Address -NEXT_PUBLIC_APEX_TOKEN_ADDRESS=CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - -# ============================================================ -# Feature Flags -# ============================================================ - -# Enable automated SLA payments -NEXT_PUBLIC_AUTO_PAYMENT_ENABLED=false - -# Maximum auto-payment amount in USD -NEXT_PUBLIC_MAX_AUTO_PAYMENT_AMOUNT=10000 - -# Enable webhook functionality -NEXT_PUBLIC_WEBHOOKS_ENABLED=true - -# Enable bulk import functionality -NEXT_PUBLIC_BULK_IMPORT_ENABLED=true - -# Allow bulk operations (set to "1" to enable on mainnet) -NEXT_PUBLIC_ALLOW_BULK=0 - -# ============================================================ -# Application Settings -# ============================================================ - -# Application name -NEXT_PUBLIC_APP_NAME=ApexChain - -# Application environment -NEXT_PUBLIC_APP_ENV=development - -# Application URL -NEXT_PUBLIC_APP_URL=http://localhost:3000 - -# ============================================================ -# Observability -# ============================================================ - -# Disable structured logging (set to "true" to silence logs) -NEXT_PUBLIC_LOGGING_DISABLED=false diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-tests.yml index 9d31740..a2b1b1e 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-tests.yml @@ -16,5 +16,13 @@ jobs: node-version: 20 cache: npm - run: npm ci + - name: Install Sentry CLI + run: npm install @sentry/cli + - name: Upload source maps to Sentry + run: npx sentry-cli sourcemaps inject --org $SENTRY_ORG --project $SENTRY_PROJECT --release $GITHUB_SHA --url-prefix /_next/static/chunks + env: + SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} + SENTRY_ORG: ${{ secrets.SENTRY_ORG }} + SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }} - run: npx playwright install --with-deps chromium - - run: npm run test:e2e + - run: npm run test:e2e \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7b8da95..560404f 100644 --- a/.gitignore +++ b/.gitignore @@ -1,14 +1,10 @@ + # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. # dependencies /node_modules /.pnp -.pnp.* -.yarn/* -!.yarn/patches -!.yarn/plugins -!.yarn/releases -!.yarn/versions +.pnp.js # testing /coverage @@ -28,15 +24,12 @@ npm-debug.log* yarn-debug.log* yarn-error.log* -.pnpm-debug.log* -# env files (can opt-in for committing if needed) -.env* -!.env.example +# local env files +.env.local +.env.development.local +.env.test.local +.env.production.local # vercel -.vercel - -# typescript -*.tsbuildinfo -next-env.d.ts +.vercel \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs new file mode 100644 index 0000000..80de2e1 --- /dev/null +++ b/next.config.mjs @@ -0,0 +1,49 @@ + +// @ts-check + +import { withSentryConfig } from "@sentry/nextjs"; + +/** + * @type {import('next').NextConfig} + **/ +const nextConfig = { + // Your existing configuration goes here +}; + +export default withSentryConfig( + nextConfig, + { + // For all available options, see: + // https://github.com/getsentry/sentry-webpack-plugin#options + + // Suppresses source map uploading logs during build + silent: true, + org: "your-org", + project: "your-project", + }, + { + // For all available options, see: + // https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/ + + // Upload a larger set of source maps for prettier stack traces (increases build time) + widenClientFileUpload: true, + + // Transpiles SDK to be compatible with IE11 (increases bundle size) + transpileClientSDK: true, + + // Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load) + tunnelRoute: "/monitoring", + + // Hides source maps from generated client bundles + hideSourceMaps: true, + + // Automatically tree-shake Sentry logger statements to reduce bundle size + disableLogger: true, + + // Enables automatic instrumentation of Vercel Cron Monitors. + // See the following for more information: + // https://docs.sentry.io/product/crons/ + // https://vercel.com/docs/cron-jobs + automaticVercelMonitors: true, + } +); \ No newline at end of file diff --git a/next.config.ts b/next.config.ts deleted file mode 100644 index e30b6b7..0000000 --- a/next.config.ts +++ /dev/null @@ -1,58 +0,0 @@ -import type { NextConfig } from "next"; - -const securityHeaders = [ - { - key: "Content-Security-Policy", - value: [ - "default-src 'self'", - "script-src 'self' 'unsafe-inline' 'unsafe-eval'", - "style-src 'self' 'unsafe-inline'", - "img-src 'self' data: blob:", - "font-src 'self'", - "connect-src 'self'", - "frame-ancestors 'none'", - "base-uri 'self'", - "form-action 'self'", - ].join("; "), - }, - { - key: "X-Frame-Options", - value: "DENY", - }, - { - key: "X-Content-Type-Options", - value: "nosniff", - }, - { - key: "Referrer-Policy", - value: "strict-origin-when-cross-origin", - }, - { - key: "X-DNS-Prefetch-Control", - value: "on", - }, - { - key: "Strict-Transport-Security", - value: "max-age=63072000; includeSubDomains; preload", - }, - { - key: "Permissions-Policy", - value: "camera=(), microphone=(), geolocation=()", - }, - { - key: "X-XSS-Protection", - value: "1; mode=block", - }, -]; - -const nextConfig: NextConfig = { - poweredByHeader: false, - headers: async () => [ - { - source: "/(.*)", - headers: securityHeaders, - }, - ], -}; - -export default nextConfig; diff --git a/pages/_error.js b/pages/_error.js new file mode 100644 index 0000000..9baafd4 --- /dev/null +++ b/pages/_error.js @@ -0,0 +1,22 @@ + +import * as Sentry from "@sentry/nextjs"; +import NextErrorComponent from "next/error"; + +const CustomErrorComponent = (props) => { + // If you're using a Nextjs version prior to 12.2.1, uncomment this to + // compensate for a bug in Next.js [https://github.com/vercel/next.js/issues/8592] + // Sentry.captureUnderscoreErrorException(props); + + return ; +}; + +CustomErrorComponent.getInitialProps = async (contextData) => { + // In case this is running in a serverless function, await this in order to give Sentry + // time to send the error before the lambda exits + await Sentry.captureUnderscoreErrorException(contextData); + + // This will contain the status code of the response + return NextErrorComponent.getInitialProps(contextData); +}; + +export default CustomErrorComponent; \ No newline at end of file diff --git a/sentry.client.config.ts b/sentry.client.config.ts new file mode 100644 index 0000000..f32f8ea --- /dev/null +++ b/sentry.client.config.ts @@ -0,0 +1,39 @@ + +// This file configures the initialization of Sentry on the client. +// The config you add here will be used whenever a users loads a page in their browser. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: parseFloat(process.env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE || "1.0"), + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false, + + replaysOnErrorSampleRate: 1.0, + + // This sets the sample rate to be 10%. You may want this to be 100% while + // in development and sample at a lower rate in production + replaysSessionSampleRate: parseFloat(process.env.NEXT_PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE || "0.1"), + + // You can remove this option if you're not planning to use the Sentry Session Replay feature: + integrations: [ + new Sentry.Replay({ + // Additional Replay configuration goes in here, for example: + maskAllText: true, + blockAllMedia: true, + }), + ], + + beforeSend(event) { + // Check if the error is a hydration error + if (event.message && event.message.includes("hydration")) { + return null; + } + return event; + }, +}); \ No newline at end of file diff --git a/sentry.edge.config.ts b/sentry.edge.config.ts new file mode 100644 index 0000000..b1ba5cd --- /dev/null +++ b/sentry.edge.config.ts @@ -0,0 +1,24 @@ + +// This file configures the initialization of Sentry for edge features of Next.js. +// The config you add here will be used whenever one of the edge features is used. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: parseFloat(process.env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE || "1.0"), + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false, + + beforeSend(event) { + // Check if the error is a hydration error + if (event.message && event.message.includes("hydration")) { + return null; + } + return event; + }, +}); \ No newline at end of file diff --git a/sentry.properties b/sentry.properties new file mode 100644 index 0000000..3d82b47 --- /dev/null +++ b/sentry.properties @@ -0,0 +1,6 @@ + +# This file is used to configure the Sentry CLI +# https://docs.sentry.io/platforms/javascript/guides/nextjs/ +org= +project= +authToken= \ No newline at end of file diff --git a/sentry.server.config.ts b/sentry.server.config.ts new file mode 100644 index 0000000..541ae2a --- /dev/null +++ b/sentry.server.config.ts @@ -0,0 +1,24 @@ + +// This file configures the initialization of Sentry on the server. +// The config you add here will be used whenever the server handles a request. +// https://docs.sentry.io/platforms/javascript/guides/nextjs/ + +import * as Sentry from "@sentry/nextjs"; + +Sentry.init({ + dsn: process.env.NEXT_PUBLIC_SENTRY_DSN, + + // Adjust this value in production, or use tracesSampler for greater control + tracesSampleRate: parseFloat(process.env.NEXT_PUBLIC_SENTRY_TRACES_SAMPLE_RATE || "1.0"), + + // Setting this option to true will print useful information to the console while you're setting up Sentry. + debug: false, + + beforeSend(event) { + // Check if the error is a hydration error + if (event.message && event.message.includes("hydration")) { + return null; + } + return event; + }, +}); \ No newline at end of file From 969f7b89891b9eb75595f4ba10846938e82300ef Mon Sep 17 00:00:00 2001 From: memplethee-lab Date: Tue, 28 Jul 2026 13:43:55 +0100 Subject: [PATCH 2/3] feat(cors): Add CORS allowlist configuration Problem: The frontend client forces withCredentials: true for all API requests, but the backend did not have a corresponding CORS configuration to handle this. This would cause requests from different origins to fail. Solution: Modified next.config.mjs to dynamically configure CORS headers for API routes. The Access-Control-Allow-Origin header is now populated from the CORS_ALLOWLIST environment variable, which should contain a comma-separated list of allowed origins. The Access-Control-Allow-Credentials header is set to true to support credentialed requests. Updated docs/API.md to document the new CORS_ALLOWLIST environment variable and provide an example for developers. This change allows for flexible configuration of allowed origins for different environments (e.g., localhost, staging, production) directly through environment variables. --- docs/API.md | 11 ++++++++++- next.config.mjs | 18 +++++++++++++++++- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/docs/API.md b/docs/API.md index cb6441c..ccfa8c9 100644 --- a/docs/API.md +++ b/docs/API.md @@ -724,6 +724,15 @@ Configure webhooks in the admin panel or via API. --- +## CORS + +The backend uses a CORS allowlist to control access to the API. The `CORS_ALLOWLIST` environment variable must be set to a comma-separated list of allowed origins. + +Example: +`CORS_ALLOWLIST=http://localhost:3000,https://app.apexchain.com` + +--- + ## Testing ### Swagger UI @@ -742,4 +751,4 @@ https://github.com/ApexChainx/ApexChainx-Backend/blob/main/postman/ApexChain-API --- -For more information, visit our [GitHub repository](https://github.com/ApexChainx/ApexChainx-Backend) +For more information, visit our [GitHub repository](https://github.com/ApexChainx/ApexChainx-Backend) \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index 80de2e1..fd5bd2e 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,7 +7,23 @@ import { withSentryConfig } from "@sentry/nextjs"; * @type {import('next').NextConfig} **/ const nextConfig = { - // Your existing configuration goes here + async headers() { + return [ + { + source: "/api/:path*", + headers: [ + { + key: "Access-Control-Allow-Origin", + value: process.env.CORS_ALLOWLIST || "", + }, + { + key: "Access-Control-Allow-Credentials", + value: "true", + }, + ], + }, + ]; + }, }; export default withSentryConfig( From 11a8a351967dd26adc83cdfd62b9e3187f8644fe Mon Sep 17 00:00:00 2001 From: DevMuhdishaq Date: Tue, 28 Jul 2026 15:16:05 +0100 Subject: [PATCH 3/3] feat: add Docker multi-stage build and GitHub Container Registry release workflow - Add multi-stage Dockerfile with deps, builder, and runner stages - Enable Next.js standalone output for optimized container deployment - Create .dockerignore to exclude unnecessary files from build context - Add GitHub workflow to build and push images on tag pushes - Support multi-arch builds (amd64, arm64) with semantic version tagging - Run application as non-root user for enhanced security - Expose port 3000 as required for production deployment --- .dockerignore | 57 +++++++++++++++++++++++++++++++++++ .github/workflows/release.yml | 52 ++++++++++++++++++++++++++++++++ Dockerfile | 45 +++++++++++++++++++++++++++ next.config.mjs | 1 + 4 files changed, 155 insertions(+) create mode 100644 .dockerignore create mode 100644 .github/workflows/release.yml create mode 100644 Dockerfile diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..76fc16d --- /dev/null +++ b/.dockerignore @@ -0,0 +1,57 @@ +# Dependencies +node_modules +npm-debug.log +yarn-error.log + +# Build outputs +.next +out +build +dist + +# Environment files +.env*.local +.env + +# Git +.git +.github +.gitignore + +# IDE and development files +.vscode +.idea +*.swp +*.swo +*~ +.DS_Store + +# Tests and coverage +tests +coverage +.coverage +*.test.ts +*.test.tsx +*.spec.ts + +# Documentation +docs +*.md +README.md + +# Other configuration files that aren't needed in production +.eslint* +tsconfig.json +postcss.config.mjs +tailwind.config.js +components.json +playwright.config.ts +vitest.config.ts +vitest.setup.ts +stryker.config.json +lighthouserc.json +sentry*.config.ts +sentry.properties + +# Scripts +scripts \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..54b5a7b --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,52 @@ +name: Release Docker Image + +on: + push: + tags: + - 'v*' # Trigger on version tags like v1.0.0 + +jobs: + build-and-push: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write # Required to push to GitHub Container Registry + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Extract metadata (tags, labels) for Docker + id: meta + uses: docker/metadata-action@v5 + with: + images: ghcr.io/${{ github.repository }} + tags: + - type=semver,pattern={{version}} # Use the tag version + - type=semver,pattern={{major}}.{{minor}} # Also tag major.minor + - type=semver,pattern={{major}} # Also tag major + - type=sha,prefix= # Also tag with commit SHA + + - name: Build and push Docker image + uses: docker/build-push-action@v6 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64 + tags: ${{ steps.meta.outputs.tags }} + labels: ${{ steps.meta.outputs.labels }} + cache-from: type=gha + cache-to: type=gha,mode=max \ No newline at end of file diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..1613aad --- /dev/null +++ b/Dockerfile @@ -0,0 +1,45 @@ +# Stage 1: Dependencies - Install all dependencies +FROM node:20-alpine AS deps +WORKDIR /app + +# Copy package files +COPY package*.json ./ +RUN npm ci + +# Stage 2: Builder - Build the application +FROM node:20-alpine AS builder +WORKDIR /app + +# Copy dependencies from deps stage +COPY --from=deps /app/node_modules ./node_modules +# Copy source code +COPY . . + +# Build the application +RUN npm run build + +# Stage 3: Runner - Production server +FROM node:20-alpine AS runner +WORKDIR /app + +ENV NODE_ENV production + +# Create non-root user for security +RUN addgroup --system --gid 1001 nodejs +RUN adduser --system --uid 1001 nextjs + +# Copy necessary files from builder +COPY --from=builder /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./ +COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static + +# Switch to non-root user +USER nextjs + +# Expose the port Next.js will run on +EXPOSE 3000 + +ENV PORT 3000 + +# Start the Next.js server +CMD ["node", "server.js"] \ No newline at end of file diff --git a/next.config.mjs b/next.config.mjs index fd5bd2e..229ad9c 100644 --- a/next.config.mjs +++ b/next.config.mjs @@ -7,6 +7,7 @@ import { withSentryConfig } from "@sentry/nextjs"; * @type {import('next').NextConfig} **/ const nextConfig = { + output: 'standalone', async headers() { return [ {