Compare commits

..

9 Commits

4 changed files with 256 additions and 23 deletions

View File

@@ -9,9 +9,13 @@ WORKDIR /app
ARG NEXT_PUBLIC_SUPABASE_URL ARG NEXT_PUBLIC_SUPABASE_URL
ARG NEXT_PUBLIC_SUPABASE_ANON_KEY ARG NEXT_PUBLIC_SUPABASE_ANON_KEY
ARG SUPABASE_URL
ARG NEXT_PUBLIC_APP_URL
ARG BUILD_CACHE_BUSTER ARG BUILD_CACHE_BUSTER
ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL} ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}
ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY} ENV NEXT_PUBLIC_SUPABASE_ANON_KEY=${NEXT_PUBLIC_SUPABASE_ANON_KEY}
ENV SUPABASE_URL=${SUPABASE_URL}
ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
ENV BUILD_CACHE_BUSTER=${BUILD_CACHE_BUSTER} ENV BUILD_CACHE_BUSTER=${BUILD_CACHE_BUSTER}
COPY --from=deps /app/node_modules ./node_modules COPY --from=deps /app/node_modules ./node_modules
@@ -28,6 +32,12 @@ FROM node:20-bookworm-slim AS runner
WORKDIR /app WORKDIR /app
ENV NODE_ENV=production ENV NODE_ENV=production
ARG NEXT_PUBLIC_SUPABASE_URL
ARG SUPABASE_URL
ARG NEXT_PUBLIC_APP_URL
ENV NEXT_PUBLIC_SUPABASE_URL=${NEXT_PUBLIC_SUPABASE_URL}
ENV SUPABASE_URL=${SUPABASE_URL}
ENV NEXT_PUBLIC_APP_URL=${NEXT_PUBLIC_APP_URL}
COPY --from=prod-deps /app/node_modules ./node_modules COPY --from=prod-deps /app/node_modules ./node_modules
COPY --from=builder /app/package.json ./package.json COPY --from=builder /app/package.json ./package.json

33
Jenkinsfile vendored
View File

