Compare commits
23 Commits
auto-subsc
...
backup-scr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
341bcbea7d | ||
|
|
b7214044bb | ||
|
|
93cb3615c3 | ||
|
|
7abfa6a162 | ||
|
|
1372a1f0a8 | ||
|
|
484024ec28 | ||
|
|
46639c7b86 | ||
|
|
d10d1654c1 | ||
|
|
2f06070ecb | ||
|
|
deefdb9bfd | ||
|
|
3cc62d80de | ||
|
|
4962c5d4d3 | ||
|
|
571a332658 | ||
|
|
b44c318a5d | ||
|
|
bd9717f4dc | ||
|
|
f48aea8e5a | ||
|
|
0ac3a5dea9 | ||
|
|
56b40ad4cb | ||
|
|
9b6f934990 | ||
|
|
80e3522f8a | ||
|
|
7975643765 | ||
|
|
2ac7f86bdb | ||
|
|
60248ec3f6 |
@@ -105,6 +105,24 @@
|
||||
"contributions": [
|
||||
"bug"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "benpsnyder",
|
||||
"name": "Ben Snyder",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/707567?v=4",
|
||||
"profile": "https://snyder.tech",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "cloudsbird",
|
||||
"name": "Vederis Leunardus",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/13505006?v=4",
|
||||
"profile": "http://vederis.id",
|
||||
"contributions": [
|
||||
"code"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
@@ -96,7 +96,6 @@ PLAID_LINK_WEBHOOK=
|
||||
PLAID_SANDBOX_REDIRECT_URI=
|
||||
PLAID_DEVELOPMENT_REDIRECT_URI=
|
||||
|
||||
|
||||
# https://docs.lemonsqueezy.com/guides/developer-guide/getting-started#create-an-api-key
|
||||
LEMONSQUEEZY_API_KEY=
|
||||
LEMONSQUEEZY_STORE_ID=
|
||||
|
||||
89
.github/workflows/build-deploy-container.yml
vendored
89
.github/workflows/build-deploy-container.yml
vendored
@@ -12,20 +12,37 @@ env:
|
||||
|
||||
jobs:
|
||||
build-publish-webapp:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
name: Build and deploy webapp container
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GH_TOKEN }}
|
||||
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
@@ -35,14 +52,29 @@ jobs:
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: .
|
||||
file: ./packages/webapp/Dockerfile
|
||||
push: true
|
||||
tags: ghcr.io/bigcapitalhq/webapp:latest
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
context: .
|
||||
file: ./packages/webapp/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: ghcr.io/bigcapitalhq/webapp:latest
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-main-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published webapp container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
@@ -53,12 +85,23 @@ jobs:
|
||||
name: Build and deploy server container
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Prepare
|
||||
run: |
|
||||
platform=${{ matrix.platform }}
|
||||
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
# Login to Container registry.
|
||||
- name: Log in to the Container registry
|
||||
uses: docker/login-action@v1
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ${{ env.REGISTRY }}
|
||||
username: ${{ github.actor }}
|
||||
@@ -66,14 +109,30 @@ jobs:
|
||||
|
||||
# Builds and push the Docker image.
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v2
|
||||
uses: docker/build-push-action@v5
|
||||
id: build
|
||||
with:
|
||||
context: ./
|
||||
file: ./packages/server/Dockerfile
|
||||
platforms: ${{ matrix.platform }}
|
||||
push: true
|
||||
tags: ghcr.io/bigcapitalhq/server:latest
|
||||
labels: ${{ steps.meta.outputs.labels }}
|
||||
|
||||
- name: Export digest
|
||||
run: |
|
||||
mkdir -p /tmp/digests
|
||||
digest="${{ steps.build.outputs.digest }}"
|
||||
touch "/tmp/digests/${digest#sha256:}"
|
||||
|
||||
- name: Upload digest
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: digests-main-${{ env.PLATFORM_PAIR }}
|
||||
path: /tmp/digests/*
|
||||
if-no-files-found: error
|
||||
retention-days: 1
|
||||
|
||||
# Send notification to Slack channel.
|
||||
- name: Slack Notification built and published server container successfully.
|
||||
uses: rtCamp/action-slack-notify@v2
|
||||
|
||||
4
.github/workflows/e2e.yml
vendored
4
.github/workflows/e2e.yml
vendored
@@ -8,14 +8,14 @@ on:
|
||||
- '**.ts'
|
||||
- '**.tsx'
|
||||
- '**/tsconfig.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
pull_request:
|
||||
paths:
|
||||
- '**.ts'
|
||||
- '**.tsx'
|
||||
- '**/tsconfig.json'
|
||||
- 'yarn.lock'
|
||||
- 'pnpm-lock.yaml'
|
||||
- '.github/workflows/e2e.yml'
|
||||
|
||||
defaults:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/env sh
|
||||
. "$(dirname -- "$0")/_/husky.sh"
|
||||
|
||||
yarn commitlint --edit
|
||||
pnpx commitlint --edit
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
<img src="https://img.shields.io/twitter/follow/bigcapitalhq?style=social" alt="twitter" />
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://app.bigcapital.ly">Bigcapital Cloud</a>
|
||||
</p>
|
||||
</p>
|
||||
|
||||
# What's Bigcapital?
|
||||
@@ -118,6 +122,8 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/ANasouf"><img src="https://avatars.githubusercontent.com/u/19536487?v=4?s=100" width="100px;" alt="ANasouf"/><br /><sub><b>ANasouf</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=ANasouf" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://ragnarlaud.dev"><img src="https://avatars.githubusercontent.com/u/3042904?v=4?s=100" width="100px;" alt="Ragnar Laud"/><br /><sub><b>Ragnar Laud</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Axprnio" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://github.com/asenawritescode"><img src="https://avatars.githubusercontent.com/u/67445192?v=4?s=100" width="100px;" alt="Asena"/><br /><sub><b>Asena</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/issues?q=author%3Aasenawritescode" title="Bug reports">🐛</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="https://snyder.tech"><img src="https://avatars.githubusercontent.com/u/707567?v=4?s=100" width="100px;" alt="Ben Snyder"/><br /><sub><b>Ben Snyder</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=benpsnyder" title="Code">💻</a></td>
|
||||
<td align="center" valign="top" width="14.28%"><a href="http://vederis.id"><img src="https://avatars.githubusercontent.com/u/13505006?v=4?s=100" width="100px;" alt="Vederis Leunardus"/><br /><sub><b>Vederis Leunardus</b></sub></a><br /><a href="https://github.com/bigcapitalhq/bigcapital/commits?author=cloudsbird" title="Code">💻</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
@@ -21,16 +21,12 @@ services:
|
||||
depends_on:
|
||||
- server
|
||||
- webapp
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
|
||||
webapp:
|
||||
container_name: bigcapital-webapp
|
||||
image: ghcr.io/bigcapitalhq/webapp:latest
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
|
||||
server:
|
||||
container_name: bigcapital-server
|
||||
@@ -45,9 +41,7 @@ services:
|
||||
- mysql
|
||||
- mongo
|
||||
- redis
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
environment:
|
||||
# Mail
|
||||
- MAIL_HOST=${MAIL_HOST}
|
||||
@@ -92,6 +86,22 @@ services:
|
||||
- GOTENBERG_URL=${GOTENBERG_URL}
|
||||
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_URL}
|
||||
|
||||
# Bank Sync
|
||||
- BANKING_CONNECT=${BANKING_CONNECT}
|
||||
|
||||
# Plaid
|
||||
- PLAID_ENV=${PLAID_ENV}
|
||||
- PLAID_CLIENT_ID=${PLAID_CLIENT_ID}
|
||||
- PLAID_SECRET_DEVELOPMENT=${PLAID_SECRET_DEVELOPMENT}
|
||||
- PLAID_SECRET_SANDBOX=${b8cf42b441e110451e2f69ad7e1e9f}
|
||||
- PLAID_LINK_WEBHOOK=${PLAID_LINK_WEBHOOK}
|
||||
|
||||
# Lemon Squeez
|
||||
- LEMONSQUEEZY_API_KEY=${LEMONSQUEEZY_API_KEY}
|
||||
- LEMONSQUEEZY_STORE_ID=${LEMONSQUEEZY_STORE_ID}
|
||||
- LEMONSQUEEZY_WEBHOOK_SECRET=${LEMONSQUEEZY_WEBHOOK_SECRET}
|
||||
- HOSTED_ON_BIGCAPITAL_CLOUD=${HOSTED_ON_BIGCAPITAL_CLOUD}
|
||||
|
||||
database_migration:
|
||||
container_name: bigcapital-database-migration
|
||||
build:
|
||||
@@ -111,9 +121,7 @@ services:
|
||||
|
||||
mysql:
|
||||
container_name: bigcapital-mysql
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build:
|
||||
context: ./docker/mariadb
|
||||
environment:
|
||||
@@ -128,9 +136,7 @@ services:
|
||||
|
||||
mongo:
|
||||
container_name: bigcapital-mongo
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build: ./docker/mongo
|
||||
expose:
|
||||
- '27017'
|
||||
@@ -139,9 +145,7 @@ services:
|
||||
|
||||
redis:
|
||||
container_name: bigcapital-redis
|
||||
deploy:
|
||||
restart_policy:
|
||||
condition: unless-stopped
|
||||
restart: on-failure
|
||||
build:
|
||||
context: ./docker/redis
|
||||
expose:
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"$schema": "node_modules/lerna/schemas/lerna-schema.json",
|
||||
"version": "independent",
|
||||
"npmClient": "pnpm",
|
||||
"useWorkspaces": true,
|
||||
"packages": ["packages/*"]
|
||||
}
|
||||
"packages": [
|
||||
"packages/*"
|
||||
]
|
||||
}
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"@faker-js/faker": "^8.0.2",
|
||||
"@playwright/test": "^1.32.3",
|
||||
"husky": "^8.0.3",
|
||||
"lerna": "^6.4.1"
|
||||
"lerna": "^8.1.2",
|
||||
"pnpm": "^9.0.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": "16.x || 17.x || 18.x"
|
||||
|
||||
4
packages/server/.gitignore
vendored
4
packages/server/.gitignore
vendored
@@ -3,4 +3,6 @@
|
||||
stdout.log
|
||||
/dist
|
||||
/build
|
||||
/public/imports
|
||||
/public/imports
|
||||
|
||||
dist
|
||||
17747
packages/server/package-lock.json
generated
17747
packages/server/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -22,6 +22,7 @@
|
||||
"dependencies": {
|
||||
"@casl/ability": "^5.4.3",
|
||||
"@hapi/boom": "^7.4.3",
|
||||
"@lemonsqueezy/lemonsqueezy.js": "^2.2.0",
|
||||
"@types/i18n": "^0.8.7",
|
||||
"@types/knex": "^0.16.1",
|
||||
"@types/mathjs": "^6.0.12",
|
||||
@@ -89,17 +90,17 @@
|
||||
"objection-filter": "^4.0.1",
|
||||
"objection-soft-delete": "^1.0.7",
|
||||
"objection-unique": "^1.2.2",
|
||||
"plaid": "^10.3.0",
|
||||
"pluralize": "^8.0.0",
|
||||
"pug": "^3.0.2",
|
||||
"puppeteer": "^10.2.0",
|
||||
"plaid": "^10.3.0",
|
||||
"qim": "0.0.52",
|
||||
"ramda": "^0.27.1",
|
||||
"rate-limiter-flexible": "^2.1.14",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rtl-detect": "^1.0.4",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"socket.io": "^4.7.4",
|
||||
"source-map-loader": "^4.0.1",
|
||||
"tmp-promise": "^3.0.3",
|
||||
"ts-transformer-keys": "^0.4.2",
|
||||
"tsyringe": "^4.3.0",
|
||||
|
||||
@@ -8,10 +8,10 @@ export default class DashboardMetaController {
|
||||
dashboardService: DashboardService;
|
||||
|
||||
/**
|
||||
*
|
||||
* Constructor router.
|
||||
* @returns
|
||||
*/
|
||||
router() {
|
||||
public router() {
|
||||
const router = Router();
|
||||
|
||||
router.get('/boot', this.getDashboardBoot);
|
||||
@@ -25,7 +25,7 @@ export default class DashboardMetaController {
|
||||
* @param {Response} res -
|
||||
* @param {NextFunction} next -
|
||||
*/
|
||||
getDashboardBoot = async (
|
||||
private getDashboardBoot = async (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import Multer from 'multer';
|
||||
import { ServiceError } from '@/exceptions';
|
||||
import { getImportsStoragePath } from '@/services/Import/_utils';
|
||||
|
||||
export function allowSheetExtensions(req, file, cb) {
|
||||
if (
|
||||
@@ -16,7 +17,8 @@ export function allowSheetExtensions(req, file, cb) {
|
||||
|
||||
const storage = Multer.diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
cb(null, './public/imports');
|
||||
const path = getImportsStoragePath();
|
||||
cb(null, path);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
// Add the creation timestamp to clean up temp files later.
|
||||
|
||||
@@ -180,6 +180,14 @@ module.exports = {
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* Bank Synchronization.
|
||||
*/
|
||||
bankSync: {
|
||||
enabled: parseBoolean(defaultTo(process.env.BANKING_CONNECT, false), false),
|
||||
provider: 'plaid',
|
||||
},
|
||||
|
||||
/**
|
||||
* Plaid.
|
||||
*/
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
export enum Features {
|
||||
WAREHOUSES = 'warehouses',
|
||||
BRANCHES = 'branches',
|
||||
BankSyncing = 'BankSyncing'
|
||||
}
|
||||
|
||||
export interface IFeatureAllItem {
|
||||
|
||||
@@ -62,13 +62,13 @@ export default class MetableStore implements IMetableStore {
|
||||
* @param {String} key -
|
||||
* @param {Mixied} defaultValue -
|
||||
*/
|
||||
get(query: string | IMetaQuery, defaultValue: any): any | false {
|
||||
get(query: string | IMetaQuery, defaultValue: any): any | null {
|
||||
const metadata = this.find(query);
|
||||
return metadata
|
||||
? metadata.value
|
||||
: typeof defaultValue !== 'undefined'
|
||||
? defaultValue
|
||||
: false;
|
||||
: null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import { defaultTo } from 'lodash';
|
||||
import { Inject, Service } from 'typedi';
|
||||
import { omit } from 'lodash';
|
||||
import { FeaturesSettingsDriver } from './FeaturesSettingsDriver';
|
||||
import { FeaturesConfigureManager } from './FeaturesConfigureManager';
|
||||
import { IFeatureAllItem } from '@/interfaces';
|
||||
|
||||
@Service()
|
||||
@@ -10,9 +7,6 @@ export class FeaturesManager {
|
||||
@Inject()
|
||||
private drive: FeaturesSettingsDriver;
|
||||
|
||||
@Inject()
|
||||
private configure: FeaturesConfigureManager;
|
||||
|
||||
/**
|
||||
* Turns-on the given feature name.
|
||||
* @param {number} tenantId
|
||||
@@ -40,35 +34,15 @@ export class FeaturesManager {
|
||||
* @returns {Promise<void>}
|
||||
*/
|
||||
public async accessible(tenantId: number, feature: string) {
|
||||
// Retrieves the feature default accessible value.
|
||||
const defaultValue = this.configure.getFeatureConfigure(
|
||||
feature,
|
||||
'defaultValue'
|
||||
);
|
||||
const isAccessible = await this.drive.accessible(tenantId, feature);
|
||||
|
||||
return defaultTo(isAccessible, defaultValue);
|
||||
return this.drive.accessible(tenantId, feature);
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the all features and their accessible value and default value.
|
||||
* @param {number} tenantId
|
||||
* @returns
|
||||
* @returns {Promise<IFeatureAllItem[]>}
|
||||
*/
|
||||
public async all(tenantId: number): Promise<IFeatureAllItem[]> {
|
||||
const all = await this.drive.all(tenantId);
|
||||
|
||||
return all.map((feature: IFeatureAllItem) => {
|
||||
const defaultAccessible = this.configure.getFeatureConfigure(
|
||||
feature.name,
|
||||
'defaultValue'
|
||||
);
|
||||
const isAccessible = feature.isAccessible;
|
||||
|
||||
return {
|
||||
...feature,
|
||||
isAccessible: defaultTo(isAccessible, defaultAccessible),
|
||||
};
|
||||
});
|
||||
return this.drive.all(tenantId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,11 +2,15 @@ import { Service, Inject } from 'typedi';
|
||||
import HasTenancyService from '@/services/Tenancy/TenancyService';
|
||||
import { FeaturesConfigure } from './constants';
|
||||
import { IFeatureAllItem } from '@/interfaces';
|
||||
import { FeaturesConfigureManager } from './FeaturesConfigureManager';
|
||||
|
||||
@Service()
|
||||
export class FeaturesSettingsDriver {
|
||||
@Inject()
|
||||
tenancy: HasTenancyService;
|
||||
private tenancy: HasTenancyService;
|
||||
|
||||
@Inject()
|
||||
private configure: FeaturesConfigureManager;
|
||||
|
||||
/**
|
||||
* Turns-on the given feature name.
|
||||
@@ -41,7 +45,15 @@ export class FeaturesSettingsDriver {
|
||||
async accessible(tenantId: number, feature: string) {
|
||||
const settings = this.tenancy.settings(tenantId);
|
||||
|
||||
return !!settings.get({ group: 'features', key: feature });
|
||||
const defaultValue = this.configure.getFeatureConfigure(
|
||||
feature,
|
||||
'defaultValue'
|
||||
);
|
||||
const settingValue = settings.get(
|
||||
{ group: 'features', key: feature },
|
||||
defaultValue
|
||||
);
|
||||
return settingValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { Features, IFeatureConfiugration } from '@/interfaces';
|
||||
import config from '@/config';
|
||||
import { defaultTo } from 'lodash';
|
||||
|
||||
export const FeaturesConfigure: IFeatureConfiugration[] = [
|
||||
{
|
||||
@@ -9,4 +11,8 @@ export const FeaturesConfigure: IFeatureConfiugration[] = [
|
||||
name: Features.WAREHOUSES,
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
name: Features.BankSyncing,
|
||||
defaultValue: defaultTo(config.bankSync.enabled, false),
|
||||
}
|
||||
];
|
||||
|
||||
@@ -38,8 +38,6 @@ export class ImportFileUploadService {
|
||||
filename: string,
|
||||
params: Record<string, number | string>
|
||||
): Promise<ImportFileUploadPOJO> {
|
||||
console.log(filename, 'filename');
|
||||
|
||||
try {
|
||||
return await this.importUnhandled(
|
||||
tenantId,
|
||||
|
||||
@@ -3,6 +3,7 @@ import moment from 'moment';
|
||||
import * as R from 'ramda';
|
||||
import { Knex } from 'knex';
|
||||
import fs from 'fs/promises';
|
||||
import path from 'path';
|
||||
import {
|
||||
defaultTo,
|
||||
upperFirst,
|
||||
@@ -353,7 +354,6 @@ export const parseKey = R.curry(
|
||||
_key = `${fieldKey}`;
|
||||
}
|
||||
}
|
||||
console.log(_key);
|
||||
return _key;
|
||||
}
|
||||
);
|
||||
@@ -432,13 +432,19 @@ export const sanitizeSheetData = (json) => {
|
||||
export const getMapToPath = (to: string, group = '') =>
|
||||
group ? `${group}.${to}` : to;
|
||||
|
||||
export const getImportsStoragePath = () => {
|
||||
return path.join(global.__storage_dir, `/imports`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes the imported file from the storage and database.
|
||||
* @param {string} filename
|
||||
*/
|
||||
export const deleteImportFile = async (filename: string) => {
|
||||
const filePath = getImportsStoragePath();
|
||||
|
||||
// Deletes the imported file.
|
||||
await fs.unlink(`public/imports/${filename}`);
|
||||
await fs.unlink(`${filePath}/${filename}`);
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -447,5 +453,7 @@ export const deleteImportFile = async (filename: string) => {
|
||||
* @returns {Promise<Buffer>}
|
||||
*/
|
||||
export const readImportFile = (filename: string) => {
|
||||
return fs.readFile(`public/imports/${filename}`);
|
||||
const filePath = getImportsStoragePath();
|
||||
|
||||
return fs.readFile(`${filePath}/${filename}`);
|
||||
};
|
||||
|
||||
@@ -70,7 +70,7 @@ export class LemonSqueezyWebhooks {
|
||||
const variantId = attributes.variant_id as string;
|
||||
|
||||
// We assume that the Plan table is up to date.
|
||||
const plan = await Plan.query().findOne('slug', 'essentials-yearly');
|
||||
const plan = await Plan.query().findOne('slug', 'early-adaptor');
|
||||
|
||||
if (!plan) {
|
||||
throw new Error(`Plan with variantId ${variantId} not found.`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import moment, { type unitOfTime } from 'moment';
|
||||
import moment, { unitOfTime } from 'moment';
|
||||
|
||||
export default class SubscriptionPeriod {
|
||||
private start: Date;
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
exports.up = function (knex) {
|
||||
return knex.seed.run({
|
||||
specific: 'seed_tenants_free_subscription.js',
|
||||
});
|
||||
};
|
||||
|
||||
exports.down = function (knex) {};
|
||||
@@ -194,7 +194,12 @@ export default class Tenant extends BaseModel {
|
||||
* @param {*} subscriptionSlug
|
||||
* @returns
|
||||
*/
|
||||
public newSubscription(planId, invoiceInterval, invoicePeriod, subscriptionSlug) {
|
||||
public newSubscription(
|
||||
planId,
|
||||
invoiceInterval,
|
||||
invoicePeriod,
|
||||
subscriptionSlug
|
||||
) {
|
||||
return Tenant.newSubscription(
|
||||
this.id,
|
||||
planId,
|
||||
@@ -202,7 +207,6 @@ export default class Tenant extends BaseModel {
|
||||
invoicePeriod,
|
||||
subscriptionSlug
|
||||
);
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
exports.seed = (knex) => {
|
||||
// Deletes ALL existing entries
|
||||
return knex('subscription_plan_subscriptions')
|
||||
.then(async () => {
|
||||
const tenants = await knex('tenants');
|
||||
|
||||
for (const tenant of tenants) {
|
||||
const existingSubscription = await knex('subscription_plan_subscriptions')
|
||||
.where('tenantId', tenant.id)
|
||||
.first();
|
||||
|
||||
if (!existingSubscription) {
|
||||
const freePlan = await knex('subscription_plans').where('slug', 'free').first();
|
||||
|
||||
await knex('subscription_plan_subscriptions').insert({
|
||||
tenantId: tenant.id,
|
||||
planId: freePlan.id,
|
||||
slug: 'main',
|
||||
startsAt: knex.fn.now(),
|
||||
endsAt: null,
|
||||
createdAt: knex.fn.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
1
packages/server/storage/.gitignore
vendored
1
packages/server/storage/.gitignore
vendored
@@ -1,3 +1,4 @@
|
||||
*
|
||||
!pdf/
|
||||
!imports/
|
||||
!.gitignore
|
||||
2
packages/server/storage/imports/.gitignore
vendored
Normal file
2
packages/server/storage/imports/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
*
|
||||
!.gitignore
|
||||
3
packages/webapp/.gitignore
vendored
3
packages/webapp/.gitignore
vendored
@@ -20,4 +20,5 @@
|
||||
.env.production.local
|
||||
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
|
||||
dist
|
||||
@@ -5,5 +5,6 @@ export const Features = {
|
||||
Warehouses: 'warehouses',
|
||||
Branches: 'branches',
|
||||
ManualJournal: 'manualJournal',
|
||||
Projects:'Projects'
|
||||
Projects:'Projects',
|
||||
BankSyncing: 'BankSyncing',
|
||||
}
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
Can,
|
||||
Icon,
|
||||
FormattedMessage as T,
|
||||
FeatureCan,
|
||||
} from '@/components';
|
||||
import { useRefreshCashflowAccounts } from '@/hooks/query';
|
||||
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
|
||||
@@ -21,7 +22,7 @@ import withCashflowAccountsTableActions from '../AccountTransactions/withCashflo
|
||||
|
||||
import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils';
|
||||
|
||||
import { ACCOUNT_TYPE } from '@/constants';
|
||||
import { ACCOUNT_TYPE, Features } from '@/constants';
|
||||
import { DialogsName } from '@/constants/dialogs';
|
||||
|
||||
import { compose } from '@/utils';
|
||||
@@ -110,12 +111,14 @@ function CashFlowAccountsActionsBar({
|
||||
</NavbarGroup>
|
||||
|
||||
<NavbarGroup align={Alignment.RIGHT}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={'Connect to Bank / Credit Card'}
|
||||
onClick={handleConnectToBank}
|
||||
/>
|
||||
<FeatureCan feature={Features.BankSyncing}>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
text={'Connect to Bank / Credit Card'}
|
||||
onClick={handleConnectToBank}
|
||||
/>
|
||||
<NavbarDivider />
|
||||
</FeatureCan>
|
||||
<Button
|
||||
className={Classes.MINIMAL}
|
||||
icon={<Icon icon="refresh-16" iconSize={14} />}
|
||||
|
||||
2709
pnpm-lock.yaml
generated
2709
pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -1,2 +0,0 @@
|
||||
packages:
|
||||
- "packages/*"
|
||||
24
scripts/backup.sh
Normal file
24
scripts/backup.sh
Normal file
@@ -0,0 +1,24 @@
|
||||
# Takes a backup of database Docker volume and compress into one .tar.gz
|
||||
# file and upload to s3 package through s3cmd.
|
||||
S3_BUCKET="s3://bigcapital-backup"
|
||||
|
||||
# Generate the current date and time formatted as YYYY-MM-DD-HH-MM-SS
|
||||
CURRENT_DATETIME=$(date +"%Y-%m-%d-%H-%M-%S")
|
||||
|
||||
# Define the filename with the current date and time
|
||||
FILE_NAME="bigcapital-mariadb-${CURRENT_DATETIME}.tar.gz"
|
||||
|
||||
# Create a sample file (replace this with your actual file creation process)
|
||||
echo "This is a sample file created on ${CURRENT_DATETIME}" > "$FILE_NAME"
|
||||
|
||||
docker run --rm \
|
||||
--mount source=bigcapital_prod_mysql,target=/data/db \
|
||||
-v $(pwd):/backup \
|
||||
busybox \
|
||||
tar -czvf "/backup/$FILE_NAME" /data/db
|
||||
|
||||
# Upload the file to S3 using s3cmd
|
||||
s3cmd put "$FILE_NAME" "$S3_BUCKET/"
|
||||
|
||||
# Remove the temporary file
|
||||
rm "$FILE_NAME"
|
||||
7
scripts/store-backup.sh
Normal file
7
scripts/store-backup.sh
Normal file
@@ -0,0 +1,7 @@
|
||||
|
||||
# Store the backup.
|
||||
docker run --rm \
|
||||
--mount source=bigcapital_dev_mysql,target=/data/db \
|
||||
-v $(pwd):/backup \
|
||||
busybox \
|
||||
tar -xzvf /backup/bigcapital-mariadb-2024-04-24-15-14-40.tar.gz -C /
|
||||
Reference in New Issue
Block a user