refactor: rename and enhance CORS path handling for public API and widgets
This commit is contained in:
+3
-3
@@ -6,7 +6,7 @@ import { Hono } from '@api/utils/hono';
|
||||
import { accountRouter } from '@api/routes/account';
|
||||
import { auth } from '@cio/db/auth';
|
||||
import { communityRouter } from '@api/routes/community';
|
||||
import { isPublicApiPath, sessionCors } from '@api/middlewares/cors';
|
||||
import { isPublicCorsPath, sessionCors } from '@api/middlewares/cors';
|
||||
import { courseRouter } from '@api/routes/course';
|
||||
import { dashAnalyticsRouter } from '@api/routes/dash';
|
||||
import { domainRouter } from '@api/routes/domain/domain';
|
||||
@@ -42,13 +42,13 @@ export const app = new Hono()
|
||||
.use('*', prettyJSON())
|
||||
.use('*', secureHeaders())
|
||||
.use('*', async (c, next) => {
|
||||
if (isPublicApiPath(c.req.path)) return next();
|
||||
if (isPublicCorsPath(c.req.path)) return next();
|
||||
|
||||
return sessionCors(c, next);
|
||||
})
|
||||
.use('*', rateLimiter)
|
||||
.use('*', async (c, next) => {
|
||||
if (isPublicApiPath(c.req.path)) {
|
||||
if (isPublicCorsPath(c.req.path)) {
|
||||
c.set('user', null);
|
||||
c.set('session', null);
|
||||
c.set('orgRoles', {});
|
||||
|
||||
@@ -28,9 +28,16 @@ export const sessionCors = cors({
|
||||
credentials: true
|
||||
});
|
||||
|
||||
/** Path prefix considered "public REST API" for CORS / session bypass. */
|
||||
export const PUBLIC_API_PATH_PREFIX = '/public-api/';
|
||||
/**
|
||||
* Path prefixes that must accept requests from any third-party origin (no cookies):
|
||||
*
|
||||
* - `/public-api/`: Bearer-token REST API (automation keys).
|
||||
* - `/widgets/`: Anonymous payload endpoint hit by the course-widget embed script on customer sites.
|
||||
*
|
||||
* Requests under these prefixes use `publicApiCors` and skip the Better Auth session lookup.
|
||||
*/
|
||||
export const PUBLIC_CORS_PATH_PREFIXES = ['/public-api/', '/widgets/'] as const;
|
||||
|
||||
export function isPublicApiPath(path: string): boolean {
|
||||
return path.startsWith(PUBLIC_API_PATH_PREFIX);
|
||||
export function isPublicCorsPath(path: string): boolean {
|
||||
return PUBLIC_CORS_PATH_PREFIXES.some((prefix) => path.startsWith(prefix));
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { ZWidgetPublicKeyParams } from '@cio/utils/validation/widget';
|
||||
import { zValidator } from '@hono/zod-validator';
|
||||
import { createRateLimiter } from '@api/middlewares/rate-limiter';
|
||||
import { DEFAULT_WINDOW_MS } from '@api/constants/rate-limiter';
|
||||
import { publicApiCors } from '@api/middlewares/cors';
|
||||
|
||||
const widgetPayloadIpLimiter = createRateLimiter({
|
||||
maxRequests: 100,
|
||||
@@ -19,7 +20,7 @@ const widgetPayloadKeyLimiter = createRateLimiter({
|
||||
keyGenerator: (c) => `widget_payload_key:${c.req.param('publicKey')}`
|
||||
});
|
||||
|
||||
export const publicWidgetsRouter = new Hono().get(
|
||||
export const publicWidgetsRouter = new Hono().use('*', publicApiCors).get(
|
||||
'/:publicKey/payload',
|
||||
zValidator('param', ZWidgetPublicKeyParams),
|
||||
widgetPayloadIpLimiter,
|
||||
|
||||
@@ -251,8 +251,8 @@
|
||||
<div class="mx-auto flex w-full items-center justify-between gap-20 px-4 py-1 lg:w-[90%] lg:px-0">
|
||||
<a href="/" class="w-[10%]">
|
||||
<div class="flex w-full items-center">
|
||||
<img loading="lazy" width="28" height="28" src="/logo-512.png" alt="classroomio logo" class="w-6 md:w-7" />
|
||||
<h1 class="ml-2 text-xs font-medium md:text-base">ClassroomIO</h1>
|
||||
<img loading="lazy" width="28" height="28" src="/logo-512.png" alt="classroomio logo" class="w-7" />
|
||||
<h1 class="ml-2 text-base font-medium">ClassroomIO</h1>
|
||||
</div>
|
||||
</a>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user