Commit Graph

1608 Commits

Author SHA1 Message Date
Ahmed Bouhuolia
ed48a2cbdb chore: format webapp files 2026-05-31 23:35:10 +02:00
Ahmed Bouhuolia
ef9c93cbf9 ci: add Prettier format check GitHub Action
Adds a format-check workflow that runs `pnpm run format:check` (Lerna fans
out to all packages with the script) on pushes to main/develop and all PRs.
Also adds the missing `format:check` script to the server package so Lerna
picks it up alongside the existing webapp script.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:33:30 +02:00
Ahmed Bouhuolia
9feebb2b1a Merge branch 'develop' into refactor/typescript-preferences-containers 2026-05-31 22:03:20 +02:00
Ahmed Bouhuolia
de28aea086 refactor(typescript): add proper types to Preferences container files
Remove @ts-nocheck directives and add TypeScript interfaces for
Preferences container components (Users, Roles, Accountant, Branches,
Currencies). Replace duplicated withUserPreferences HOC with existing
withDialogActions. Install @types/flat for the flat module.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 21:58:11 +02:00
Ahmed Bouhuolia
b6970fefc2 refactor: convert containers default exports to named exports
## Summary
Converted 905 default exports in src/containers to named exports for improved tree-shaking, better IDE refactoring support, and consistency with modern TypeScript practices.

## Changes
- Converted `export default function X` to `export function X` (916 files)
- Converted `export default compose(...)(X)` to `export const X = compose(...)(XInner)` with HOC wrapping
- Updated 373 import sites from default to named imports
- Fixed 136 React.lazy() imports to use .then() pattern for compatibility with named exports
- Updated re-export patterns in index files
- Fixed edge cases (alert arrays, connector HOCs, type definitions)

## Implementation
- Created codemod script: codemod-containers-exports.js (905 files converted)
- Created import updater: codemod-update-default-imports.js (373 imports fixed)
- Created lazy import fixer: codemod-fix-lazy-imports.js (136 lazy imports fixed)
- Manual fixes for 30 edge-case files (arrays, HOC factories, type definitions)

## Testing
- TypeScript type check: 0 codemod-related errors
- All lazy imports updated with .then() pattern
- All import sites updated to use named imports
- Zero remaining default exports in containers directory

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 20:08:39 +02:00
Ahmed Bouhuolia
7a70195ac0 feat(sdk): add table accept header to report fetchers and refactor audit log hooks
- Add `accept: application/json+table` header to all SDK report table
  fetcher functions for proper table response negotiation
- Refactor audit log query hooks to use SDK fetch functions instead of
  manual API calls with qs.stringify
- Modernize useInfiniteQuery to object syntax with initialPageParam
- Replace deprecated keepPreviousData option with placeholderData import
- Uncomment AuditLogHeader, AuditLogLoadingBar, and AuditLogBody components

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-31 18:52:44 +02:00
Ahmed Bouhuolia
7e787cf288 wip 2026-05-30 21:43:13 +02:00
Ahmed Bouhuolia
757480d258 wip 2026-05-30 19:37:03 +02:00
Ahmed Bouhuolia
326daaf4b4 wip 2026-05-30 00:40:35 +02:00
Ahmed Bouhuolia
def0fb62b6 Merge branch 'develop' into feat/query-hooks-refactor-kebab-case 2026-05-29 18:00:40 +02:00
Ahmed Bouhuolia
18c98f9c34 Merge pull request #1102 from bigcapitalhq/fix/server-typescript-type-errors
fix(server): resolve all TypeScript errors without unsafe type casts
2026-05-29 17:56:13 +02:00
Ahmed Bouhuolia
edf12fb87a fix(server): resolve all TypeScript errors without unsafe type casts
Fix 20+ pre-existing TypeScript errors in the server package using
proper type-safe solutions — no `as any`, `as unknown`, or `any` types.

