Compare commits

...

7 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
dfa63ece21 Merge pull request #921 from bigcapitalhq/20260131-145158-fd0c
fix(scripts): db migration dockerfile
2026-01-31 15:32:07 +02:00
Ahmed Bouhuolia
6e95bd7da1 fix(scripts): db migration dockerfile 2026-01-31 15:31:17 +02:00
Ahmed Bouhuolia
f51fffa5c7 Merge pull request #918 from bigcapitalhq/20260129-203653-75b0
feat(server): add bull ui board
2026-01-29 20:39:05 +02:00
Ahmed Bouhuolia
6193358cc3 feat(server): add bull ui board 2026-01-29 20:37:04 +02:00
Ahmed Bouhuolia
518abcd30d Merge pull request #917 from bigcapitalhq/20260128-195652-2287
fix: dockerfile build script
2026-01-28 23:42:24 +02:00
Ahmed Bouhuolia
7874b9f765 fix(ci): dockerfile build script 2026-01-28 23:40:32 +02:00
Ahmed Bouhuolia
02cc7e0c96 Merge pull request #916 from bigcapitalhq/20260128-181425-8b6a
fix(webapp): blueprintjs datetime version
2026-01-28 18:17:29 +02:00
27 changed files with 497 additions and 142 deletions

93
.dockerignore Normal file
View 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

View File

@@ -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}

View File

@@ -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 "node dist/cli.js system:migrate:latest && node dist/cli.js tenants:migrate:latest"

View File

@@ -1,100 +1,102 @@
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 database migration files (needed for running migrations)
COPY --from=builder --chown=nodejs:nodejs /app/packages/server/src/database ./packages/server/src/database
# 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" ]

View File

@@ -2,10 +2,23 @@
"$schema": "https://json.schemastore.org/nest-cli",
"collection": "@nestjs/schematics",
"sourceRoot": "src",
"entryFile": "main",
"compilerOptions": {
"deleteOutDir": true,
"assets": [
{ "include": "i18n/**/*", "watchAssets": true }
{ "include": "i18n/**/*", "watchAssets": true },
{ "include": "database/**/*", "watchAssets": true }
]
},
"projects": {
"cli": {
"type": "application",
"root": "src",
"entryFile": "cli",
"sourceRoot": "src",
"compilerOptions": {
"tsConfigPath": "tsconfig.json"
}
}
}
}

View File

@@ -40,6 +40,9 @@
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
"@liaoliaots/nestjs-redis": "^10.0.0",
"@nest-lab/throttler-storage-redis": "^1.1.0",
"@bull-board/api": "^5.22.0",
"@bull-board/express": "^5.22.0",
"@bull-board/nestjs": "^5.22.0",
"@nestjs/bull": "^10.2.1",
"@nestjs/bullmq": "^10.2.2",
"@nestjs/cache-manager": "^2.2.2",

View File

@@ -0,0 +1,8 @@
import { registerAs } from '@nestjs/config';
import { parseBoolean } from '@/utils/parse-boolean';
export default registerAs('bullBoard', () => ({
enabled: parseBoolean<boolean>(process.env.BULL_BOARD_ENABLED, false),
username: process.env.BULL_BOARD_USERNAME,
password: process.env.BULL_BOARD_PASSWORD,
}));

View File

@@ -17,6 +17,9 @@ import loops from './loops';
import bankfeed from './bankfeed';
import throttle from './throttle';
import cloud from './cloud';
import redis from './redis';
import queue from './queue';
import bullBoard from './bull-board';
export const config = [
app,
@@ -38,4 +41,7 @@ export const config = [
loops,
bankfeed,
throttle,
redis,
queue,
bullBoard,
];

View 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,
}));

View File

