diff --git a/apps/remix/app/components/forms/branding-preferences-form.tsx b/apps/remix/app/components/forms/branding-preferences-form.tsx index 9c8bc05ce..b835c653a 100644 --- a/apps/remix/app/components/forms/branding-preferences-form.tsx +++ b/apps/remix/app/components/forms/branding-preferences-form.tsx @@ -98,7 +98,7 @@ export function BrandingPreferencesForm({ ? `${NEXT_PUBLIC_WEBAPP_URL()}/api/branding/logo/team/${team?.id}` : `${NEXT_PUBLIC_WEBAPP_URL()}/api/branding/logo/organisation/${organisation?.id}`; - setPreviewUrl(logoUrl); + setPreviewUrl(logoUrl + '?v=' + Date.now()); setHasLoadedPreview(true); } } @@ -173,7 +173,7 @@ export function BrandingPreferencesForm({ />
- {!isBrandingEnabled &&
} + {!isBrandingEnabled &&
}
-
+
{previewUrl ? ( ) : ( -
+
Please upload a logo {!hasLoadedPreview && ( -
- +
+
)}
@@ -243,7 +243,7 @@ export function BrandingPreferencesForm({ type="button" variant="link" size="sm" - className="text-destructive text-xs" + className="text-xs text-destructive" onClick={() => { setPreviewUrl(''); onChange(null); diff --git a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx index 64f8511ed..8f5e47117 100644 --- a/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx +++ b/apps/remix/app/routes/_authenticated+/o.$orgUrl.settings.branding.tsx @@ -48,12 +48,17 @@ export default function OrganisationSettingsBrandingPage() { try { const { brandingEnabled, brandingLogo, brandingUrl, brandingCompanyDetails } = data; - let uploadedBrandingLogo: string | undefined = ''; + let uploadedBrandingLogo: string | undefined = undefined; if (brandingLogo) { uploadedBrandingLogo = JSON.stringify(await putFile(brandingLogo)); } + // Empty the branding logo if the user unsets it. + if (brandingLogo === null) { + uploadedBrandingLogo = ''; + } + await updateOrganisationSettings({ organisationId: organisation.id, data: { diff --git a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.branding.tsx b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.branding.tsx index a7317505f..9ffc05255 100644 --- a/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.branding.tsx +++ b/apps/remix/app/routes/_authenticated+/t.$teamUrl+/settings.branding.tsx @@ -34,12 +34,13 @@ export default function TeamsSettingsPage() { try { const { brandingEnabled, brandingLogo, brandingUrl, brandingCompanyDetails } = data; - let uploadedBrandingLogo = teamWithSettings?.teamSettings?.brandingLogo; + let uploadedBrandingLogo: string | undefined = undefined; if (brandingLogo) { uploadedBrandingLogo = JSON.stringify(await putFile(brandingLogo)); } + // Empty the branding logo if the user unsets it. if (brandingLogo === null) { uploadedBrandingLogo = ''; } @@ -48,7 +49,7 @@ export default function TeamsSettingsPage() { teamId: team.id, data: { brandingEnabled, - brandingLogo: uploadedBrandingLogo || null, + brandingLogo: uploadedBrandingLogo, brandingUrl: brandingUrl || null, brandingCompanyDetails: brandingCompanyDetails || null, }, diff --git a/apps/remix/app/routes/api+/branding.logo.organisation.$orgId.ts b/apps/remix/app/routes/api+/branding.logo.organisation.$orgId.ts index da6c8da1a..050bb4a6b 100644 --- a/apps/remix/app/routes/api+/branding.logo.organisation.$orgId.ts +++ b/apps/remix/app/routes/api+/branding.logo.organisation.$orgId.ts @@ -1,10 +1,13 @@ +import { sha256 } from '@documenso/lib/universal/crypto'; import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server'; import { loadLogo } from '@documenso/lib/utils/images/logo'; import { prisma } from '@documenso/prisma'; import type { Route } from './+types/branding.logo.organisation.$orgId'; -export async function loader({ params }: Route.LoaderArgs) { +const CACHE_CONTROL = 'public, max-age=0, stale-while-revalidate=86400'; + +export async function loader({ params, request }: Route.LoaderArgs) { const organisationId = params.orgId; if (!organisationId) { @@ -48,6 +51,18 @@ export async function loader({ params }: Route.LoaderArgs) { ); } + const etag = `"${Buffer.from(sha256(settings.brandingLogo)).toString('hex')}"`; + + if (request.headers.get('If-None-Match') === etag) { + return new Response(null, { + status: 304, + headers: { + ETag: etag, + 'Cache-Control': CACHE_CONTROL, + }, + }); + } + const file = await getFileServerSide(JSON.parse(settings.brandingLogo)).catch((e) => { console.error(e); }); @@ -68,8 +83,8 @@ export async function loader({ params }: Route.LoaderArgs) { headers: { 'Content-Type': contentType, 'Content-Length': content.length.toString(), - // Stale while revalidate for 1 hours to 24 hours - 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400', + 'Cache-Control': CACHE_CONTROL, + ETag: etag, }, }); } diff --git a/apps/remix/app/routes/api+/branding.logo.team.$teamId.ts b/apps/remix/app/routes/api+/branding.logo.team.$teamId.ts index 30a0e5b3c..923e8e0ca 100644 --- a/apps/remix/app/routes/api+/branding.logo.team.$teamId.ts +++ b/apps/remix/app/routes/api+/branding.logo.team.$teamId.ts @@ -1,10 +1,13 @@ import { getTeamSettings } from '@documenso/lib/server-only/team/get-team-settings'; +import { sha256 } from '@documenso/lib/universal/crypto'; import { getFileServerSide } from '@documenso/lib/universal/upload/get-file.server'; import { loadLogo } from '@documenso/lib/utils/images/logo'; import type { Route } from './+types/branding.logo.team.$teamId'; -export async function loader({ params }: Route.LoaderArgs) { +const CACHE_CONTROL = 'public, max-age=0, stale-while-revalidate=86400'; + +export async function loader({ params, request }: Route.LoaderArgs) { const teamId = Number(params.teamId); if (teamId === 0 || Number.isNaN(teamId)) { @@ -41,6 +44,18 @@ export async function loader({ params }: Route.LoaderArgs) { ); } + const etag = `"${Buffer.from(sha256(settings.brandingLogo)).toString('hex')}"`; + + if (request.headers.get('If-None-Match') === etag) { + return new Response(null, { + status: 304, + headers: { + ETag: etag, + 'Cache-Control': CACHE_CONTROL, + }, + }); + } + const file = await getFileServerSide(JSON.parse(settings.brandingLogo)).catch((e) => { console.error(e); }); @@ -61,8 +76,8 @@ export async function loader({ params }: Route.LoaderArgs) { headers: { 'Content-Type': contentType, 'Content-Length': content.length.toString(), - // Stale while revalidate for 1 hours to 24 hours - 'Cache-Control': 'public, s-maxage=3600, stale-while-revalidate=86400', + 'Cache-Control': CACHE_CONTROL, + ETag: etag, }, }); }