mirror of
https://github.com/hanzoai/dataroom.git
synced 2026-10-30 18:41:07 +00:00
Drop the dual-licensed `ee/` tree entirely. All features previously
locked behind the Hanzo Dataroom Commercial License are now part of the
AGPL-3.0 core and freely available — custom domains, branded viewing,
advanced analytics, watermarks, access controls, Q&A, AI vector stores,
workflows, SAML SSO, dataroom invitations, conversations, templates.
Layout changes:
* `ee/features/{ai,conversations,workflows,permissions,storage,templates,
dataroom-invitations,access-notifications,security,conversions}/` →
`features/<name>/` (AGPL).
* `ee/limits/` → `lib/billing/limits/` with every plan resolving to an
unlimited profile (no users/links/documents/domains/datarooms cap,
conversations and watermarks always on).
* `ee/stripe/` → `lib/billing/legacy/` (already a compat shim over
`@hanzoai/commerce`; renewal-reminder webhook is now a no-op).
* `ee/features/billing/cancellation/` deleted. Pause/unpause/cancel/
retention routes now compile against `lib/billing/cancellation.ts`
stubs that return 410 Gone; `isTeamPaused`/`isTeamPausedById` in
`lib/billing/paused.ts` always report active. `CancellationModal` is
a no-op shim in `components/billing/`.
* `app/(ee)/api/*` route group folded into `app/api/*` (SAML auth,
SCIM, AI chat, workflows, FAQ, link-upload).
Build: `npm run build` green.
100 lines
2.9 KiB
TypeScript
100 lines
2.9 KiB
TypeScript
import type {
|
|
IConnectionAPIController,
|
|
IDirectorySyncController,
|
|
IOAuthController,
|
|
JacksonOption,
|
|
} from "@boxyhq/saml-jackson";
|
|
import samlJackson from "@boxyhq/saml-jackson";
|
|
import crypto from "crypto";
|
|
|
|
export const samlAudience = "https://saml.dataroom.hanzo.ai";
|
|
|
|
export { JACKSON_PRODUCT as jacksonProduct } from "@/features/security/sso/product";
|
|
|
|
// Jackson's AES-256-GCM encrypter requires exactly 32 bytes.
|
|
// Derive a stable 32-byte key from NEXTAUTH_SECRET using SHA-256.
|
|
const encryptionKey = crypto
|
|
.createHash("sha256")
|
|
.update(process.env.NEXTAUTH_SECRET || "")
|
|
.digest("base64url")
|
|
.substring(0, 32);
|
|
|
|
function sanitizePostgresUrl(rawUrl: string): string {
|
|
try {
|
|
const parsed = new URL(rawUrl);
|
|
|
|
// Some providers emit ssl cert params with value "system", which can cause
|
|
// node-postgres to attempt reading a local file literally named "system".
|
|
for (const param of ["sslcert", "sslrootcert", "sslkey", "sslcrl"]) {
|
|
const value = parsed.searchParams.get(param);
|
|
if (value?.toLowerCase() === "system") {
|
|
parsed.searchParams.delete(param);
|
|
}
|
|
}
|
|
|
|
return parsed.toString();
|
|
} catch {
|
|
// If URL parsing fails, pass through so downstream errors remain visible.
|
|
return rawUrl;
|
|
}
|
|
}
|
|
|
|
function getJacksonDbUrl(): string {
|
|
const candidates = [
|
|
process.env.POSTGRES_PRISMA_URL,
|
|
process.env.POSTGRES_PRISMA_URL_NON_POOLING,
|
|
];
|
|
|
|
const dbUrl = candidates.find((value) => typeof value === "string" && value.trim().length > 0);
|
|
|
|
if (!dbUrl) {
|
|
throw new Error(
|
|
"Missing Jackson DB URL. Set POSTGRES_PRISMA_URL or POSTGRES_PRISMA_URL_NON_POOLING.",
|
|
);
|
|
}
|
|
|
|
return sanitizePostgresUrl(dbUrl);
|
|
}
|
|
|
|
function getJacksonOptions(): JacksonOption {
|
|
return {
|
|
externalUrl: process.env.NEXTAUTH_URL || "https://dataroom.hanzo.ai",
|
|
samlPath: "/api/auth/saml/callback",
|
|
samlAudience,
|
|
db: {
|
|
engine: "sql",
|
|
type: "postgres",
|
|
url: getJacksonDbUrl(),
|
|
encryptionKey,
|
|
},
|
|
idpEnabled: true, // to allow to SSO from their IDP
|
|
scimPath: "/api/scim/v2.0",
|
|
clientSecretVerifier: process.env.NEXTAUTH_SECRET as string,
|
|
};
|
|
}
|
|
|
|
declare global {
|
|
var apiController: IConnectionAPIController | undefined;
|
|
var oauthController: IOAuthController | undefined;
|
|
var directorySyncController: IDirectorySyncController | undefined;
|
|
}
|
|
|
|
export async function jackson() {
|
|
if (
|
|
!globalThis.apiController ||
|
|
!globalThis.oauthController ||
|
|
!globalThis.directorySyncController
|
|
) {
|
|
const ret = await samlJackson(getJacksonOptions());
|
|
globalThis.apiController = ret.connectionAPIController;
|
|
globalThis.oauthController = ret.oauthController;
|
|
globalThis.directorySyncController = ret.directorySyncController;
|
|
}
|
|
|
|
return {
|
|
apiController: globalThis.apiController,
|
|
oauthController: globalThis.oauthController,
|
|
directorySyncController: globalThis.directorySyncController,
|
|
};
|
|
}
|