Key changes:
- Replace R.curry with regular curried arrow functions for proper inference
- Add return types to abstract methods (DynamicFilterRoleAbstractor)
- Add field declarations to empty models (ItemWarehouseQuantity)
- Add index signature to IMetadata for dynamic extra columns
- Use explicit field construction instead of pick()+cast patterns
- Convert moment format strings to Date objects where Date type expected
- Make interface properties optional where payloads don't include them
- Use native Array.reduce with proper typing instead of lodash chain

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-29 17:52:46 +02:00
Ahmed Bouhuolia
3d687d51e1 wip 2026-05-29 13:32:59 +02:00
Ahmed Bouhuolia
d7d1783eee wip 2026-05-27 23:28:17 +02:00
Ahmed Bouhuolia
4a84d5996e wip 2026-05-27 17:37:39 +02:00
Ahmed Bouhuolia
f7d9ac765b Merge branch 'develop' into feat/query-hooks-refactor-kebab-case 2026-05-22 17:12:01 +02:00
Ahmed Bouhuolia
fa98b06a09 refactor(webapp): standardize withX HOC types with shared MapState generic and props interfaces
- Extract shared `MapState<MappedProps, OwnProps>` generic into `containers/hoc.types.ts`,
  replacing 57 per-file local type duplicates
- Add exported `WithXProps` interfaces to all state HOCs that lacked them, replacing
  `MapState<Record<string, unknown>, Props>` with the properly typed generic
- Add exported `WithXActionsProps` interfaces to all action HOCs, annotating
  `mapDispatchToProps` return types and ensuring consistent exports
