mirror of
https://github.com/bigcapitalhq/bigcapital.git
synced 2026-02-21 07:10:33 +00:00
Compare commits
3 Commits
v0.17.0
...
plaid-env-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9174203320 | ||
|
|
11cc4ffb0a | ||
|
|
c279b982e6 |
23
.env.example
23
.env.example
@@ -75,9 +75,30 @@ PLAID_ENV=sandbox
|
|||||||
# Your Plaid keys, which can be found in the Plaid Dashboard.
|
# Your Plaid keys, which can be found in the Plaid Dashboard.
|
||||||
# https://dashboard.plaid.com/account/keys
|
# https://dashboard.plaid.com/account/keys
|
||||||
PLAID_CLIENT_ID=
|
PLAID_CLIENT_ID=
|
||||||
PLAID_SECRET=
|
PLAID_SECRET_DEVELOPMENT=
|
||||||
|
PLAID_SECRET_SANDBOX=
|
||||||
|
|
||||||
PLAID_LINK_WEBHOOK=
|
PLAID_LINK_WEBHOOK=
|
||||||
|
|
||||||
|
# (Optional) Redirect URI settings section
|
||||||
|
# Only required for OAuth redirect URI testing (not common on desktop):
|
||||||
|
# Sandbox Mode:
|
||||||
|
# Set the PLAID_SANDBOX_REDIRECT_URI below to 'http://localhost:3001/oauth-link'.
|
||||||
|
# The OAuth redirect flow requires an endpoint on the developer's website
|
||||||
|
# that the bank website should redirect to. You will also need to configure
|
||||||
|
# this redirect URI for your client ID through the Plaid developer dashboard
|
||||||
|
# at https://dashboard.plaid.com/team/api.
|
||||||
|
# Development mode:
|
||||||
|
# When running in development mode, you must use an https:// url.
|
||||||
|
# You will need to configure this https:// redirect URI in the Plaid developer dashboard.
|
||||||
|
# Instructions to create a self-signed certificate for localhost can be found at
|
||||||
|
# https://github.com/plaid/pattern/blob/master/README.md#testing-oauth.
|
||||||
|
# If your system is not set up to run localhost with https://, you will be unable to test
|
||||||
|
# the OAuth in development and should leave the PLAID_DEVELOPMENT_REDIRECT_URI blank.
|
||||||
|
|
||||||
|
PLAID_SANDBOX_REDIRECT_URI=
|
||||||
|
PLAID_DEVELOPMENT_REDIRECT_URI=
|
||||||
|
|
||||||
# https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key
|
# https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key
|
||||||
LEMONSQUEEZY_API_KEY=
|
LEMONSQUEEZY_API_KEY=
|
||||||
LEMONSQUEEZY_STORE_ID=
|
LEMONSQUEEZY_STORE_ID=
|
||||||
|
|||||||
@@ -22,15 +22,11 @@ services:
|
|||||||
- server
|
- server
|
||||||
- webapp
|
- webapp
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
webapp:
|
webapp:
|
||||||
container_name: bigcapital-webapp
|
container_name: bigcapital-webapp
|
||||||
image: bigcapitalhq/webapp:latest
|
image: bigcapitalhq/webapp:latest
|
||||||
restart: on-failure
|
restart: on-failure
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
server:
|
server:
|
||||||
container_name: bigcapital-server
|
container_name: bigcapital-server
|
||||||
@@ -93,17 +89,14 @@ services:
|
|||||||
- GOTENBERG_URL=${GOTENBERG_URL}
|
- GOTENBERG_URL=${GOTENBERG_URL}
|
||||||
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_URL}
|
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_URL}
|
||||||
|
|
||||||
# Exchange Rate
|
|
||||||
- EXCHANGE_RATE_SERVICE=${EXCHANGE_RATE_SERVICE}
|
|
||||||
- OPEN_EXCHANGE_RATE_APP_ID-${OPEN_EXCHANGE_RATE_APP_ID}
|
|
||||||
|
|
||||||
# Bank Sync
|
# Bank Sync
|
||||||
- BANKING_CONNECT=${BANKING_CONNECT}
|
- BANKING_CONNECT=${BANKING_CONNECT}
|
||||||
|
|
||||||
# Plaid
|
# Plaid
|
||||||
- PLAID_ENV=${PLAID_ENV}
|
- PLAID_ENV=${PLAID_ENV}
|
||||||
- PLAID_CLIENT_ID=${PLAID_CLIENT_ID}
|
- PLAID_CLIENT_ID=${PLAID_CLIENT_ID}
|
||||||
- PLAID_SECRET=${PLAID_SECRET}
|
- PLAID_SECRET_DEVELOPMENT=${PLAID_SECRET_DEVELOPMENT}
|
||||||
|
- PLAID_SECRET_SANDBOX=${b8cf42b441e110451e2f69ad7e1e9f}
|
||||||
- PLAID_LINK_WEBHOOK=${PLAID_LINK_WEBHOOK}
|
- PLAID_LINK_WEBHOOK=${PLAID_LINK_WEBHOOK}
|
||||||
|
|
||||||
# Lemon Squeez
|
# Lemon Squeez
|
||||||
@@ -127,8 +120,6 @@ services:
|
|||||||
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
- S3_SECRET_ACCESS_KEY=${S3_SECRET_ACCESS_KEY}
|
||||||
- S3_ENDPOINT=${S3_ENDPOINT}
|
- S3_ENDPOINT=${S3_ENDPOINT}
|
||||||
- S3_BUCKET=${S3_BUCKET}
|
- S3_BUCKET=${S3_BUCKET}
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
database_migration:
|
database_migration:
|
||||||
container_name: bigcapital-database-migration
|
container_name: bigcapital-database-migration
|
||||||
@@ -146,8 +137,6 @@ services:
|
|||||||
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
- TENANT_DB_NAME_PERFIX=${TENANT_DB_NAME_PERFIX}
|
||||||
depends_on:
|
depends_on:
|
||||||
- mysql
|
- mysql
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
mysql:
|
mysql:
|
||||||
container_name: bigcapital-mysql
|
container_name: bigcapital-mysql
|
||||||
@@ -163,8 +152,6 @@ services:
|
|||||||
- mysql:/var/lib/mysql
|
- mysql:/var/lib/mysql
|
||||||
expose:
|
expose:
|
||||||
- '3306'
|
- '3306'
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
mongo:
|
mongo:
|
||||||
container_name: bigcapital-mongo
|
container_name: bigcapital-mongo
|
||||||
@@ -174,8 +161,6 @@ services:
|
|||||||
- '27017'
|
- '27017'
|
||||||
volumes:
|
volumes:
|
||||||
- mongo:/var/lib/mongodb
|
- mongo:/var/lib/mongodb
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
redis:
|
redis:
|
||||||
container_name: bigcapital-redis
|
container_name: bigcapital-redis
|
||||||
@@ -186,15 +171,11 @@ services:
|
|||||||
- '6379'
|
- '6379'
|
||||||
volumes:
|
volumes:
|
||||||
- redis:/data
|
- redis:/data
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
gotenberg:
|
gotenberg:
|
||||||
image: gotenberg/gotenberg:7
|
image: gotenberg/gotenberg:7
|
||||||
expose:
|
expose:
|
||||||
- '9000'
|
- '9000'
|
||||||
networks:
|
|
||||||
- bigcapital_network
|
|
||||||
|
|
||||||
# Volumes
|
# Volumes
|
||||||
volumes:
|
volumes:
|
||||||
@@ -209,8 +190,3 @@ volumes:
|
|||||||
redis:
|
redis:
|
||||||
name: bigcapital_prod_redis
|
name: bigcapital_prod_redis
|
||||||
driver: local
|
driver: local
|
||||||
|
|
||||||
# Networks
|
|
||||||
networks:
|
|
||||||
bigcapital_network:
|
|
||||||
driver: bridge
|
|
||||||
|
|||||||
@@ -4,16 +4,12 @@ import { Router, Response, NextFunction, Request } from 'express';
|
|||||||
import { body, param } from 'express-validator';
|
import { body, param } from 'express-validator';
|
||||||
import BaseController from '@/api/controllers/BaseController';
|
import BaseController from '@/api/controllers/BaseController';
|
||||||
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
|
import { AttachmentsApplication } from '@/services/Attachments/AttachmentsApplication';
|
||||||
import { AttachmentUploadPipeline } from '@/services/Attachments/S3UploadPipeline';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class AttachmentsController extends BaseController {
|
export class AttachmentsController extends BaseController {
|
||||||
@Inject()
|
@Inject()
|
||||||
private attachmentsApplication: AttachmentsApplication;
|
private attachmentsApplication: AttachmentsApplication;
|
||||||
|
|
||||||
@Inject()
|
|
||||||
private uploadPipelineService: AttachmentUploadPipeline;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Router constructor.
|
* Router constructor.
|
||||||
*/
|
*/
|
||||||
@@ -22,8 +18,7 @@ export class AttachmentsController extends BaseController {
|
|||||||
|
|
||||||
router.post(
|
router.post(
|
||||||
'/',
|
'/',
|
||||||
this.uploadPipelineService.validateS3Configured,
|
this.attachmentsApplication.uploadPipeline.single('file'),
|
||||||
this.uploadPipelineService.uploadPipeline().single('file'),
|
|
||||||
this.validateUploadedFileExistance,
|
this.validateUploadedFileExistance,
|
||||||
this.uploadAttachment.bind(this)
|
this.uploadAttachment.bind(this)
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -71,10 +71,6 @@ function getAllSystemTenants(knex) {
|
|||||||
return knex('tenants');
|
return knex('tenants');
|
||||||
}
|
}
|
||||||
|
|
||||||
function getAllInitializedSystemTenants(knex) {
|
|
||||||
return knex('tenants').whereNotNull('initializedAt');
|
|
||||||
}
|
|
||||||
|
|
||||||
// module.exports = {
|
// module.exports = {
|
||||||
// log,
|
// log,
|
||||||
// success,
|
// success,
|
||||||
@@ -187,7 +183,7 @@ commander
|
|||||||
.action(async (cmd) => {
|
.action(async (cmd) => {
|
||||||
try {
|
try {
|
||||||
const sysKnex = await initSystemKnex();
|
const sysKnex = await initSystemKnex();
|
||||||
const tenants = await getAllInitializedSystemTenants(sysKnex);
|
const tenants = await getAllSystemTenants(sysKnex);
|
||||||
const tenantsOrgsIds = tenants.map((tenant) => tenant.organizationId);
|
const tenantsOrgsIds = tenants.map((tenant) => tenant.organizationId);
|
||||||
|
|
||||||
if (cmd.tenant_id && tenantsOrgsIds.indexOf(cmd.tenant_id) === -1) {
|
if (cmd.tenant_id && tenantsOrgsIds.indexOf(cmd.tenant_id) === -1) {
|
||||||
@@ -224,6 +220,7 @@ commander
|
|||||||
const oper = migrateTenant(cmd.tenant_id);
|
const oper = migrateTenant(cmd.tenant_id);
|
||||||
migrateOpers.push(oper);
|
migrateOpers.push(oper);
|
||||||
}
|
}
|
||||||
|
|
||||||
Promise.all(migrateOpers).then(() => {
|
Promise.all(migrateOpers).then(() => {
|
||||||
success('All tenants are migrated.');
|
success('All tenants are migrated.');
|
||||||
});
|
});
|
||||||
@@ -283,3 +280,4 @@ commander
|
|||||||
exit(error);
|
exit(error);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -235,6 +235,6 @@ module.exports = {
|
|||||||
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
accessKeyId: process.env.S3_ACCESS_KEY_ID,
|
||||||
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
secretAccessKey: process.env.S3_SECRET_ACCESS_KEY,
|
||||||
endpoint: process.env.S3_ENDPOINT,
|
endpoint: process.env.S3_ENDPOINT,
|
||||||
bucket: process.env.S3_BUCKET || 'bigcapital-documents',
|
bucket: process.env.S3_BUCKET,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -2,9 +2,11 @@ import { Inject, Service } from 'typedi';
|
|||||||
import { UploadDocument } from './UploadDocument';
|
import { UploadDocument } from './UploadDocument';
|
||||||
import { DeleteAttachment } from './DeleteAttachment';
|
import { DeleteAttachment } from './DeleteAttachment';
|
||||||
import { GetAttachment } from './GetAttachment';
|
import { GetAttachment } from './GetAttachment';
|
||||||
|
import { AttachmentUploadPipeline } from './S3UploadPipeline';
|
||||||
import { LinkAttachment } from './LinkAttachment';
|
import { LinkAttachment } from './LinkAttachment';
|
||||||
import { UnlinkAttachment } from './UnlinkAttachment';
|
import { UnlinkAttachment } from './UnlinkAttachment';
|
||||||
import { getAttachmentPresignedUrl } from './GetAttachmentPresignedUrl';
|
import { getAttachmentPresignedUrl } from './GetAttachmentPresignedUrl';
|
||||||
|
import type { Multer } from 'multer';
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class AttachmentsApplication {
|
export class AttachmentsApplication {
|
||||||
@@ -17,6 +19,9 @@ export class AttachmentsApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getDocumentService: GetAttachment;
|
private getDocumentService: GetAttachment;
|
||||||
|
|
||||||
|
@Inject()
|
||||||
|
private uploadPipelineService: AttachmentUploadPipeline;
|
||||||
|
|
||||||
@Inject()
|
@Inject()
|
||||||
private linkDocumentService: LinkAttachment;
|
private linkDocumentService: LinkAttachment;
|
||||||
|
|
||||||
@@ -26,6 +31,14 @@ export class AttachmentsApplication {
|
|||||||
@Inject()
|
@Inject()
|
||||||
private getPresignedUrlService: getAttachmentPresignedUrl;
|
private getPresignedUrlService: getAttachmentPresignedUrl;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Express middleware for uploading attachments to an S3 bucket.
|
||||||
|
* @returns {Multer}
|
||||||
|
*/
|
||||||
|
get uploadPipeline(): Multer {
|
||||||
|
return this.uploadPipelineService.uploadPipeline();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Saves the metadata of uploaded document to S3 on database.
|
* Saves the metadata of uploaded document to S3 on database.
|
||||||
* @param {number} tenantId
|
* @param {number} tenantId
|
||||||
|
|||||||
@@ -1,38 +1,12 @@
|
|||||||
import multer from 'multer';
|
import multer from 'multer';
|
||||||
import type { Multer } from 'multer';
|
import type { Multer } from 'multer'
|
||||||
import multerS3 from 'multer-s3';
|
import multerS3 from 'multer-s3';
|
||||||
import { s3 } from '@/lib/S3/S3';
|
import { s3 } from '@/lib/S3/S3';
|
||||||
import { Service } from 'typedi';
|
import { Service } from 'typedi';
|
||||||
import config from '@/config';
|
import config from '@/config';
|
||||||
import { NextFunction, Request, Response } from 'express';
|
|
||||||
|
|
||||||
@Service()
|
@Service()
|
||||||
export class AttachmentUploadPipeline {
|
export class AttachmentUploadPipeline {
|
||||||
/**
|
|
||||||
* Middleware to ensure that S3 configuration is properly set before proceeding.
|
|
||||||
* This function checks if the necessary S3 configuration keys are present and throws an error if any are missing.
|
|
||||||
*
|
|
||||||
* @param req The HTTP request object.
|
|
||||||
* @param res The HTTP response object.
|
|
||||||
* @param next The callback to pass control to the next middleware function.
|
|
||||||
*/
|
|
||||||
public validateS3Configured(req: Request, res: Response, next: NextFunction) {
|
|
||||||
if (
|
|
||||||
!config.s3.region ||
|
|
||||||
!config.s3.accessKeyId ||
|
|
||||||
!config.s3.secretAccessKey
|
|
||||||
) {
|
|
||||||
const missingKeys = [];
|
|
||||||
if (!config.s3.region) missingKeys.push('region');
|
|
||||||
if (!config.s3.accessKeyId) missingKeys.push('accessKeyId');
|
|
||||||
if (!config.s3.secretAccessKey) missingKeys.push('secretAccessKey');
|
|
||||||
const missing = missingKeys.join(', ');
|
|
||||||
|
|
||||||
throw new Error(`S3 configuration error: Missing ${missing}`);
|
|
||||||
}
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Express middleware for uploading attachments to an S3 bucket.
|
* Express middleware for uploading attachments to an S3 bucket.
|
||||||
* It utilizes the multer middleware for handling multipart/form-data, specifically for file uploads.
|
* It utilizes the multer middleware for handling multipart/form-data, specifically for file uploads.
|
||||||
|
|||||||
@@ -42,12 +42,7 @@ export const transformPlaidTrxsToCashflowCreate = R.curry(
|
|||||||
): CreateUncategorizedTransactionDTO => {
|
): CreateUncategorizedTransactionDTO => {
|
||||||
return {
|
return {
|
||||||
date: plaidTranasction.date,
|
date: plaidTranasction.date,
|
||||||
|
amount: plaidTranasction.amount,
|
||||||
// Plaid: Positive values when money moves out of the account; negative values
|
|
||||||
// when money moves in. For example, debit card purchases are positive;
|
|
||||||
// credit card payments, direct deposits, and refunds are negative.
|
|
||||||
amount: -1 * plaidTranasction.amount,
|
|
||||||
|
|
||||||
description: plaidTranasction.name,
|
description: plaidTranasction.name,
|
||||||
payee: plaidTranasction.payment_meta?.payee,
|
payee: plaidTranasction.payment_meta?.payee,
|
||||||
currencyCode: plaidTranasction.iso_currency_code,
|
currencyCode: plaidTranasction.iso_currency_code,
|
||||||
|
|||||||
Reference in New Issue
Block a user