@@ -0,0 +1,59 @@
import { Request, Response, NextFunction } from 'express';
/**
* Creates Express middleware for the Bull Board UI:
* - When disabled: responds with 404.
* - When enabled and username/password are set: enforces HTTP Basic Auth (401 if invalid).
* - When enabled and credentials are not set: allows access (no auth).
*/
export function createBullBoardAuthMiddleware(
enabled: boolean,
username: string | undefined,
password: string | undefined,
): (req: Request, res: Response, next: NextFunction) => void {
return (req: Request, res: Response, next: NextFunction) => {
if (!enabled) {
res.status(404).send('Not Found');
return;
}
if (!username || !password) {
return next();
}
const authHeader = req.headers.authorization;
if (!authHeader || !authHeader.startsWith('Basic ')) {
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
res.status(401).send('Authentication required');
return;
}
const base64Credentials = authHeader.slice(6);
let decoded: string;
try {
decoded = Buffer.from(base64Credentials, 'base64').toString('utf8');
} catch {
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
res.status(401).send('Invalid credentials');
return;
}
const colonIndex = decoded.indexOf(':');
if (colonIndex === -1) {
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
res.status(401).send('Invalid credentials');
return;
}
const reqUsername = decoded.slice(0, colonIndex);
const reqPassword = decoded.slice(colonIndex + 1);
if (reqUsername !== username || reqPassword !== password) {
res.setHeader('WWW-Authenticate', 'Basic realm="Bull Board"');
res.status(401).send('Invalid credentials');
return;
}
next();
};
}

View File

@@ -12,6 +12,9 @@ import {
I18nModule,
QueryResolver,
} from 'nestjs-i18n';
import { BullBoardModule } from '@bull-board/nestjs';
import { ExpressAdapter } from '@bull-board/express';
import { createBullBoardAuthMiddleware } from '@/middleware/bull-board-auth.middleware';
import { BullModule } from '@nestjs/bullmq';
import { ScheduleModule } from '@nestjs/schedule';
import { PassportModule } from '@nestjs/passport';
@@ -137,12 +140,30 @@ 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],
}),
BullBoardModule.forRootAsync({
imports: [ConfigModule],
useFactory: (configService: ConfigService) => {
const enabled = configService.get<boolean>('bullBoard.enabled');
const username = configService.get<string>('bullBoard.username');
const password = configService.get<string>('bullBoard.password');
return {
route: '/queues',
adapter: ExpressAdapter,
middleware: createBullBoardAuthMiddleware(
enabled,
username,
password,
),
};
},
inject: [ConfigService],
}),
ClsModule.forRoot({
global: true,
middleware: {
@@ -158,8 +179,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],

View File

@@ -17,6 +17,8 @@ import { PassportModule } from '@nestjs/passport';
import { APP_GUARD } from '@nestjs/core';
import { JwtAuthGuard } from './guards/jwt.guard';
import { AuthMailSubscriber } from './subscribers/AuthMail.subscriber';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { BullModule } from '@nestjs/bullmq';
import {
SendResetPasswordMailQueue,
@@ -63,6 +65,14 @@ const models = [
TenancyModule,
BullModule.registerQueue({ name: SendResetPasswordMailQueue }),
BullModule.registerQueue({ name: SendSignupVerificationMailQueue }),
BullBoardModule.forFeature({
name: SendResetPasswordMailQueue,
adapter: BullMQAdapter,
}),
BullBoardModule.forFeature({
name: SendSignupVerificationMailQueue,
adapter: BullMQAdapter,
}),
],
exports: [...models],
providers: [
@@ -98,4 +108,4 @@ const models = [
AuthMailSubscriber,
],
})
export class AuthModule { }
export class AuthModule {}

View File

@@ -1,3 +1,5 @@
import { BullBoardModule } from '@bull-board/nestjs';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { BullModule } from '@nestjs/bullmq';
import { Module } from '@nestjs/common';
import { SocketModule } from '../Socket/Socket.module';
@@ -33,6 +35,10 @@ const models = [RegisterTenancyModel(PlaidItem)];
BankingCategorizeModule,
BankingTransactionsModule,
BullModule.registerQueue({ name: UpdateBankingPlaidTransitionsQueueJob }),
BullBoardModule.forFeature({
name: UpdateBankingPlaidTransitionsQueueJob,
adapter: BullMQAdapter,
}),
...models,
],
providers: [
@@ -51,4 +57,4 @@ const models = [RegisterTenancyModel(PlaidItem)];
exports: [...models],
controllers: [BankingPlaidController, BankingPlaidWebhooksController],
})
export class BankingPlaidModule { }
export class BankingPlaidModule {}

