Compare commits

..

2 Commits

Author SHA1 Message Date
allcontributors[bot]
6012c035ec docs: update .all-contributorsrc [skip ci] 2024-04-22 08:20:43 +00:00
allcontributors[bot]
7587c6510c docs: update README.md [skip ci] 2024-04-22 08:20:42 +00:00
16 changed files with 59 additions and 174 deletions

View File

@@ -114,15 +114,6 @@
"contributions": [ "contributions": [
"code" "code"
] ]
},
{
"login": "cloudsbird",
"name": "Vederis Leunardus",
"avatar_url": "https://avatars.githubusercontent.com/u/13505006?v=4",
"profile": "http://vederis.id",
"contributions": [
"code"
]
} }
], ],
"contributorsPerLine": 7, "contributorsPerLine": 7,

View File

@@ -12,33 +12,16 @@ env:
jobs: jobs:
build-publish-webapp: build-publish-webapp:
strategy:
fail-fast: false
matrix:
platform:
- linux/amd64
- linux/arm64
name: Build and deploy webapp container name: Build and deploy webapp container
runs-on: ubuntu-latest runs-on: ubuntu-latest
environment: production environment: production
steps: steps:
- name: Prepare
run: |
platform=${{ matrix.platform }}
echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@v2
- 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. # Login to Container registry.
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@v3 uses: docker/login-action@v1
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -52,29 +35,14 @@ jobs:
# Builds and push the Docker image. # Builds and push the Docker image.
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v2
id: build
with: with:
context: . context: .
file: ./packages/webapp/Dockerfile file: ./packages/webapp/Dockerfile
platforms: ${{ matrix.platform }}
push: true push: true
tags: ghcr.io/bigcapitalhq/webapp:latest tags: ghcr.io/bigcapitalhq/webapp:latest
labels: ${{ steps.meta.outputs.labels }} 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. # Send notification to Slack channel.
- name: Slack Notification built and published webapp container successfully. - name: Slack Notification built and published webapp container successfully.
uses: rtCamp/action-slack-notify@v2 uses: rtCamp/action-slack-notify@v2
@@ -85,23 +53,12 @@ jobs:
name: Build and deploy server container name: Build and deploy server container
runs-on: ubuntu-latest runs-on: ubuntu-latest
steps: steps:
- name: Prepare - name: Checkout code
run: | uses: actions/checkout@v2
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. # Login to Container registry.
- name: Log in to the Container registry - name: Log in to the Container registry
uses: docker/login-action@v3 uses: docker/login-action@v1
with: with:
registry: ${{ env.REGISTRY }} registry: ${{ env.REGISTRY }}
username: ${{ github.actor }} username: ${{ github.actor }}
@@ -109,30 +66,14 @@ jobs:
# Builds and push the Docker image. # Builds and push the Docker image.
- name: Build and push Docker image - name: Build and push Docker image
uses: docker/build-push-action@v5 uses: docker/build-push-action@v2
id: build
with: with:
context: ./ context: ./
file: ./packages/server/Dockerfile file: ./packages/server/Dockerfile
platforms: ${{ matrix.platform }}
push: true push: true
tags: ghcr.io/bigcapitalhq/server:latest tags: ghcr.io/bigcapitalhq/server:latest
labels: ${{ steps.meta.outputs.labels }} 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. # Send notification to Slack channel.
- name: Slack Notification built and published server container successfully. - name: Slack Notification built and published server container successfully.
uses: rtCamp/action-slack-notify@v2 uses: rtCamp/action-slack-notify@v2

View File

@@ -123,7 +123,6 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d
<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://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://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="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> </tr>
</tbody> </tbody>
</table> </table>

View File

