diff --git a/.gitignore b/.gitignore index 7485fbf5..fbab4145 100644 --- a/.gitignore +++ b/.gitignore @@ -45,3 +45,6 @@ public/swagger.json certificates/cert-key.pem certificates/cert.pem gitclean.sh + +dockerfile +Jenkinsfile diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 00000000..191b532f --- /dev/null +++ b/Dockerfile @@ -0,0 +1,25 @@ +# Use official Node.js 20 LTS image +FROM node:20-alpine + +# Set working directory +WORKDIR /app + +# Install dependencies first (better caching) +COPY package*.json ./ +RUN npm ci --production + +# Copy source files (including .next build output if pre-built) +COPY . . + +# If you want to build inside container, uncomment: +# RUN npm run build + +# Use non-root user (optional, but recommended) +RUN addgroup -S appgroup && adduser -S appuser -G appgroup +USER appuser + +# Expose port your app listens on +EXPOSE 3000 + +# Start your Next.js app (adjust if using custom server) +CMD ["npm", "start"] diff --git a/Jenkinsfile b/Jenkinsfile new file mode 100644 index 00000000..71cc12b6 --- /dev/null +++ b/Jenkinsfile @@ -0,0 +1,50 @@ +pipeline { + agent { + docker { + image 'node:20' // ✅ Node.js 20 with npm included + } + } + + environment { + IMAGE = "pedw-app" + } + + stages { + stage('Install & Build') { + steps { + sh 'npm ci' + sh 'npm run build' + } + } + + stage('Build Docker Image') { + steps { + sh "docker build -t $REGISTRY/$IMAGE:latest ." + } + } + + stage('Push Docker Image') { + steps { + withCredentials([usernamePassword(credentialsId: 'dockerhub-credentials', usernameVariable: 'DOCKER_USER', passwordVariable: 'DOCKER_PASS')]) { + sh "echo $DOCKER_PASS | docker login -u $DOCKER_USER --password-stdin" + sh "docker push $REGISTRY/$IMAGE:latest" + } + } + } + + stage('Deploy to Plesk') { + steps { + sshagent(['plesk-ssh-key']) { + sh """ + ssh root@your-plesk-domain.com ' + docker pull $REGISTRY/$IMAGE:latest && + docker stop nextjs-app || true && + docker rm nextjs-app || true && + docker run -d --restart=always --name nextjs-app -p 3000:3000 $REGISTRY/$IMAGE:latest + ' + """ + } + } + } + } +} diff --git a/components/case/representation/representationComplete.js b/components/case/representation/representationComplete.js index 82340e70..0bfa4b12 100644 --- a/components/case/representation/representationComplete.js +++ b/components/case/representation/representationComplete.js @@ -91,7 +91,8 @@ const RepComplete = (props) => { : props.props.currentView.caseReference .representationType, }), - setRepresentationMessageSent(true)); + setRepresentationMessageSent(true)), + window.scrollTo({ top: 0, behavior: "smooth" }); }); return (
diff --git a/components/newappeal/complete.js b/components/newappeal/complete.js index 68c1e1f8..648980c2 100644 --- a/components/newappeal/complete.js +++ b/components/newappeal/complete.js @@ -65,6 +65,7 @@ let CompleteAppeal = (props) => { console.log("patching///////////////"); // //patchCase(incidentId); + window.scrollTo({ top: 0, behavior: "smooth" }); }, [ props.appealType.caseReference, props.props.form.appealForm.values,