View File

@@ -10,6 +10,8 @@ import { BankingRecognizedTransactionsController } from './BankingRecognizedTran
import { RecognizedTransactionsApplication } from './RecognizedTransactions.application';
import { GetRecognizedTransactionsService } from './GetRecongizedTransactions';
import { GetRecognizedTransactionService } from './queries/GetRecognizedTransaction.service';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { BullModule } from '@nestjs/bullmq';
import { RecognizeUncategorizedTransactionsQueue } from './_types';
import { RegonizeTransactionsPrcessor } from './jobs/RecognizeTransactionsJob';
@@ -25,6 +27,10 @@ const models = [RegisterTenancyModel(RecognizedBankTransaction)];
BullModule.registerQueue({
name: RecognizeUncategorizedTransactionsQueue,
}),
BullBoardModule.forFeature({
name: RecognizeUncategorizedTransactionsQueue,
adapter: BullMQAdapter,
}),
...models,
],
providers: [

View File

@@ -16,6 +16,8 @@ import {
ComputeItemCostQueue,
WriteInventoryTransactionsGLEntriesQueue,
} from './types/InventoryCost.types';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { BullModule } from '@nestjs/bullmq';
import { InventoryAverageCostMethodService } from './commands/InventoryAverageCostMethod.service';
import { InventoryItemCostService } from './commands/InventoryCosts.service';
@@ -39,6 +41,14 @@ const models = [
BullModule.registerQueue({
name: WriteInventoryTransactionsGLEntriesQueue,
}),
BullBoardModule.forFeature({
name: ComputeItemCostQueue,
adapter: BullMQAdapter,
}),
BullBoardModule.forFeature({
name: WriteInventoryTransactionsGLEntriesQueue,
adapter: BullMQAdapter,
}),
forwardRef(() => SaleInvoicesModule),
ImportModule,
],
@@ -56,7 +66,7 @@ const models = [
InventoryItemCostService,
InventoryItemOpeningAvgCostService,
InventoryCostSubscriber,
GetItemsInventoryValuationListService
GetItemsInventoryValuationListService,
],
exports: [
...models,
@@ -64,6 +74,6 @@ const models = [
InventoryItemCostService,
InventoryComputeCostService,
],
controllers: [InventoryCostController]
controllers: [InventoryCostController],
})
export class InventoryCostModule {}

View File

@@ -3,6 +3,8 @@ import { GetCurrentOrganizationService } from './queries/GetCurrentOrganization.
import { BuildOrganizationService } from './commands/BuildOrganization.service';
import { UpdateOrganizationService } from './commands/UpdateOrganization.service';
import { OrganizationController } from './Organization.controller';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullMQAdapter } from '@bull-board/api/bullMQAdapter';
import { BullModule } from '@nestjs/bullmq';
import { OrganizationBuildQueue } from './Organization.types';
import { OrganizationBuildProcessor } from './processors/OrganizationBuild.processor';
@@ -25,10 +27,14 @@ import { GetBuildOrganizationBuildJob } from './commands/GetBuildOrganizationJob
OrganizationBaseCurrencyLocking,
SyncSystemUserToTenantService,
SyncSystemUserToTenantSubscriber,
GetBuildOrganizationBuildJob
GetBuildOrganizationBuildJob,
],
imports: [
BullModule.registerQueue({ name: OrganizationBuildQueue }),
BullBoardModule.forFeature({
name: OrganizationBuildQueue,
adapter: BullMQAdapter,
}),
TenantDBManagerModule,
],
controllers: [OrganizationController],

View File

@@ -1,4 +1,6 @@
import { Module } from '@nestjs/common';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullAdapter } from '@bull-board/api/bullAdapter';
import { BullModule } from '@nestjs/bull';
import { PaymentReceivesController } from './PaymentsReceived.controller';
import { PaymentReceivesApplication } from './PaymentReceived.application';
@@ -95,6 +97,10 @@ import { ValidateBulkDeletePaymentReceivedService } from './ValidateBulkDeletePa
DynamicListModule,
MailModule,
BullModule.registerQueue({ name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE }),
BullBoardModule.forFeature({
name: SEND_PAYMENT_RECEIVED_MAIL_QUEUE,
adapter: BullAdapter,
}),
],
})
export class PaymentsReceivedModule {}

