fix(ci): dockerfile build script
This commit is contained in:
93
.dockerignore
Normal file
93
.dockerignore
Normal file
@@ -0,0 +1,93 @@
|
||||
# Dependencies
|
||||
node_modules/
|
||||
**/node_modules/
|
||||
.pnpm-store/
|
||||
|
||||
# Build outputs
|
||||
dist/
|
||||
build/
|
||||
**/dist/
|
||||
**/build/
|
||||
*.tsbuildinfo
|
||||
|
||||
# Development files
|
||||
.git/
|
||||
.gitignore
|
||||
.vscode/
|
||||
.idea/
|
||||
*.swp
|
||||
*.swo
|
||||
*~
|
||||
|
||||
# Test files
|
||||
test/
|
||||
**/test/
|
||||
**/*.spec.ts
|
||||
**/*.test.ts
|
||||
**/*.e2e-spec.ts
|
||||
coverage/
|
||||
.nyc_output/
|
||||
test-results/
|
||||
playwright-report/
|
||||
|
||||
# Documentation
|
||||
*.md
|
||||
!README.md
|
||||
docs/
|
||||
CHANGELOG.md
|
||||
CONTRIBUTING.md
|
||||
DISCLAIMER
|
||||
LICENSE
|
||||
|
||||
# CI/CD
|
||||
.github/
|
||||
.gitpod.yml
|
||||
|
||||
# Environment files
|
||||
.env
|
||||
.env.*
|
||||
!.env.example
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
logs/
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
|
||||
# OS files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Docker files (don't copy Dockerfiles into themselves)
|
||||
docker-compose*.yml
|
||||
Dockerfile*
|
||||
.dockerignore
|
||||
|
||||
# Misc
|
||||
.cache/
|
||||
.temp/
|
||||
tmp/
|
||||
*.tmp
|
||||
.qodo/
|
||||
e2e/
|
||||
playwright.config.ts
|
||||
|
||||
# Source maps (not needed in production)
|
||||
*.map
|
||||
|
||||
# TypeScript configs (not needed at runtime)
|
||||
tsconfig*.json
|
||||
!tsconfig.json
|
||||
|
||||
# Linting/formatting
|
||||
.eslintrc*
|
||||
.prettierrc*
|
||||
.eslintcache
|
||||
|
||||
# Package manager locks (we copy them explicitly)
|
||||
# pnpm-lock.yaml
|
||||
@@ -58,6 +58,12 @@ services:
|
||||
# System database
|
||||
- SYSTEM_DB_NAME=${SYSTEM_DB_NAME}
|
||||
|
||||
# Redis
|
||||
- REDIS_HOST=redis
|
||||
- REDIS_PORT=6379
|
||||
- QUEUE_HOST=redis
|
||||
- QUEUE_PORT=6379
|
||||
|
||||
# Tenants databases
|
||||
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
||||
|
||||
|
||||
@@ -35,4 +35,4 @@ WORKDIR /app/packages/server
|
||||
RUN git clone https://github.com/vishnubob/wait-for-it.git
|
||||
|
||||
# Once we listen the mysql port run the migration task.
|
||||
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "node ./build/commands.js system:migrate:latest && node ./build/commands.js tenants:migrate:latest"
|
||||
CMD ./wait-for-it/wait-for-it.sh mysql:3306 -- sh -c "pnpm run system:migrate:latest && pnpm run tenants:migrate:latest"
|
||||
|
||||
@@ -1,100 +1,99 @@
|
||||
FROM node:18.16.0-alpine as build
|
||||
# Stage 1: Build
|
||||
FROM node:18.16.0-alpine AS builder
|
||||
|
||||
USER root
|
||||
|
||||
ARG MAIL_HOST= \
|
||||
MAIL_USERNAME= \
|
||||
MAIL_PASSWORD= \
|
||||
MAIL_PORT= \
|
||||
MAIL_SECURE= \
|
||||
MAIL_FROM_NAME= \
|
||||
MAIL_FROM_ADDRESS= \
|
||||
# Database
|
||||
DB_HOST= \
|
||||
DB_USER= \
|
||||
DB_PASSWORD= \
|
||||
DB_CHARSET= \
|
||||
# System database.
|
||||
SYSTEM_DB_NAME= \
|
||||
SYSTEM_DB_PASSWORD= \
|
||||
SYSTEM_DB_USER= \
|
||||
SYSTEM_DB_HOST= \
|
||||
SYSTEM_DB_CHARSET= \
|
||||
# Tenant databases.
|
||||
TENANT_DB_USER= \
|
||||
TENANT_DB_PASSWORD= \
|
||||
TENANT_DB_HOST= \
|
||||
TENANT_DB_NAME_PERFIX= \
|
||||
TENANT_DB_CHARSET= \
|
||||
# Authentication
|
||||
JWT_SECRET= \
|
||||
# Application
|
||||
BASE_URL= \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED= \
|
||||
SIGNUP_ALLOWED_DOMAINS= \
|
||||
SIGNUP_ALLOWED_EMAILS=
|
||||
|
||||
ENV MAIL_HOST=$MAIL_HOST \
|
||||
MAIL_USERNAME=$MAIL_USERNAME \
|
||||
MAIL_PASSWORD=$MAIL_PASSWORD \
|
||||
MAIL_PORT=$MAIL_PORT \
|
||||
MAIL_SECURE=$MAIL_SECURE \
|
||||
MAIL_FROM_NAME=$MAIL_FROM_NAME \
|
||||
MAIL_FROM_ADDRESS=$MAIL_FROM_ADDRESS \
|
||||
# Database
|
||||
DB_HOST=$DB_HOST \
|
||||
DB_USER=$DB_USER \
|
||||
DB_PASSWORD=$DB_PASSWORD \
|
||||
DB_CHARSET=$DB_CHARSET \
|
||||
# System database.
|
||||
SYSTEM_DB_HOST=$SYSTEM_DB_HOST \
|
||||
SYSTEM_DB_USER=$SYSTEM_DB_USER \
|
||||
SYSTEM_DB_PASSWORD=$SYSTEM_DB_PASSWORD \
|
||||
SYSTEM_DB_NAME=$SYSTEM_DB_NAME \
|
||||
SYSTEM_DB_CHARSET=$SYSTEM_DB_CHARSET \
|
||||
# Tenant databases.
|
||||
TENANT_DB_NAME_PERFIX=$TENANT_DB_NAME_PERFIX \
|
||||
TENANT_DB_HOST=$TENANT_DB_HOST \
|
||||
TENANT_DB_PASSWORD=$TENANT_DB_PASSWORD \
|
||||
TENANT_DB_USER=$TENANT_DB_USER \
|
||||
TENANT_DB_CHARSET=$TENANT_DB_CHARSET \
|
||||
# Authentication
|
||||
JWT_SECRET=$JWT_SECRET \
|
||||
# Application
|
||||
BASE_URL=$BASE_URL \
|
||||
# Sign-up restriction
|
||||
SIGNUP_DISABLED=$SIGNUP_DISABLED \
|
||||
SIGNUP_ALLOWED_DOMAINS=$SIGNUP_ALLOWED_DOMAINS \
|
||||
SIGNUP_ALLOWED_EMAILS=$SIGNUP_ALLOWED_EMAILS
|
||||
|
||||
# New Relic config file.
|
||||
ENV NEW_RELIC_NO_CONFIG_FILE=true
|
||||
|
||||
# Create app directory.
|
||||
WORKDIR /app
|
||||
|
||||
RUN chown node:node /
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache python3 build-base chromium
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY --chown=node:node ./ ./
|
||||
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
|
||||
# Set PYHTON env
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install packages dependencies for production.
|
||||
RUN pnpm install
|
||||
# Copy package files for dependency installation
|
||||
COPY --chown=node:node package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./
|
||||
COPY --chown=node:node packages/server/package.json ./packages/server/
|
||||
COPY --chown=node:node shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=node:node shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=node:node shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Install all dependencies (including devDependencies for build)
|
||||
RUN pnpm install --frozen-lockfile
|
||||
|
||||
# Copy source code
|
||||
COPY --chown=node:node ./packages/server ./packages/server
|
||||
COPY --chown=node:node ./shared/bigcapital-utils ./shared/bigcapital-utils
|
||||
COPY --chown=node:node ./shared/pdf-templates ./shared/pdf-templates
|
||||
COPY --chown=node:node ./shared/email-components ./shared/email-components
|
||||
|
||||
# # Creates a "dist" folder with the production build
|
||||
# Build NestJS application
|
||||
RUN pnpm run build:server --skip-nx-cache
|
||||
|
||||
CMD [ "node", "./packages/server/build/index.js" ]
|
||||
# Stage 2: Production
|
||||
FROM node:18.16.0-alpine AS production
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Install pnpm for production
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Create non-root user
|
||||
RUN addgroup -g 1001 -S nodejs && \
|
||||
adduser -S nodejs -u 1001
|
||||
|
||||
# Install build dependencies for native modules (bcrypt, etc.)
|
||||
RUN apk add --no-cache python3 build-base
|
||||
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Copy package files for production dependency installation
|
||||
COPY --chown=nodejs:nodejs package.json pnpm-lock.yaml pnpm-workspace.yaml ./
|
||||
COPY --chown=nodejs:nodejs packages/server/package.json ./packages/server/
|
||||
COPY --chown=nodejs:nodejs shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=nodejs:nodejs shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=nodejs:nodejs shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Copy .husky directory (needed for husky install command)
|
||||
COPY --chown=nodejs:nodejs .husky ./.husky
|
||||
|
||||
# Install only production dependencies
|
||||
# Install husky temporarily so prepare script can run, then remove it
|
||||
RUN pnpm add -D -w husky && \
|
||||
pnpm install --prod --frozen-lockfile && \
|
||||
pnpm remove -w husky && \
|
||||
# Remove build dependencies to reduce image size
|
||||
apk del python3 build-base
|
||||
|
||||
# Copy built application from builder stage
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/dist ./packages/server/dist
|
||||
|
||||
# Copy static assets (i18n, public, static directories)
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/i18n ./packages/server/dist/i18n
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/public ./packages/server/public
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/static ./packages/server/static
|
||||
|
||||
# Copy built shared packages (dist folders and package.json for module resolution)
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/shared/bigcapital-utils/dist ./shared/bigcapital-utils/dist
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/shared/pdf-templates/dist ./shared/pdf-templates/dist
|
||||
COPY --from=builder --chown=nodejs:nodejs /app/shared/email-components/dist ./shared/email-components/dist
|
||||
|
||||
# Set runtime environment variables (these should be provided at runtime via docker-compose or k8s)
|
||||
ENV NODE_ENV=production
|
||||
ENV NEW_RELIC_NO_CONFIG_FILE=true
|
||||
ENV PORT=3000
|
||||
|
||||
# Switch to non-root user
|
||||
USER nodejs
|
||||
|
||||
# Expose port
|
||||
EXPOSE 3000
|
||||
|
||||
# Health check - uses /api/system_db ping endpoint
|
||||
HEALTHCHECK --interval=30s --timeout=3s --start-period=40s --retries=3 \
|
||||
CMD node -e "require('http').get('http://localhost:3000/api/system_db', (r) => {process.exit(r.statusCode >= 200 && r.statusCode < 300 ? 0 : 1)}).on('error', () => process.exit(1))"
|
||||
|
||||
# Start the application
|
||||
CMD [ "node", "packages/server/dist/main.js" ]
|
||||
|
||||
@@ -17,6 +17,8 @@ import loops from './loops';
|
||||
import bankfeed from './bankfeed';
|
||||
import throttle from './throttle';
|
||||
import cloud from './cloud';
|
||||
import redis from './redis';
|
||||
import queue from './queue';
|
||||
|
||||
export const config = [
|
||||
app,
|
||||
@@ -38,4 +40,6 @@ export const config = [
|
||||
loops,
|
||||
bankfeed,
|
||||
throttle,
|
||||
redis,
|
||||
queue,
|
||||
];
|
||||
|
||||
6
packages/server/src/common/config/queue.ts
Normal file
6
packages/server/src/common/config/queue.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
import { registerAs } from '@nestjs/config';
|
||||
|
||||
export default registerAs('queue', () => ({
|
||||
host: process.env.QUEUE_HOST || 'localhost',
|
||||
port: parseInt(process.env.QUEUE_PORT, 10) || 6379,
|
||||
}));
|
||||
@@ -137,8 +137,8 @@ import { AppThrottleModule } from './AppThrottle.module';
|
||||
imports: [ConfigModule],
|
||||
useFactory: async (configService: ConfigService) => ({
|
||||
connection: {
|
||||
host: configService.get('QUEUE_HOST'),
|
||||
port: configService.get('QUEUE_PORT'),
|
||||
host: configService.get('queue.host'),
|
||||
port: configService.get('queue.port'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
@@ -158,8 +158,8 @@ import { AppThrottleModule } from './AppThrottle.module';
|
||||
imports: [ConfigModule],
|
||||
useFactory: (configService: ConfigService) => ({
|
||||
config: {
|
||||
host: configService.get('redis.host') || 'localhost',
|
||||
port: configService.get('redis.port') || 6379,
|
||||
host: configService.get('redis.host'),
|
||||
port: configService.get('redis.port'),
|
||||
},
|
||||
}),
|
||||
inject: [ConfigService],
|
||||
|
||||
@@ -1,27 +1,47 @@
|
||||
FROM node:18.16.0-alpine as build
|
||||
|
||||
USER root
|
||||
# Stage 1: Build
|
||||
FROM node:18.16.0-alpine AS builder
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
# Copy application dependency manifests to the container image.
|
||||
COPY . .
|
||||
# Install pnpm
|
||||
RUN npm install -g pnpm@8.10.2
|
||||
|
||||
# Install application dependencies
|
||||
RUN apk update
|
||||
RUN apk add python3 build-base chromium
|
||||
# Install build dependencies
|
||||
RUN apk add --no-cache python3 build-base chromium
|
||||
|
||||
# Set PYHTON env
|
||||
# Set Python environment
|
||||
ENV PYTHON=/usr/bin/python3
|
||||
|
||||
# Install pnpm packages dependencies
|
||||
RUN npm install -g pnpm
|
||||
# Copy package files for dependency installation
|
||||
COPY --chown=node:node package.json pnpm-lock.yaml pnpm-workspace.yaml lerna.json ./
|
||||
COPY --chown=node:node packages/webapp/package.json ./packages/webapp/
|
||||
COPY --chown=node:node shared/bigcapital-utils/package.json ./shared/bigcapital-utils/
|
||||
COPY --chown=node:node shared/pdf-templates/package.json ./shared/pdf-templates/
|
||||
COPY --chown=node:node shared/email-components/package.json ./shared/email-components/
|
||||
|
||||
# Install all dependencies (including devDependencies for build)
|
||||
RUN pnpm install
|
||||
|
||||
# Copy source code for webapp and dependencies
|
||||
COPY --chown=node:node ./packages/webapp ./packages/webapp
|
||||
COPY --chown=node:node ./shared/bigcapital-utils ./shared/bigcapital-utils
|
||||
COPY --chown=node:node ./shared/pdf-templates ./shared/pdf-templates
|
||||
COPY --chown=node:node ./shared/email-components ./shared/email-components
|
||||
|
||||
# Build webapp package
|
||||
RUN pnpm run build:webapp
|
||||
|
||||
FROM nginx
|
||||
# Stage 2: Nginx
|
||||
FROM nginx:alpine
|
||||
|
||||
COPY ./packages/webapp/nginx/sites/default.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/packages/webapp/dist /usr/share/nginx/html
|
||||
# Copy nginx configuration
|
||||
COPY --chown=root:root ./packages/webapp/nginx/sites/default.conf /etc/nginx/conf.d/default.conf
|
||||
|
||||
# Copy built webapp assets from builder stage
|
||||
COPY --from=builder --chown=nginx:nginx /app/packages/webapp/dist /usr/share/nginx/html
|
||||
|
||||
# Expose port
|
||||
EXPOSE 80
|
||||
|
||||
# Nginx runs as nginx user by default, which is good for security
|
||||
# No CMD needed as nginx base image already has it
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
import React, { useState } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { FormattedMessage as T, AppToaster } from '@/components';
|
||||
|
||||
import { withAlertStoreConnect } from '@/containers/Alert/withAlertStoreConnect';
|
||||
@@ -22,6 +22,7 @@ function AccountBulkActivateAlert({
|
||||
requestBulkActivateAccounts,
|
||||
}) {
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
@@ -38,9 +39,9 @@ function AccountBulkActivateAlert({
|
||||
message: intl.get('the_accounts_has_been_successfully_activated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
queryClient.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => { })
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useState } from 'react';
|
||||
import { FormattedMessage as T } from '@/components';
|
||||
import intl from 'react-intl-universal';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
import { AppToaster } from '@/components';
|
||||
|
||||
// import { withAccountsActions } from '@/containers/Accounts/withAccountsTableActions';
|
||||
@@ -22,8 +22,8 @@ function AccountBulkInactivateAlert({
|
||||
|
||||
closeAlert,
|
||||
}) {
|
||||
|
||||
const [isLoading, setLoading] = useState(false);
|
||||
const queryClient = useQueryClient();
|
||||
const selectedRowsCount = 0;
|
||||
|
||||
// Handle alert cancel.
|
||||
@@ -39,9 +39,9 @@ function AccountBulkInactivateAlert({
|
||||
message: intl.get('the_accounts_have_been_successfully_inactivated'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('accounts-table');
|
||||
queryClient.invalidateQueries('accounts-table');
|
||||
})
|
||||
.catch((errors) => { })
|
||||
.catch((errors) => {})
|
||||
.finally(() => {
|
||||
setLoading(false);
|
||||
closeAlert(name);
|
||||
|
||||
@@ -3,7 +3,7 @@ import React, { useCallback } from 'react';
|
||||
import intl from 'react-intl-universal';
|
||||
import { AppToaster, FormattedMessage as T } from '@/components';
|
||||
import { Intent, Alert } from '@blueprintjs/core';
|
||||
import { queryCache } from 'react-query';
|
||||
import { useQueryClient } from 'react-query';
|
||||
|
||||
import { useApproveEstimate } from '@/hooks/query';
|
||||
|
||||
@@ -25,6 +25,7 @@ function EstimateApproveAlert({
|
||||
// #withAlertActions
|
||||
closeAlert,
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const { mutateAsync: deliverEstimateMutate, isLoading } =
|
||||
useApproveEstimate();
|
||||
|
||||
@@ -40,7 +41,7 @@ function EstimateApproveAlert({
|
||||
message: intl.get('the_estimate_has_been_approved_successfully'),
|
||||
intent: Intent.SUCCESS,
|
||||
});
|
||||
queryCache.invalidateQueries('estimates-table');
|
||||
queryClient.invalidateQueries('estimates-table');
|
||||
})
|
||||
.catch((error) => {})
|
||||
.finally(() => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// @ts-nocheck
|
||||
import React from 'react';
|
||||
import { DialogContent } from '@/components';
|
||||
import { useQuery, queryCache } from 'react-query';
|
||||
import { useQuery, useQueryClient } from 'react-query';
|
||||
|
||||
import ReferenceNumberForm from '@/containers/JournalNumber/ReferenceNumberForm';
|
||||
|
||||
@@ -31,6 +31,7 @@ function BillNumberDialogContent({
|
||||
// #withBillsActions
|
||||
setBillNumberChanged,
|
||||
}) {
|
||||
const queryClient = useQueryClient();
|
||||
const fetchSettings = useQuery(['settings'], () => requestFetchOptions({}));
|
||||
|
||||
const handleSubmitForm = (values, { setSubmitting }) => {
|
||||
@@ -45,7 +46,7 @@ function BillNumberDialogContent({
|
||||
setBillNumberChanged(true);
|
||||
|
||||
setTimeout(() => {
|
||||
queryCache.invalidateQueries('settings');
|
||||
queryClient.invalidateQueries('settings');
|
||||
}, 250);
|
||||
})
|
||||
.catch(() => {
|
||||
|
||||
Reference in New Issue
Block a user