deploy: hr-portal

This commit is contained in:
TenX PM
2026-05-04 20:37:44 +00:00
parent 87e9346d62
commit 443c4a2f00
35 changed files with 9795 additions and 13 deletions
+19 -7
View File
@@ -2,26 +2,37 @@
FROM node:20-alpine AS backend-builder
WORKDIR /app/backend
COPY backend/package*.json ./
RUN npm ci --legacy-peer-deps
# Use inline NODE_ENV=development so devDeps (nest CLI) are installed
# but the global NODE_ENV stays production for the actual build
RUN NODE_ENV=development npm ci --legacy-peer-deps --include=dev
COPY backend/ .
RUN npm run build
RUN ./node_modules/.bin/nest build
# Stage 2: Build frontend
FROM node:20-alpine AS frontend-builder
WORKDIR /app/frontend
COPY frontend/package*.json ./
RUN npm ci --legacy-peer-deps
RUN NODE_ENV=development npm ci --legacy-peer-deps --include=dev
COPY frontend/ .
ENV NEXT_PUBLIC_API_URL=/api-backend
ENV NEXT_TELEMETRY_DISABLED=1
ENV NODE_ENV=production
RUN npm run build
# Stage 3: Runtime
FROM node:20-alpine AS runner
FROM node:20-bookworm-slim AS runner
WORKDIR /app
# Install nginx
RUN apk add --no-cache nginx
# Install nginx and MongoDB
RUN apt-get update && apt-get install -y --no-install-recommends \
nginx \
gnupg \
curl \
&& curl -fsSL https://www.mongodb.org/static/pgp/server-7.0.asc | gpg -o /usr/share/keyrings/mongodb-server-7.0.gpg --dearmor \
&& echo "deb [ signed-by=/usr/share/keyrings/mongodb-server-7.0.gpg ] https://repo.mongodb.org/apt/debian bookworm/mongodb-org/7.0 main" > /etc/apt/sources.list.d/mongodb-org-7.0.list \
&& apt-get update && apt-get install -y --no-install-recommends mongodb-org \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /data/db /run/nginx
# Copy backend build
COPY --from=backend-builder /app/backend/dist ./backend/dist
@@ -31,7 +42,8 @@ COPY --from=backend-builder /app/backend/package.json ./backend/
# Copy frontend build (standalone mode)
COPY --from=frontend-builder /app/frontend/.next/standalone ./frontend/
COPY --from=frontend-builder /app/frontend/.next/static ./frontend/.next/static
COPY --from=frontend-builder /app/frontend/public ./frontend/public
# public dir may be empty — copy if exists
COPY --from=frontend-builder /app/frontend/public/ ./frontend/public/
# Nginx config
COPY nginx.conf /etc/nginx/nginx.conf
+7780
View File
File diff suppressed because it is too large Load Diff
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { announcementsApi } from '@/lib/api';
import { formatDate } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { attendanceApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { leavesApi, attendanceApi, reimbursementsApi, announcementsApi } from '@/lib/api';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { leavesApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { payrollApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { employeesApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState, useRef } from 'react';
import { useAuth } from '@/lib/auth-context';
import { reimbursementsApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useAuth } from '@/lib/auth-context';
import { taxApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { announcementsApi, departmentsApi } from '@/lib/api';
import { formatDate } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { attendanceApi, employeesApi } from '@/lib/api';
import { getDaysInMonth, monthNames, getStatusColor } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { employeesApi, leavesApi, reimbursementsApi, reportsApi } from '@/lib/api';
import { formatCurrency, formatDate, getStatusColor } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useParams, useRouter } from 'next/navigation';
import { employeesApi, departmentsApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { employeesApi, departmentsApi } from '@/lib/api';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { useRouter } from 'next/navigation';
import { employeesApi, departmentsApi } from '@/lib/api';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { leavesApi } from '@/lib/api';
import { formatDate, getStatusColor } from '@/lib/utils';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { payrollApi } from '@/lib/api';
import { formatCurrency, monthNames } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { reimbursementsApi } from '@/lib/api';
import { formatDate, formatCurrency, getStatusColor } from '@/lib/utils';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { reportsApi } from '@/lib/api';
import { formatCurrency, monthNames } from '@/lib/utils';
+2
View File
@@ -3,6 +3,8 @@ import { Inter } from 'next/font/google';
import './globals.css';
import { AuthProvider } from '@/lib/auth-context';
export const dynamic = 'force-dynamic';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useState } from 'react';
import { useAuth } from '@/lib/auth-context';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { adminApi, departmentsApi } from '@/lib/api';
import { formatDate } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { adminApi } from '@/lib/api';
import { formatDate } from '@/lib/utils';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { adminApi, employeesApi } from '@/lib/api';
import { formatDate } from '@/lib/utils';
+2
View File
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';
import { useAuth } from '@/lib/auth-context';
@@ -1,4 +1,6 @@
'use client';
export const dynamic = 'force-dynamic';
import { useEffect, useState } from 'react';
import { adminApi } from '@/lib/api';
import Topbar from '@/components/layout/Topbar';
+5
View File
@@ -0,0 +1,5 @@
/// <reference types="next" />
/// <reference types="next/image-types/global" />
// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
+1915
View File
File diff suppressed because it is too large Load Diff
View File
+10 -4
View File
@@ -1,14 +1,20 @@
#!/bin/sh
# Create nginx pid directory
mkdir -p /run/nginx
# Create required directories
mkdir -p /run/nginx /data/db
# Start MongoDB
echo "Starting MongoDB..."
mongod --dbpath /data/db --logpath /var/log/mongod.log --fork --bind_ip 127.0.0.1
echo "Waiting for MongoDB to be ready..."
sleep 3
# Start backend (NestJS)
echo "Starting backend on port 3001..."
cd /app/backend && NODE_ENV=production node dist/main.js &
cd /app/backend && MONGODB_URI=mongodb://127.0.0.1:27017/hr_portal NODE_ENV=production node dist/main.js &
# Give backend a moment to start
sleep 2
sleep 3
# Start frontend (Next.js standalone)
echo "Starting frontend on port 3000..."
+10 -2
View File
@@ -123,13 +123,12 @@ else: print('')
APP_RESPONSE=$(curl -sk -X POST -H "Authorization: Bearer ${COOLIFY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
"${COOLIFY_API}/applications" \
"${COOLIFY_API}/applications/public" \
-d "{
\"project_uuid\": \"${PROJECT_UUID}\",
\"environment_name\": \"production\",
\"server_uuid\": \"${SERVER_UUID}\",
\"destination_uuid\": \"${DESTINATION_UUID}\",
\"type\": \"public\",
\"name\": \"${PROJECT_NAME}\",
\"git_repository\": \"${REPO_FULL_URL}\",
\"git_branch\": \"main\",
@@ -140,6 +139,15 @@ APP_RESPONSE=$(curl -sk -X POST -H "Authorization: Bearer ${COOLIFY_ACCESS_TOKEN
APP_UUID=$(echo "${APP_RESPONSE}" | python3 -c "import sys,json; print(json.load(sys.stdin).get('uuid',''))" 2>/dev/null)
if [ -n "${APP_UUID}" ]; then
# Coolify strips the full URL — patch it back
curl -sk -X PATCH -H "Authorization: Bearer ${COOLIFY_ACCESS_TOKEN}" \
-H "Content-Type: application/json" \
"${COOLIFY_API}/applications/${APP_UUID}" \
-d "{\"git_repository\": \"${REPO_FULL_URL}\"}" > /dev/null
echo "[deploy] Patched git_repository to full URL"
fi
if [ -z "${APP_UUID}" ]; then
echo "[deploy] App creation response: ${APP_RESPONSE}"
# Try to find existing app