- Fix TS errors in `withAlertStoreConnect`, `withDrawers`, `withAuthentication`,
  `withCurrentOrganization`, and `withOrganization` (missing args and missing casts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-22 15:25:45 +02:00
Ahmed Bouhuolia
bae7222230 Merge pull request #1100 from bigcapitalhq/refactor/store-kebab-case-rename
refactor(webapp): rename store files and folders to kebab-case
2026-05-20 21:58:34 +02:00
Ahmed Bouhuolia
d44ced73ad wip 2026-05-20 21:47:41 +02:00
Ahmed Bouhuolia
5edc3e4723 wip 2026-05-20 21:30:39 +02:00
Ahmed Bouhuolia
f77baaa84f wip 2026-05-20 20:57:29 +02:00
Ahmed Bouhuolia
a1a2f86e86 refactor(webapp/store): update all import paths to match kebab-case renames
Update ~112 files across packages/webapp/src to use the new
kebab-case folder and file names introduced in the previous commit.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 00:48:46 +02:00
Ahmed Bouhuolia
3d529833d5 refactor(webapp/store): rename store files/folders to kebab-case and .ts extension
- Rename 19 PascalCase/camelCase folders to kebab-case
  (e.g. CashflowAccounts → cashflow-accounts, financialStatement → financial-statement)
- Rename all .tsx store files to .ts (no JSX in any store file)
- Rename camelCase/PascalCase file base names to kebab-case
  (e.g. paymentMades.reducer.tsx → payment-mades.reducer.ts)
- Rename 9 root-level store files to kebab-case
  (createStore.tsx → create-store.ts, reducers.tsx → reducers.ts, etc.)
- Update all ~112 import paths across packages/webapp/src to match new paths

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 00:46:06 +02:00
Ahmed Bouhuolia
445bc8a582 Merge pull request #1078 from bigcapitalhq/feat/ee-workspaces-multi-org-pr
feat(ee): add multi-organization workspaces feature
2026-05-19 21:03:31 +02:00
Ahmed Bouhuolia
a8431da447 wip 2026-05-19 20:59:21 +02:00
Ahmed Bouhuolia
dede2b03ce wip 2026-05-19 20:58:04 +02:00
Ahmed Bouhuolia
e9b5974182 wip 2026-05-18 23:52:45 +02:00
Ahmed Bouhuolia
73578ab902 wip 2026-05-18 16:08:33 +02:00
Ahmed Bouhuolia
c69515f618 wip 2026-05-18 13:29:16 +02:00
Ahmed Bouhuolia
b1766a9c6f Merge branch 'develop' into feat/ee-workspaces-multi-org-pr 2026-05-18 13:25:35 +02:00
Ahmed Bouhuolia
7f3fbdc57d Merge pull request #1095 from bigcapitalhq/fix/plaid-webhook-signature-verification
fix(server): verify Plaid webhook signatures (GHSA-g56w-g54f-whq5)
2026-05-17 23:04:30 +02:00
Ahmed Bouhuolia
1a4f2e5b5d wip 2026-05-17 21:12:31 +02:00
Ahmed Bouhuolia
19d8aec2aa Merge remote-tracking branch 'refs/remotes/origin/feat/financial-audit-trail' into feat/financial-audit-trail 2026-05-17 20:46:03 +02:00
Ahmed Bouhuolia
8c8e5138df wip 2026-05-17 20:38:56 +02:00
Ahmed Bouhuolia
d74e02c21a Merge branch 'develop' into feat/financial-audit-trail 2026-05-17 19:55:15 +02:00
Ahmed Bouhuolia
00feae58a7 wip 2026-05-17 19:50:00 +02:00
Ahmed Bouhuolia
c23bc76afa fix(server): prevent SQL injection via sortOrder in DynamicListing (GHSA-hcp2-qqg6-jjpm)
Validate sortOrder against an allowlist at the DTO layer, normalize the
direction centrally in DynamicFilterSortBy.buildQuery, and re-sanitize
inside every orderByRaw modifier so attacker-controlled SQL cannot reach
the ORDER BY clause.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 21:35:22 +02:00
Ahmed Bouhuolia
ace15dbdeb fix(server): use CSPRNG for attachment S3 keys (GHSA-gj48-p5ff-g67f)
The multer-s3 storage factory used `Date.now().toString()` as the S3 key
for every upload, yielding a 13-digit ms-epoch key. The keyspace for any
time window equals the millisecond count of that window, so an attacker
holding a registered account can enumerate keys for known upload moments
(e.g. ~10 minutes for a 10-second window with a 10-proxy rotation), then
download files via `GET /attachments/:id/presigned-url`. Two uploads in
the same millisecond also collide and silently overwrite each other.

Replace the key callback with `${organizationId}/${randomUUID()}`:

  * `randomUUID()` from `node:crypto` is a v4 UUID with 122 bits of
    entropy, making brute-force enumeration infeasible.
  * The `<organizationId>/` prefix (read from the `nestjs-cls` store
    populated by `ClsModule` middleware in `App.module.ts`) limits the
    blast radius of any hypothetical bucket-listing leak to a single
    tenant.

Add a tenant migration applying `unique` to `documents.key` so any future
key collision surfaces as a DB error instead of a silent S3 overwrite.

Legacy 13-digit numeric keys remain accessible via their stored values;
only new uploads use the new format.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 19:38:39 +02:00
Ahmed Bouhuolia
78fb158b98 fix(server): verify Plaid webhook signatures (GHSA-g56w-g54f-whq5)
POST /api/banking/plaid/webhooks was @PublicRoute() and processed the
body without verifying Plaid's Plaid-Verification JWT, letting any
unauthenticated client replay or fabricate webhook events for a tenant
by guessing a plaidItemId.

Add PlaidWebhookVerificationService that verifies the Plaid-Verification
ES256 JWS using a JWK fetched from plaidClient.webhookVerificationKeyGet
(cached per kid via lru-cache for 24h), enforces a 5-minute iat replay
window through jose.jwtVerify({ maxTokenAge }), and timing-safe compares
the body's SHA-256 against the request_body_sha256 claim. The webhook
controller now consumes the raw body and the plaid-verification header,
runs verification before setupPlaidTenant, and returns 400 Bad Request
on any failure - so no tenant context is ever set for an unsigned or
tampered request.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 12:19:30 +02:00
Ahmed Bouhuolia
7efac090a9 fix(server): prevent cross-tenant access via organization-id header
Resolve a CLS middleware in App.module.ts to copy the request
`organization-id` header straight into `cls.organizationId`, which the
TenancyDB factory used to pick the per-tenant database. The JWT path
never set `organizationId` from the authenticated user, and
TenancyGlobalGuard only checked that the header was present — so any
authenticated user could read or write another tenant's database by
sending their own JWT plus the victim's `organization-id`.

Make the JWT-resolved tenant the source of truth and validate the
header at the edge:

- AuthSigninService.verifyPayload now loads the user's tenant and sets
  `cls.organizationId` from `tenant.organizationId`, mirroring the
  API-key path in AuthApiKeyAuthorizeService.
- TenancyGlobalGuard rejects with `Organization mismatch.` when the
  request header disagrees with the CLS value set by the auth guard.
- App.module.ts no longer seeds `cls.organizationId` from the
  attacker-controlled request header.

GHSA-2g96-86rw-qmvg

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 20:43:14 +02:00
Ahmed Bouhuolia
a9d2316fc2 Merge pull request #1093 from bigcapitalhq/fix/attachment-tenant-isolation
fix(server): prevent cross-tenant attachment access (IDOR)
2026-05-15 14:54:19 +02:00
Ahmed Bouhuolia
e18e61000d fix(server): prevent cross-tenant attachment access (IDOR)
Add tenant-scoped document lookup with throwIfNotFound() before S3
operations in GetAttachment, DeleteAttachment, and
GetAttachmentPresignedUrl services. This prevents users from reading,
deleting, or generating presigned URLs for attachments belonging to
other tenants.

Also adds RequirePermission decorators to the three attachment
endpoints and introduces Attachment ability subject with View and
Delete actions.

GHSA-rc4v-wq22-v6cf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 14:47:18 +02:00
Ahmed Bouhuolia
c53bf60406 wip 2026-05-12 18:25:52 +02:00
Ahmed Bouhuolia
f8afa00f29 Merge pull request #1089 from c-premus/fix/dynamic-filter-pagination 2026-05-12 10:53:54 +02:00
rebekah-create
3cbc4842b9 fix(server): correct "Liabilties" typo to "Liabilities" in balance sheet
The balance sheet rendered "Current Liabilties" in the Liabilities
section because of a typo in the i18n key, the schema reference, the
swagger example responses, and the generated SDK fixtures. Fixed all
five locations so PDF/HTML/JSON renders all read "Current Liabilities".

- packages/server/src/i18n/en/balance_sheet.json: rename key and value
- packages/server/src/modules/FinancialStatements/modules/BalanceSheet/
  BalanceSheetSchema.ts: update i18n key reference
- packages/server/src/modules/FinancialStatements/modules/BalanceSheet/
  BalanceSheet.swagger.ts: fix three example response strings
- shared/sdk-ts/openapi.json + schema.ts: regenerate to match
2026-05-12 00:10:44 -04:00
Chris
46012a1b1c Fix pagination params silently ignored on collection GET endpoints
Hoist `page` and `pageSize` declarations from the per-module DTOs into
the shared DynamicFilterQueryDto base class. Without these declarations,
the global ValidationPipe (whitelist: true) strips the params from the
request before the service layer sees them, so list services fall back
to their default page=1, pageSize=12 regardless of what the client sent.

Affects 10 collection GET endpoints whose query DTOs are empty subclasses
of DynamicFilterQueryDto: expenses, bills, credit-notes, manual-journals,
payments-received, sale-invoices, sale-estimates, sale-receipts,
vendor-credits, item-categories.

The 3 already-working DTOs (Customers, Vendors, Items) keep their local
page/pageSize declarations as redundant overrides — no behavior change.

Closes #1088
2026-04-29 11:51:49 +00:00
Ahmed Bouhuolia
6ef1a6a651 wip 2026-04-17 01:10:27 +02:00
Ahmed Bouhuolia
91f3c28737 wip 2026-04-15 22:09:25 +02:00
Ahmed Bouhuolia
a1430db37e wip 2026-04-15 00:35:17 +02:00
Ahmed Bouhuolia
70ff1fc179 wip 2026-04-14 21:36:40 +02:00