diff --git a/AGENTS.md b/AGENTS.md index 121eeea9..ff0a9dc9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -217,3 +217,7 @@ This project contains multiple web applications with distinct design systems. Fo - **`apps/web`**: Contains the "Glassmorphic Depth Design System" for the main user-facing photo gallery. See `apps/web/AGENTS.md` for details. - **`be/apps/dashboard`**: Contains guidelines for the functional, data-driven UI of the administration panel. See `be/apps/dashboard/AGENTS.md` for details. + +## IMPORTANT + +Avoid feature gates/flags and any backwards compability changes - since our app is still unreleased" is really helpful. \ No newline at end of file diff --git a/be/apps/core/src/modules/infrastructure/static-web/static-asset.service.ts b/be/apps/core/src/modules/infrastructure/static-web/static-asset.service.ts index 4062f744..e1a0969c 100644 --- a/be/apps/core/src/modules/infrastructure/static-web/static-asset.service.ts +++ b/be/apps/core/src/modules/infrastructure/static-web/static-asset.service.ts @@ -5,7 +5,7 @@ import { extname, isAbsolute, join, normalize, relative, resolve } from 'node:pa import { Readable } from 'node:stream' import type { PrettyLogger } from '@afilmory/framework' -import { createLogger } from '@afilmory/framework' +import { createLogger, HttpContext } from '@afilmory/framework' import { DOMParser } from 'linkedom' import { lookup as lookupMimeType } from 'mime-types' @@ -34,10 +34,6 @@ export interface StaticAssetServiceOptions { staticAssetHostResolver?: (requestHost?: string | null) => Promise } -export interface StaticAssetRequestOptions { - requestHost?: string | null -} - export interface ResolvedStaticAsset { absolutePath: string relativePath: string @@ -61,11 +57,7 @@ export abstract class StaticAssetService { this.staticAssetHostResolver = options.staticAssetHostResolver } - async handleRequest( - fullPath: string, - headOnly: boolean, - options?: StaticAssetRequestOptions, - ): Promise { + async handleRequest(fullPath: string, headOnly: boolean): Promise { const staticRoot = await this.resolveStaticRoot() if (!staticRoot) { return null @@ -78,7 +70,7 @@ export abstract class StaticAssetService { return null } - return await this.createResponse(target, headOnly, options) + return await this.createResponse(target, headOnly) } protected get routeSegment(): string { @@ -364,13 +356,9 @@ export abstract class StaticAssetService { return relativePath !== '' && !relativePath.startsWith('..') && !isAbsolute(relativePath) } - private async createResponse( - file: ResolvedStaticAsset, - headOnly: boolean, - options?: StaticAssetRequestOptions, - ): Promise { + private async createResponse(file: ResolvedStaticAsset, headOnly: boolean): Promise { if (this.isHtml(file.relativePath)) { - return await this.createHtmlResponse(file, headOnly, options) + return await this.createHtmlResponse(file, headOnly) } const mimeType = lookupMimeType(file.absolutePath) || 'application/octet-stream' @@ -390,13 +378,9 @@ export abstract class StaticAssetService { return new Response(body, { headers, status: 200 }) } - private async createHtmlResponse( - file: ResolvedStaticAsset, - headOnly: boolean, - options?: StaticAssetRequestOptions, - ): Promise { + private async createHtmlResponse(file: ResolvedStaticAsset, headOnly: boolean): Promise { const html = await readFile(file.absolutePath, 'utf-8') - const transformed = await this.transformIndexHtml(html, file, options) + const transformed = await this.transformIndexHtml(html, file) const headers = new Headers() headers.set('content-type', 'text/html; charset=utf-8') headers.set('content-length', `${Buffer.byteLength(transformed, 'utf-8')}`) @@ -410,16 +394,12 @@ export abstract class StaticAssetService { return new Response(transformed, { headers, status: 200 }) } - private async transformIndexHtml( - html: string, - file: ResolvedStaticAsset, - options?: StaticAssetRequestOptions, - ): Promise { + private async transformIndexHtml(html: string, file: ResolvedStaticAsset): Promise { try { const document = DOM_PARSER.parseFromString(html, 'text/html') as unknown as StaticAssetDocument await this.decorateDocument(document, file) if (this.shouldRewriteAssetReferences(file)) { - const staticAssetHost = await this.getStaticAssetHost(options?.requestHost) + const staticAssetHost = await this.getStaticAssetHost(this.resolveRequestHost()) this.rewriteStaticAssetReferences(document, staticAssetHost) } return document.documentElement.outerHTML @@ -458,6 +438,29 @@ export abstract class StaticAssetService { return requestHost.trim().toLowerCase() } + private resolveRequestHost(): string | null { + const context = HttpContext.getValue('hono') + if (!context) { + return null + } + const forwardedHost = context.req.header('x-forwarded-host')?.trim() + if (forwardedHost) { + return forwardedHost + } + + const host = context.req.header('host')?.trim() + if (host) { + return host + } + + try { + const url = new URL(context.req.url) + return url.hostname + } catch { + return null + } + } + private shouldTreatAsImmutable(relativePath: string): boolean { if (this.isHtml(relativePath)) { return false diff --git a/be/apps/core/src/modules/infrastructure/static-web/static-base.controller.ts b/be/apps/core/src/modules/infrastructure/static-web/static-base.controller.ts index 87226db9..8ecb9c55 100644 --- a/be/apps/core/src/modules/infrastructure/static-web/static-base.controller.ts +++ b/be/apps/core/src/modules/infrastructure/static-web/static-base.controller.ts @@ -1,7 +1,6 @@ import type { Context } from 'hono' import type { StaticAssetService } from './static-asset.service' -import { StaticControllerUtils } from './static-controller.utils' import type { StaticDashboardService } from './static-dashboard.service' import { STATIC_DASHBOARD_BASENAME } from './static-dashboard.service' import type { StaticWebService } from './static-web.service' @@ -20,9 +19,7 @@ export abstract class StaticBaseController { protected async serve(context: Context, service: StaticAssetService, headOnly: boolean): Promise { const pathname = context.req.path const normalizedPath = this.normalizeRequestPath(pathname, service) - const response = await service.handleRequest(normalizedPath, headOnly, { - requestHost: StaticControllerUtils.resolveRequestHost(context), - }) + const response = await service.handleRequest(normalizedPath, headOnly) if (response) { return response diff --git a/be/apps/core/src/modules/infrastructure/static-web/static-controller.utils.ts b/be/apps/core/src/modules/infrastructure/static-web/static-controller.utils.ts index b41cad8f..ca2d21d3 100644 --- a/be/apps/core/src/modules/infrastructure/static-web/static-controller.utils.ts +++ b/be/apps/core/src/modules/infrastructure/static-web/static-controller.utils.ts @@ -2,7 +2,6 @@ import { isTenantSlugReserved } from '@afilmory/utils' import { BizException, ErrorCode } from 'core/errors' import { ROOT_TENANT_SLUG } from 'core/modules/platform/tenant/tenant.constants' import { getTenantContext, isPlaceholderTenantContext } from 'core/modules/platform/tenant/tenant.context' -import type { Context } from 'hono' import type { StaticDashboardService } from './static-dashboard.service' import { STATIC_DASHBOARD_BASENAME } from './static-dashboard.service' @@ -11,25 +10,6 @@ const TENANT_MISSING_ENTRY_PATH = `${STATIC_DASHBOARD_BASENAME}/tenant-missing.h const TENANT_RESTRICTED_ENTRY_PATH = `${STATIC_DASHBOARD_BASENAME}/tenant-restricted.html` export const StaticControllerUtils = { - resolveRequestHost(context: Context): string | null { - const forwardedHost = context.req.header('x-forwarded-host')?.trim() - if (forwardedHost) { - return forwardedHost - } - - const host = context.req.header('host')?.trim() - if (host) { - return host - } - - try { - const url = new URL(context.req.url) - return url.host - } catch { - return null - } - }, - cloneResponseWithStatus(response: Response, status: number): Response { const headers = new Headers(response.headers) return new Response(response.body, { diff --git a/be/apps/core/src/modules/infrastructure/static-web/static-share.controller.ts b/be/apps/core/src/modules/infrastructure/static-web/static-share.controller.ts index b06f9da2..e03dd401 100644 --- a/be/apps/core/src/modules/infrastructure/static-web/static-share.controller.ts +++ b/be/apps/core/src/modules/infrastructure/static-web/static-share.controller.ts @@ -5,7 +5,7 @@ import { z } from 'zod' import { StaticControllerUtils } from './static-controller.utils' import { StaticDashboardService } from './static-dashboard.service' -import { STATIC_SHARE_ENTRY_PATH,StaticShareService } from './static-share.service' +import { STATIC_SHARE_ENTRY_PATH, StaticShareService } from './static-share.service' const shareQuerySchema = z.object({ id: z.string().min(1, 'Photo ID(s) required'), @@ -28,9 +28,7 @@ export class StaticShareController { return await StaticControllerUtils.renderTenantMissingPage(this.staticDashboardService) } - const response = await this.staticShareService.handleRequest(STATIC_SHARE_ENTRY_PATH, false, { - requestHost: StaticControllerUtils.resolveRequestHost(context), - }) + const response = await this.staticShareService.handleRequest(STATIC_SHARE_ENTRY_PATH, false) if (!response || response.status === 404) { throw new BizException(ErrorCode.COMMON_NOT_FOUND, { diff --git a/be/apps/dashboard/src/entries/tenant-missing.tsx b/be/apps/dashboard/src/entries/tenant-missing.tsx index dc2f2465..797eb58d 100644 --- a/be/apps/dashboard/src/entries/tenant-missing.tsx +++ b/be/apps/dashboard/src/entries/tenant-missing.tsx @@ -2,6 +2,8 @@ import '../styles/index.css' import { createRoot } from 'react-dom/client' +import { I18nProvider } from '~/providers/i18n-provider' + import { TenantMissingStandalone } from '../modules/welcome/components/TenantMissingStandalone' const root = document.querySelector('#root') @@ -10,4 +12,8 @@ if (!root) { throw new Error('Root element not found for tenant missing entry.') } -createRoot(root).render() +createRoot(root).render( + + + , +) diff --git a/be/apps/dashboard/src/entries/tenant-restricted.tsx b/be/apps/dashboard/src/entries/tenant-restricted.tsx index bd4a065e..bbec1438 100644 --- a/be/apps/dashboard/src/entries/tenant-restricted.tsx +++ b/be/apps/dashboard/src/entries/tenant-restricted.tsx @@ -2,6 +2,8 @@ import '../styles/index.css' import { createRoot } from 'react-dom/client' +import { I18nProvider } from '~/providers/i18n-provider' + import { TenantRestrictedStandalone } from '../modules/welcome/components/TenantRestrictedStandalone' const root = document.querySelector('#root') @@ -10,4 +12,8 @@ if (!root) { throw new Error('Root element not found for tenant restricted entry.') } -createRoot(root).render() +createRoot(root).render( + + + , +)