feat: support custom domain in TRUSTED_ORIGIN
This commit is contained in:
+3
-2
@@ -1,6 +1,7 @@
|
||||
import 'dotenv/config';
|
||||
|
||||
import { API_SERVER_URL, TRUSTED_ORIGINS } from '@api/constants';
|
||||
import { API_SERVER_URL } from '@api/constants';
|
||||
import { resolveTrustedBrowserOriginForCors } from '@api/utils/origins';
|
||||
|
||||
import { Hono } from '@api/utils/hono';
|
||||
import { accountRouter } from '@api/routes/account';
|
||||
@@ -44,7 +45,7 @@ export const app = new Hono()
|
||||
.use(
|
||||
'*',
|
||||
cors({
|
||||
origin: TRUSTED_ORIGINS,
|
||||
origin: (origin) => resolveTrustedBrowserOriginForCors(origin),
|
||||
allowMethods: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'],
|
||||
allowHeaders: ['Content-Type', 'Authorization', 'Cio-org-id'],
|
||||
exposeHeaders: ['Content-Length', 'X-Kuma-Revision'],
|
||||
|
||||
@@ -6,7 +6,7 @@ export const API_PORT = env.PORT ? parseInt(env.PORT) : 3002;
|
||||
export const API_SERVER_URL = env.PUBLIC_SERVER_URL || `http://localhost:${API_PORT}`;
|
||||
export const TRUSTED_ORIGINS = env.TRUSTED_ORIGINS
|
||||
? env.TRUSTED_ORIGINS.split(',')
|
||||
: ['http://localhost:5173', 'http://localhost:5180', 'http://*.classroomio.*'];
|
||||
: ['http://localhost:5173', 'http://localhost:5180', 'https://*.classroomio.com'];
|
||||
|
||||
const {
|
||||
CLOUDFLARE_BUCKET_DOMAIN,
|
||||
|
||||
@@ -2,9 +2,10 @@ import 'dotenv/config';
|
||||
|
||||
import { API_PORT } from '@api/constants';
|
||||
import { app } from '@api/app';
|
||||
import { env } from '@api/config/env';
|
||||
import { configureOpenAPI } from '@api/utils/openapi';
|
||||
import { connectRedis } from '@api/utils/redis/redis';
|
||||
import { env } from '@api/config/env';
|
||||
import { preloadVerifiedCustomDomainOriginsRegistry } from '@api/utils/origins';
|
||||
import { serve } from '@hono/node-server';
|
||||
import { showRoutes } from 'hono/dev';
|
||||
|
||||
@@ -15,6 +16,10 @@ async function startServer() {
|
||||
// Connect to Redis (non-blocking: API starts even if Redis fails)
|
||||
await connectRedis();
|
||||
|
||||
preloadVerifiedCustomDomainOriginsRegistry().then(() => {
|
||||
console.log('Verified custom domain origins preloaded');
|
||||
});
|
||||
|
||||
serve({ fetch: app.fetch, port: API_PORT });
|
||||
|
||||
if (env.NODE_ENV !== 'production') {
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
getOrgIdBySiteName,
|
||||
getOrganizationAudience,
|
||||
getOrganizationAudienceMember,
|
||||
getOrganizationById,
|
||||
getOrganizationBySiteName,
|
||||
getOrganizationTeam,
|
||||
getOrganizations,
|
||||
@@ -42,6 +43,7 @@ import { createOrganizationWithOwner } from '@api/services/onboarding';
|
||||
import { deriveAudienceMemberStatus } from '@api/utils/audience-member-status';
|
||||
import { getProfileById } from '@cio/db/queries/auth';
|
||||
import { inviteTeamMembers as inviteTeamMembersSecure } from './organization/invite';
|
||||
import { trustCustomDomainHostname, untrustCustomDomainHostname } from '@cio/db/utils';
|
||||
|
||||
const PUBLIC_ORG_LANDING_PAGE_COURSE_LIMIT = 4;
|
||||
|
||||
@@ -572,10 +574,32 @@ export async function updateOrg(orgId: string, data: Partial<TOrganization>) {
|
||||
}
|
||||
}
|
||||
|
||||
let previousCustomDomainHostname: string | undefined;
|
||||
|
||||
if (data.customDomain !== undefined || data.isCustomDomainVerified !== undefined) {
|
||||
const previousOrg = await getOrganizationById(orgId);
|
||||
const oldHost = previousOrg?.customDomain?.trim().toLowerCase();
|
||||
|
||||
if (oldHost) {
|
||||
previousCustomDomainHostname = oldHost;
|
||||
}
|
||||
}
|
||||
|
||||
const organization = await updateOrganization(orgId, data);
|
||||
if (!organization) {
|
||||
throw new AppError('Organization not found', ErrorCodes.ORGANIZATION_NOT_FOUND, 404);
|
||||
}
|
||||
|
||||
if (previousCustomDomainHostname) {
|
||||
untrustCustomDomainHostname(previousCustomDomainHostname);
|
||||
}
|
||||
|
||||
const newCustomDomainHostname = organization.customDomain?.trim().toLowerCase();
|
||||
|
||||
if (newCustomDomainHostname && organization.isCustomDomainVerified) {
|
||||
trustCustomDomainHostname(newCustomDomainHostname);
|
||||
}
|
||||
|
||||
return organization;
|
||||
} catch (error) {
|
||||
if (error instanceof AppError) {
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import {
|
||||
preloadVerifiedCustomDomainOrigins,
|
||||
resolveTrustedBrowserOrigin,
|
||||
trustCustomDomainHostname,
|
||||
untrustCustomDomainHostname
|
||||
} from '@cio/db/utils';
|
||||
|
||||
import { TRUSTED_ORIGINS } from '@api/constants';
|
||||
|
||||
const staticTrustedOriginEntries = TRUSTED_ORIGINS.map((entry) => entry.trim()).filter(Boolean) as readonly string[];
|
||||
|
||||
export async function preloadVerifiedCustomDomainOriginsRegistry(): Promise<void> {
|
||||
await preloadVerifiedCustomDomainOrigins();
|
||||
}
|
||||
|
||||
export function resolveTrustedBrowserOriginForCors(origin: string): string | undefined {
|
||||
console.log('check origin', origin);
|
||||
return resolveTrustedBrowserOrigin(origin, staticTrustedOriginEntries);
|
||||
}
|
||||
|
||||
export { trustCustomDomainHostname, untrustCustomDomainHostname };
|
||||
Reference in New Issue
Block a user