Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
import { eq, sql } from 'drizzle-orm';
|
|
|
|
|
import type { PgTable, PgColumn } from 'drizzle-orm/pg-core';
|
|
|
|
|
import { db } from './index';
|
|
|
|
|
|
fix(audit-wave-10): types-auditor fixes — Tx type, BerthDetailData, parseBody, toAuditJson
Address the CRITICAL + high-leverage HIGH items from the types-auditor:
**C1 — `tx: any` in client-restore.service**
Export a canonical `Tx` type from `lib/db/utils.ts` (derived from
Drizzle's `db.transaction` callback shape) and use it in
`applyReversal` so the 12+ downstream tx writes get full inference.
**C2 — berth-detail page stacked `useQuery<any>` escape hatches**
Export `BerthDetailData` from berth-detail-header and consume it
through useQuery + apiFetch. Removed three `any` escapes in the
highest-traffic detail page. Also collapsed the duplicate `BerthData`
in berth-tabs.tsx to import from berth-detail-header so the two
types can't drift.
**C3 — parseBody migration for portal/public routes**
Replace raw `await req.json() + schema.parse(body)` with the
project-standard `parseBody(req, schema)` helper across 7 routes:
- portal/auth/{change-password, activate, reset-password}
- auth/set-password
- public/{interests, residential-inquiries}
Skipped the three anti-enumeration routes (forgot-password, sign-in,
sign-in-by-identifier) where the manual validation gives opaque
errors on purpose. website-inquiries already wraps the parse in a
custom 400 — left as-is.
**HIGH #5 — `toAuditJson<T>` helper (21 → 0 inline casts)**
Introduce `toAuditJson<T extends object>(row: T): Record<string,
unknown>` in lib/audit.ts (mirrors gdpr-bundle-builder's `toJsonRow`
that already exists for the same reason). Codemod 21 `<row> as unknown
as Record<string, unknown>` sites across:
- invoices.ts × 6
- expenses.ts × 6
- berths.service × 2
- documents.service × 2
- ocr-config.service × 2
- ai-budget.service × 2
- yachts.service, companies.service, company-memberships.service × 1 each
document-templates' `payload as unknown as Record<...>` is a different
shape (Documenso form-values widening, not an audit log) — kept the
manual cast there. Tests stay 1315/1315.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-13 12:27:08 +02:00
|
|
|
/**
|
|
|
|
|
* Drizzle transaction client type — the argument shape `db.transaction`'s
|
|
|
|
|
* callback receives. Exported so service helpers that take a `tx`
|
|
|
|
|
* parameter can spell the type instead of falling back to `any`.
|
|
|
|
|
*/
|
|
|
|
|
export type Tx = Parameters<Parameters<typeof db.transaction>[0]>[0];
|
|
|
|
|
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
/**
|
|
|
|
|
* Wraps a database operation in a transaction.
|
|
|
|
|
* Rolls back automatically on error.
|
|
|
|
|
*
|
|
|
|
|
* @example
|
|
|
|
|
* const result = await withTransaction(async (tx) => {
|
|
|
|
|
* await tx.insert(clients).values({ ... });
|
|
|
|
|
* await tx.insert(interests).values({ ... });
|
|
|
|
|
* return result;
|
|
|
|
|
* });
|
|
|
|
|
*/
|
2026-05-11 13:01:47 +02:00
|
|
|
export async function withTransaction<T>(callback: (tx: typeof db) => Promise<T>): Promise<T> {
|
2026-03-26 12:06:18 +01:00
|
|
|
return db.transaction(callback as unknown as Parameters<typeof db.transaction>[0]) as Promise<T>;
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Soft-deletes a record by setting `archived_at` to now.
|
|
|
|
|
* The table must have an `archived_at` column.
|
|
|
|
|
*
|
|
|
|
|
* @example
|
|
|
|
|
* await softDelete(clients, clients.id, clientId);
|
|
|
|
|
*/
|
|
|
|
|
export async function softDelete<TTable extends PgTable>(
|
|
|
|
|
table: TTable,
|
|
|
|
|
idColumn: PgColumn,
|
|
|
|
|
id: string,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await db
|
|
|
|
|
.update(table)
|
2026-03-26 12:06:18 +01:00
|
|
|
.set({ archived_at: sql`now()` } as Record<string, unknown>)
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
.where(eq(idColumn, id));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Restores a soft-deleted record by clearing `archived_at`.
|
|
|
|
|
* The table must have an `archived_at` column.
|
|
|
|
|
*
|
|
|
|
|
* @example
|
|
|
|
|
* await restore(clients, clients.id, clientId);
|
|
|
|
|
*/
|
|
|
|
|
export async function restore<TTable extends PgTable>(
|
|
|
|
|
table: TTable,
|
|
|
|
|
idColumn: PgColumn,
|
|
|
|
|
id: string,
|
|
|
|
|
): Promise<void> {
|
|
|
|
|
await db
|
|
|
|
|
.update(table)
|
2026-03-26 12:06:18 +01:00
|
|
|
.set({ archived_at: null } as Record<string, unknown>)
|
Initial commit: Port Nimara CRM (Layers 0-4)
Full CRM rebuild with Next.js 15, TypeScript, Tailwind, Drizzle ORM,
PostgreSQL, Redis, BullMQ, MinIO, and Socket.io. Includes 461 source
files covering clients, berths, interests/pipeline, documents/EOI,
expenses/invoices, email, notifications, dashboard, admin, and
client portal. CI/CD via Gitea Actions with Docker builds.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 11:52:51 +01:00
|
|
|
.where(eq(idColumn, id));
|
|
|
|
|
}
|