View File

@@ -1,4 +1,6 @@
import { Module } from '@nestjs/common';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullAdapter } from '@bull-board/api/bullAdapter';
import { BullModule } from '@nestjs/bull';
import { TenancyContext } from '../Tenancy/TenancyContext.service';
import { TenancyDatabaseModule } from '../Tenancy/TenancyDB/TenancyDB.module';
@@ -54,6 +56,10 @@ import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.pr
TemplateInjectableModule,
PdfTemplatesModule,
BullModule.registerQueue({ name: SendSaleEstimateMailQueue }),
BullBoardModule.forFeature({
name: SendSaleEstimateMailQueue,
adapter: BullAdapter,
}),
],
controllers: [SaleEstimatesController],
providers: [
@@ -99,4 +105,4 @@ import { SendSaleEstimateMailProcess } from './processes/SendSaleEstimateMail.pr
GetSaleEstimateMailTemplateService,
],
})
export class SaleEstimatesModule { }
export class SaleEstimatesModule {}

View File

@@ -45,6 +45,8 @@ import { SendSaleInvoiceMailCommon } from './commands/SendInvoiceInvoiceMailComm
import { DynamicListModule } from '../DynamicListing/DynamicList.module';
import { MailNotificationModule } from '../MailNotification/MailNotification.module';
import { SendSaleInvoiceMailProcessor } from './processors/SendSaleInvoiceMail.processor';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullAdapter } from '@bull-board/api/bullAdapter';
import { BullModule } from '@nestjs/bull';
import { SendSaleInvoiceQueue } from './constants';
import { InvoicePaymentIntegrationSubscriber } from './subscribers/InvoicePaymentIntegrationSubscriber';
@@ -81,6 +83,10 @@ import { ValidateBulkDeleteSaleInvoicesService } from './ValidateBulkDeleteSaleI
forwardRef(() => PaymentLinksModule),
DynamicListModule,
BullModule.registerQueue({ name: SendSaleInvoiceQueue }),
BullBoardModule.forFeature({
name: SendSaleInvoiceQueue,
adapter: BullAdapter,
}),
],
controllers: [SaleInvoicesController],
providers: [
@@ -139,4 +145,4 @@ import { ValidateBulkDeleteSaleInvoicesService } from './ValidateBulkDeleteSaleI
SaleInvoicesImportable,
],
})
export class SaleInvoicesModule { }
export class SaleInvoicesModule {}

View File

@@ -1,4 +1,6 @@
import { Module } from '@nestjs/common';
import { BullBoardModule } from '@bull-board/nestjs';
import { BullAdapter } from '@bull-board/api/bullAdapter';
import { BullModule } from '@nestjs/bull';
import { SaleReceiptApplication } from './SaleReceiptApplication.service';
import { CreateSaleReceipt } from './commands/CreateSaleReceipt.service';
@@ -62,6 +64,10 @@ import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleR
MailModule,
MailNotificationModule,
BullModule.registerQueue({ name: SendSaleReceiptMailQueue }),
BullBoardModule.forFeature({
name: SendSaleReceiptMailQueue,
adapter: BullAdapter,
}),
],
providers: [
TenancyContext,
@@ -95,4 +101,4 @@ import { ValidateBulkDeleteSaleReceiptsService } from './ValidateBulkDeleteSaleR
ValidateBulkDeleteSaleReceiptsService,
],
})
export class SaleReceiptsModule { }
export class SaleReceiptsModule {}

View File

@@ -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

View File

@@ -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);

View File

@@ -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);

View File