@@ -86,16 +86,6 @@ services:
- GOTENBERG_URL=${GOTENBERG_URL} - GOTENBERG_URL=${GOTENBERG_URL}
- GOTENBERG_DOCS_URL=${GOTENBERG_DOCS_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 # Lemon Squeez
- LEMONSQUEEZY_API_KEY=${LEMONSQUEEZY_API_KEY} - LEMONSQUEEZY_API_KEY=${LEMONSQUEEZY_API_KEY}
- LEMONSQUEEZY_STORE_ID=${LEMONSQUEEZY_STORE_ID} - LEMONSQUEEZY_STORE_ID=${LEMONSQUEEZY_STORE_ID}

View File

@@ -8,10 +8,10 @@ export default class DashboardMetaController {
dashboardService: DashboardService; dashboardService: DashboardService;
/** /**
* Constructor router. *
* @returns * @returns
*/ */
public router() { router() {
const router = Router(); const router = Router();
router.get('/boot', this.getDashboardBoot); router.get('/boot', this.getDashboardBoot);
@@ -25,7 +25,7 @@ export default class DashboardMetaController {
* @param {Response} res - * @param {Response} res -
* @param {NextFunction} next - * @param {NextFunction} next -
*/ */
private getDashboardBoot = async ( getDashboardBoot = async (
req: Request, req: Request,
res: Response, res: Response,
next: NextFunction next: NextFunction

View File

@@ -180,14 +180,6 @@ module.exports = {
}, },
}, },
/**
* Bank Synchronization.
*/
bankSync: {
enabled: parseBoolean(defaultTo(process.env.BANKING_CONNECT, false), false),
provider: 'plaid',
},
/** /**
* Plaid. * Plaid.
*/ */

View File

@@ -1,7 +1,6 @@
export enum Features { export enum Features {
WAREHOUSES = 'warehouses', WAREHOUSES = 'warehouses',
BRANCHES = 'branches', BRANCHES = 'branches',
BankSyncing = 'BankSyncing'
} }
export interface IFeatureAllItem { export interface IFeatureAllItem {

View File

@@ -62,13 +62,13 @@ export default class MetableStore implements IMetableStore {
* @param {String} key - * @param {String} key -
* @param {Mixied} defaultValue - * @param {Mixied} defaultValue -
*/ */
get(query: string | IMetaQuery, defaultValue: any): any | null { get(query: string | IMetaQuery, defaultValue: any): any | false {
const metadata = this.find(query); const metadata = this.find(query);
return metadata return metadata
? metadata.value ? metadata.value
: typeof defaultValue !== 'undefined' : typeof defaultValue !== 'undefined'
? defaultValue ? defaultValue
: null; : false;
} }
/** /**

View File

@@ -1,5 +1,8 @@
import { defaultTo } from 'lodash';
import { Inject, Service } from 'typedi'; import { Inject, Service } from 'typedi';
import { omit } from 'lodash';
import { FeaturesSettingsDriver } from './FeaturesSettingsDriver'; import { FeaturesSettingsDriver } from './FeaturesSettingsDriver';
import { FeaturesConfigureManager } from './FeaturesConfigureManager';
import { IFeatureAllItem } from '@/interfaces'; import { IFeatureAllItem } from '@/interfaces';
@Service() @Service()
@@ -7,6 +10,9 @@ export class FeaturesManager {
@Inject() @Inject()
private drive: FeaturesSettingsDriver; private drive: FeaturesSettingsDriver;
@Inject()
private configure: FeaturesConfigureManager;
/** /**
* Turns-on the given feature name. * Turns-on the given feature name.
* @param {number} tenantId * @param {number} tenantId
@@ -34,15 +40,35 @@ export class FeaturesManager {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
public async accessible(tenantId: number, feature: string) { public async accessible(tenantId: number, feature: string) {
return this.drive.accessible(tenantId, feature); // 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);
} }
/** /**
* Retrieves the all features and their accessible value and default value. * Retrieves the all features and their accessible value and default value.
* @param {number} tenantId * @param {number} tenantId
* @returns {Promise<IFeatureAllItem[]>} * @returns
*/ */
public async all(tenantId: number): Promise<IFeatureAllItem[]> { public async all(tenantId: number): Promise<IFeatureAllItem[]> {
return this.drive.all(tenantId); 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),
};
});
} }
} }

View File

@@ -2,15 +2,11 @@ import { Service, Inject } from 'typedi';
import HasTenancyService from '@/services/Tenancy/TenancyService'; import HasTenancyService from '@/services/Tenancy/TenancyService';
import { FeaturesConfigure } from './constants'; import { FeaturesConfigure } from './constants';
import { IFeatureAllItem } from '@/interfaces'; import { IFeatureAllItem } from '@/interfaces';
import { FeaturesConfigureManager } from './FeaturesConfigureManager';
@Service() @Service()
export class FeaturesSettingsDriver { export class FeaturesSettingsDriver {
@Inject() @Inject()
private tenancy: HasTenancyService; tenancy: HasTenancyService;
@Inject()
private configure: FeaturesConfigureManager;
/** /**
* Turns-on the given feature name. * Turns-on the given feature name.
@@ -45,15 +41,7 @@ export class FeaturesSettingsDriver {
async accessible(tenantId: number, feature: string) { async accessible(tenantId: number, feature: string) {
const settings = this.tenancy.settings(tenantId); const settings = this.tenancy.settings(tenantId);
const defaultValue = this.configure.getFeatureConfigure( return !!settings.get({ group: 'features', key: feature });
feature,
'defaultValue'
);
const settingValue = settings.get(
{ group: 'features', key: feature },
defaultValue
);
return settingValue;
} }
/** /**

View File

@@ -1,6 +1,4 @@
import { Features, IFeatureConfiugration } from '@/interfaces'; import { Features, IFeatureConfiugration } from '@/interfaces';
import config from '@/config';
import { defaultTo } from 'lodash';
export const FeaturesConfigure: IFeatureConfiugration[] = [ export const FeaturesConfigure: IFeatureConfiugration[] = [
{ {
@@ -11,8 +9,4 @@ export const FeaturesConfigure: IFeatureConfiugration[] = [
name: Features.WAREHOUSES, name: Features.WAREHOUSES,
defaultValue: false, defaultValue: false,
}, },
{
name: Features.BankSyncing,
defaultValue: defaultTo(config.bankSync.enabled, false),
}
]; ];

View File

@@ -70,7 +70,7 @@ export class LemonSqueezyWebhooks {
const variantId = attributes.variant_id as string; const variantId = attributes.variant_id as string;
// We assume that the Plan table is up to date. // We assume that the Plan table is up to date.
const plan = await Plan.query().findOne('slug', 'early-adaptor'); const plan = await Plan.query().findOne('slug', 'essentials-yearly');
if (!plan) { if (!plan) {
throw new Error(`Plan with variantId ${variantId} not found.`); throw new Error(`Plan with variantId ${variantId} not found.`);

View File

@@ -5,6 +5,5 @@ export const Features = {
Warehouses: 'warehouses', Warehouses: 'warehouses',
Branches: 'branches', Branches: 'branches',
ManualJournal: 'manualJournal', ManualJournal: 'manualJournal',
Projects:'Projects', Projects:'Projects'
BankSyncing: 'BankSyncing',
} }

View File

@@ -12,7 +12,6 @@ import {
Can, Can,
Icon, Icon,
FormattedMessage as T, FormattedMessage as T,
FeatureCan,
} from '@/components'; } from '@/components';
import { useRefreshCashflowAccounts } from '@/hooks/query'; import { useRefreshCashflowAccounts } from '@/hooks/query';
import { CashflowAction, AbilitySubject } from '@/constants/abilityOption'; import { CashflowAction, AbilitySubject } from '@/constants/abilityOption';
@@ -22,7 +21,7 @@ import withCashflowAccountsTableActions from '../AccountTransactions/withCashflo
import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils'; import { AccountDialogAction } from '@/containers/Dialogs/AccountDialog/utils';
import { ACCOUNT_TYPE, Features } from '@/constants'; import { ACCOUNT_TYPE } from '@/constants';
import { DialogsName } from '@/constants/dialogs'; import { DialogsName } from '@/constants/dialogs';
import { compose } from '@/utils'; import { compose } from '@/utils';
@@ -111,14 +110,12 @@ function CashFlowAccountsActionsBar({
</NavbarGroup> </NavbarGroup>
<NavbarGroup align={Alignment.RIGHT}> <NavbarGroup align={Alignment.RIGHT}>
<FeatureCan feature={Features.BankSyncing}>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
text={'Connect to Bank / Credit Card'} text={'Connect to Bank / Credit Card'}
onClick={handleConnectToBank} onClick={handleConnectToBank}
/> />
<NavbarDivider /> <NavbarDivider />
</FeatureCan>
<Button <Button
className={Classes.MINIMAL} className={Classes.MINIMAL}
icon={<Icon icon="refresh-16" iconSize={14} />} icon={<Icon icon="refresh-16" iconSize={14} />}

View File

@@ -1,24 +0,0 @@
# 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"

View File

@@ -1,7 +0,0 @@
# 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 /