@@ -12,7 +12,8 @@ pipeline {
APP_HOST = "${env.APP_HOST ?: '192.168.68.89'}" APP_HOST = "${env.APP_HOST ?: '192.168.68.89'}"
APP_USER = "${env.APP_USER ?: 'deploy'}" APP_USER = "${env.APP_USER ?: 'deploy'}"
DEPLOY_PATH = "${env.DEPLOY_PATH ?: '/opt/plesk-agency-portal'}" DEPLOY_PATH = "${env.DEPLOY_PATH ?: '/opt/plesk-agency-portal'}"
APP_BASE_URL = "${env.APP_BASE_URL ?: 'http://192.168.68.89:3000'}" APP_BASE_URL = "${env.APP_BASE_URL ?: 'https://portal.rdbcloud.co.uk'}"
NEXT_PUBLIC_APP_URL = "${env.NEXT_PUBLIC_APP_URL ?: 'https://portal.rdbcloud.co.uk'}"
} }
@@ -64,10 +65,22 @@ pipeline {
stage('Build application') { stage('Build application') {
steps { steps {
withCredentials([ withCredentials([
string(credentialsId: 'supabase-public-url', variable: 'NEXT_PUBLIC_SUPABASE_URL'), string(credentialsId: 'supabase-public-url', variable: 'SUPABASE_PUBLIC_URL'),
string(credentialsId: 'supabase-public-anon-key', variable: 'NEXT_PUBLIC_SUPABASE_ANON_KEY') string(credentialsId: 'supabase-public-anon-key', variable: 'NEXT_PUBLIC_SUPABASE_ANON_KEY')
]) { ]) {
sh 'npm run build' sh '''
ANON_PREFIX="$(printf '%s' "$NEXT_PUBLIC_SUPABASE_ANON_KEY" | cut -c1-6)"
echo "ENV DEBUG:"
echo "NEXT_PUBLIC_SUPABASE_URL=$SUPABASE_PUBLIC_URL"
echo "NEXT_PUBLIC_APP_URL=$NEXT_PUBLIC_APP_URL"
echo "NEXT_PUBLIC_SUPABASE_ANON_KEY=${ANON_PREFIX}***"
echo "SUPABASE_URL=$SUPABASE_PUBLIC_URL"
NEXT_PUBLIC_SUPABASE_URL="$SUPABASE_PUBLIC_URL" \
SUPABASE_URL="$SUPABASE_PUBLIC_URL" \
NEXT_PUBLIC_APP_URL="$NEXT_PUBLIC_APP_URL" \
npm run build
'''
} }
} }
} }
@@ -75,10 +88,10 @@ pipeline {
stage('Build Docker image') { stage('Build Docker image') {
steps { steps {
withCredentials([ withCredentials([
string(credentialsId: 'supabase-public-url', variable: 'NEXT_PUBLIC_SUPABASE_URL'), string(credentialsId: 'supabase-public-url', variable: 'SUPABASE_PUBLIC_URL'),
string(credentialsId: 'supabase-public-anon-key', variable: 'NEXT_PUBLIC_SUPABASE_ANON_KEY') string(credentialsId: 'supabase-public-anon-key', variable: 'NEXT_PUBLIC_SUPABASE_ANON_KEY')
]) { ]) {
sh 'docker build --build-arg NEXT_PUBLIC_SUPABASE_URL="$NEXT_PUBLIC_SUPABASE_URL" --build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY="$NEXT_PUBLIC_SUPABASE_ANON_KEY" --build-arg BUILD_CACHE_BUSTER="${BUILD_NUMBER}" -t ${IMAGE_LATEST} -t ${IMAGE_BUILD} .' sh 'docker build --build-arg NEXT_PUBLIC_SUPABASE_URL="$SUPABASE_PUBLIC_URL" --build-arg SUPABASE_URL="$SUPABASE_PUBLIC_URL" --build-arg NEXT_PUBLIC_APP_URL="$NEXT_PUBLIC_APP_URL" --build-arg NEXT_PUBLIC_SUPABASE_ANON_KEY="$NEXT_PUBLIC_SUPABASE_ANON_KEY" --build-arg BUILD_CACHE_BUSTER="${BUILD_NUMBER}" -t ${IMAGE_LATEST} -t ${IMAGE_BUILD} .'
} }
} }
} }
@@ -91,7 +104,9 @@ set -euxo pipefail
SSH_KEY="/var/lib/jenkins/.ssh/plesk_agency_deploy" SSH_KEY="/var/lib/jenkins/.ssh/plesk_agency_deploy"
SSH_USER="deploy" SSH_USER="deploy"
SSH_OPTS="-i ${SSH_KEY} -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null" SSH_COMMON_OPTS="-i ${SSH_KEY} -o BatchMode=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null"
SSH_OPTS="-n ${SSH_COMMON_OPTS}"
RSYNC_SSH="ssh ${SSH_COMMON_OPTS}"
echo "SSH user from configured deploy key: ${SSH_USER}" echo "SSH user from configured deploy key: ${SSH_USER}"
echo "SSH key file path: ${SSH_KEY}" echo "SSH key file path: ${SSH_KEY}"
@@ -107,12 +122,12 @@ docker image inspect "${IMAGE_BUILD}" --format='{{.Id}} {{.RepoTags}}'
ssh -n ${SSH_OPTS} "$SSH_USER@$APP_HOST" "mkdir -p $DEPLOY_PATH/scripts" ssh -n ${SSH_OPTS} "$SSH_USER@$APP_HOST" "mkdir -p $DEPLOY_PATH/scripts"
rsync -az \ rsync -az \
-e "ssh -n ${SSH_OPTS}" \ -e "${RSYNC_SSH}" \
docker-compose.prod.yml \ docker-compose.prod.yml \
"$SSH_USER@$APP_HOST:$DEPLOY_PATH/" "$SSH_USER@$APP_HOST:$DEPLOY_PATH/"
rsync -az \ rsync -az \
-e "ssh -n ${SSH_OPTS}" \ -e "${RSYNC_SSH}" \
scripts/ \ scripts/ \
"$SSH_USER@$APP_HOST:$DEPLOY_PATH/scripts/" "$SSH_USER@$APP_HOST:$DEPLOY_PATH/scripts/"
@@ -123,7 +138,7 @@ ls -lh "${IMAGE_TAR}"
sha256sum "${IMAGE_TAR}" sha256sum "${IMAGE_TAR}"
echo "Copying image archive to remote host via scp" echo "Copying image archive to remote host via scp"
scp ${SSH_OPTS} "${IMAGE_TAR}" "$SSH_USER@$APP_HOST:$DEPLOY_PATH/" scp ${SSH_COMMON_OPTS} "${IMAGE_TAR}" "$SSH_USER@$APP_HOST:$DEPLOY_PATH/"
echo "Verifying image archive exists on remote host" echo "Verifying image archive exists on remote host"
ssh -n ${SSH_OPTS} "$SSH_USER@$APP_HOST" "ls -lh $DEPLOY_PATH/plesk-agency-portal-build.tar" ssh -n ${SSH_OPTS} "$SSH_USER@$APP_HOST" "ls -lh $DEPLOY_PATH/plesk-agency-portal-build.tar"

View File

@@ -1,38 +1,245 @@
# GUARDRAILS # GUARDRAILS
These systems are currently working and must not be broken by AI refactors. These systems are **currently working** and must **not be broken by AI refactors**.
## CI/CD AI tools must **prefer minimal changes** and avoid architecture changes unless explicitly instructed.
Jenkins pipeline builds Docker image and deploys it. ---
# Core Principle
If a task can be solved with a **small change**, the AI must **not perform a large rewrite**.
---
# Git Workflow Rules
The AI must **NEVER commit directly to `master` or `main`.**
All changes must be made via a branch.
## Branch naming conventions
Feature work:
feature/<short-description>
Bug fixes:
fix/<short-description>
Examples:
feature/supabase-public-endpoint
fix/smoke-check-url
fix/magic-link-auth
## Required workflow
1. Create a branch from `master`
2. Make the required changes
3. Commit changes to that branch
4. Show the diff
5. Do **not merge automatically**
6. Push the branch and create a pull request unless explicitly told not to.
7. User performs merge after review
---
# Infrastructure Protection
The AI must **not modify infrastructure** unless explicitly instructed.
This includes:
- DNS records
- reverse proxy configuration
- SSL certificates
- server networking
- firewall rules
- Proxmox configuration
- VM/LXC creation
- Nginx Proxy Manager configuration
- Supabase server configuration
These systems are **managed manually**.
---
# CI/CD
The Jenkins pipeline currently **builds and deploys the application**.
Deployment process: Deployment process:
docker save → scp → docker load docker save → scp → docker load
## Container The AI must **not change the deployment architecture** without instruction.
Allowed CI changes:
- fixing pipeline bugs
- improving reliability
- adding environment variables
- improving logs
Not allowed:
- replacing Jenkins
- replacing Docker deployment
- changing deployment host
- introducing Kubernetes
- major CI architecture changes
---
# Container
Container name:
Name:
plesk-agency-portal plesk-agency-portal
Deploy path: Deployment path:
/opt/plesk-agency-portal /opt/plesk-agency-portal
## Reverse Proxy The AI must **not rename containers or paths** without explicit approval.
Managed by Nginx Proxy Manager ---
Public URL: # Reverse Proxy
Reverse proxy is managed by:
Nginx Proxy Manager
Public application URL:
https://portal.rdbcloud.co.uk https://portal.rdbcloud.co.uk
## Auth Supabase public endpoint:
Supabase Magic Link authentication https://supabase.rdbcloud.co.uk
Environment variables: The AI must **not change reverse proxy configuration**.
---
# Authentication
Authentication system:
Supabase Magic Link
Required environment variables:
NEXT_PUBLIC_SUPABASE_URL NEXT_PUBLIC_SUPABASE_URL
SUPABASE_URL SUPABASE_URL
NEXT_PUBLIC_SUPABASE_ANON_KEY
AI tools must avoid large rewrites or architectural changes without explicit instruction. The AI must **not replace the authentication system**.
---
# Environment Variables
Secrets must **never be committed to the repository**.
Secrets must come from:
- Jenkins credentials
- environment variables
- deployment configuration
Never commit:
API keys
private tokens
database passwords
service keys
---
# Docker
Docker is used for **build and deployment**.
The AI may:
- update Dockerfile
- fix build issues
- improve caching
The AI must **not introduce complex container orchestration**.
Not allowed:
kubernetes
docker swarm
nomad
terraform
Unless explicitly requested.
---
# Database
Database system:
Supabase Postgres
The AI must **not perform destructive database changes**.
Not allowed automatically:
DROP TABLE
DROP COLUMN
DELETE large datasets
schema rewrites
Schema changes must be **reviewed first**.
---
# Safe Refactoring Rules
Allowed:
- small code improvements
- bug fixes
- performance improvements
- logging improvements
- type fixes
- lint fixes
Avoid:
- large rewrites
- framework changes
- directory restructuring
- renaming large parts of the codebase
Unless specifically requested.
---
# When AI Is Unsure
If the AI is unsure:
1. Explain the risk
2. Ask for clarification
3. Do **not proceed with risky changes**
---
# Priority Order
When making decisions, the AI must prioritise:
1. **Do not break production**
2. **Respect guardrails**
3. **Make minimal safe changes**
4. **Preserve deployment pipeline**
5. **Follow branch workflow**

View File

@@ -35,7 +35,8 @@ export const env = {
stripeSecretKey: process.env.STRIPE_SECRET_KEY ?? "", stripeSecretKey: process.env.STRIPE_SECRET_KEY ?? "",
stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET ?? "", stripeWebhookSecret: process.env.STRIPE_WEBHOOK_SECRET ?? "",
stripePriceId: process.env.STRIPE_PRICE_ID ?? "", stripePriceId: process.env.STRIPE_PRICE_ID ?? "",
supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL ?? "", supabaseUrl:
process.env.SUPABASE_URL ?? process.env.NEXT_PUBLIC_SUPABASE_URL ?? "",
supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "", supabaseAnonKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY ?? "",
supabaseServiceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY ?? "", supabaseServiceRoleKey: process.env.SUPABASE_SERVICE_ROLE_KEY ?? "",
}; };