@@ -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(() => {

View File

@@ -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(() => {

64
pnpm-lock.yaml generated
View File

@@ -54,6 +54,15 @@ importers:
'@bigcapital/utils':
specifier: '*'
version: link:../../shared/bigcapital-utils
'@bull-board/api':
specifier: ^5.22.0
version: 5.23.0(@bull-board/ui@5.23.0)
'@bull-board/express':
specifier: ^5.22.0
version: 5.23.0
'@bull-board/nestjs':
specifier: ^5.22.0
version: 5.23.0(@bull-board/api@5.23.0)(@bull-board/express@5.23.0)(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@casl/ability':
specifier: ^5.4.3
version: 5.4.4
@@ -4855,6 +4864,51 @@ packages:
tslib: 2.5.3
dev: false
/@bull-board/api@5.23.0(@bull-board/ui@5.23.0):
resolution: {integrity: sha512-ZZGsWJ+XBG49GAlNgAL9tTEV6Ms7gMkQnZDbzwUhjGChCKWy62RWuPoZSefNXau9QH9+QzlzHRUeFvt4xr5wiw==}
peerDependencies:
'@bull-board/ui': 5.23.0
dependencies:
'@bull-board/ui': 5.23.0
redis-info: 3.1.0
dev: false
/@bull-board/express@5.23.0:
resolution: {integrity: sha512-t/mHzJMlZBtSKD8v81kbZoexOmtQxKVnHZfHJ0um5vrkHNJJuzKuwbR+n9nf1u89AGdyXoWxqDhBDslxv3zrrQ==}
dependencies:
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
'@bull-board/ui': 5.23.0
ejs: 3.1.10
express: 4.21.1
transitivePeerDependencies:
- supports-color
dev: false
/@bull-board/nestjs@5.23.0(@bull-board/api@5.23.0)(@bull-board/express@5.23.0)(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)(reflect-metadata@0.2.2)(rxjs@7.8.1):
resolution: {integrity: sha512-eRJCz6PqKzrlZfd7akc+6/1eXwNO7Fp468TmSo8avlPcKhStZH3fUmCF6xxfRQpm9kn/eDOp+H18CTnSafmwmw==}
peerDependencies:
'@bull-board/api': ^5.23.0
'@bull-board/express': ^5.23.0
'@nestjs/common': ^9.0.0 || ^10.0.0
'@nestjs/core': ^9.0.0 || ^10.0.0
reflect-metadata: ^0.1.13 || ^0.2.0
rxjs: ^7.8.1
dependencies:
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
'@bull-board/express': 5.23.0
'@nestjs/bull-shared': 10.2.2(@nestjs/common@10.4.7)(@nestjs/core@10.4.7)
'@nestjs/common': 10.4.7(class-transformer@0.5.1)(class-validator@0.14.1)(reflect-metadata@0.2.2)(rxjs@7.8.1)
'@nestjs/core': 10.4.7(@nestjs/common@10.4.7)(@nestjs/platform-express@10.4.7)(@nestjs/websockets@10.4.20)(reflect-metadata@0.2.2)(rxjs@7.8.1)
reflect-metadata: 0.2.2
rxjs: 7.8.1
dev: false
/@bull-board/ui@5.23.0:
resolution: {integrity: sha512-iI/Ssl8T5ZEn9s899Qz67m92M6RU8thf/aqD7cUHB2yHmkCjqbw7s7NaODTsyArAsnyu7DGJMWm7EhbfFXDNgQ==}
dependencies:
'@bull-board/api': 5.23.0(@bull-board/ui@5.23.0)
dev: false
/@casl/ability@5.4.4:
resolution: {integrity: sha512-7+GOnMUq6q4fqtDDesymBXTS9LSDVezYhFiSJ8Rn3f0aQLeRm7qHn66KWbej4niCOvm0XzNj9jzpkK0yz6hUww==}
dependencies:
@@ -15466,7 +15520,6 @@ packages:
hasBin: true
dependencies:
jake: 10.9.1
dev: true
/electron-to-chromium@1.4.782:
resolution: {integrity: sha512-JUfU61e8tr+i5Y1FKXcKs+Xe+rJ+CEqm4cgv1kMihPE2EvYHmYyVr3Im/+1+Z5B29Be2EEGCZCwAc6Tazdl1Yg==}
@@ -16741,7 +16794,6 @@ packages:
resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==}
dependencies:
minimatch: 5.1.6
dev: true
/fill-range@7.1.1:
resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==}
@@ -18481,7 +18533,6 @@ packages:
chalk: 4.1.2
filelist: 1.0.4
minimatch: 3.1.2
dev: true
/javascript-natural-sort@0.7.1:
resolution: {integrity: sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==}
@@ -20042,7 +20093,6 @@ packages:
engines: {node: '>=10'}
dependencies:
brace-expansion: 2.0.1
dev: true
/minimatch@8.0.4:
resolution: {integrity: sha512-W0Wvr9HyFXZRGIDgCicunpQ299OKXs9RgZfaukz4qAW/pJhcpUfupc9c+OObPOFueNy8VSrZgEmDtk6Kh4WzDA==}
@@ -23172,6 +23222,12 @@ packages:
engines: {node: '>=4'}
dev: false
/redis-info@3.1.0:
resolution: {integrity: sha512-ER4L9Sh/vm63DkIE0bkSjxluQlioBiBgf5w1UuldaW/3vPcecdljVDisZhmnCMvsxHNiARTTDDHGg9cGwTfrKg==}
dependencies:
lodash: 4.17.21
dev: false
/redis-parser@3.0.0:
resolution: {integrity: sha512-DJnGAeenTdpMEH6uAJRK/uiyEIH9WVsUmoLwzudwGJUwZPp80PDBWPHXSAGNPwNvIXAbe7MSUB1zQFugFml66A==}
engines: {node: '>=4'}

View File

@@ -2,7 +2,6 @@
# Initialize the essential variables.
BRANCH=main
CURRENT=$PWD
BIGCAPITAL_INSTALL_DIR=$PWD/bigcapital-$(echo $BRANCH | sed -r 's@(\/|" "|\.)@-@g')
BIGCAPITAL_CLONE_TEMP_DIR=$(mktemp -d)
CPU_ARCH=$(uname -m)
@@ -20,8 +19,6 @@ else
COMPOSE_CMD="docker compose"
fi
REPO=https://github.com/bigcapitalhq/bigcapital
# Prints the Bigcapital logo once running the script.
function print_logo() {
clear
@@ -142,8 +139,8 @@ function askForAction() {
}
function install() {
echo "Installing Bigcaoital.........."
echo "installing is going to take few mintues..."
echo "Installing Bigcapital.........."
echo "installing is going to take few minutes..."
download
setup_env
}
@@ -203,10 +200,11 @@ function startServices() {
done
printf "\r\033[K"
echo " API server started successfully ✅"
source "${DOCKER_ENV_PATH}"
ACCESS_URL=$(grep -E '^BASE_URL=' "$DOCKER_ENV_PATH" 2>/dev/null | cut -d= -f2-)
[ -z "$ACCESS_URL" ] && ACCESS_URL="http://localhost"
echo " Bigcapital server started successfully ✅"
echo ""
echo " You can access the application at $WEB_URL"
echo " You can access the application at $ACCESS_URL"
echo ""
}
@@ -223,20 +221,19 @@ function restartServices() {
}
function viewLogs(){
ARG_SERVICE_NAME=$2
echo
echo "Select a Service you want to view the logs for:"
echo " 1) Webapp"
echo " 2) API"
echo " 3) Migration"
echo " 4) Nginx Proxy"
echo " 4) Envoy Proxy"
echo " 5) MariaDB"
echo " 0) Back to Main Menu"
echo
read -p "Service: " DOCKER_SERVICE_NAME
until (( DOCKER_SERVICE_NAME >= 0 && DOCKER_SERVICE_NAME <= 5 )); do
echo "Invalid selection. Please enter a number between 1 and 11."
echo "Invalid selection. Please enter a number between 0 and 5."
read -p "Service: " DOCKER_SERVICE_NAME
done
@@ -248,7 +245,7 @@ function viewLogs(){
1) viewSpecificLogs "webapp";;
2) viewSpecificLogs "server";;
3) viewSpecificLogs "database_migration";;
4) viewSpecificLogs "nginx";;
4) viewSpecificLogs "proxy";;
5) viewSpecificLogs "mysql";;
0) askForAction;;
*) echo "INVALID SERVICE NAME SUPPLIED";;