From 02f0df3a2e28a0ccc47b243b3d8031221ea23875 Mon Sep 17 00:00:00 2001 From: Mike Cao Date: Fri, 25 Apr 2025 13:59:23 -0700 Subject: [PATCH] Allow custom favicon URL. Closes #3365 --- next.config.js => next.config.mjs | 15 +++++++++------ src/components/common/Favicon.tsx | 8 ++++---- src/lib/constants.ts | 1 + 3 files changed, 14 insertions(+), 10 deletions(-) rename next.config.js => next.config.mjs (95%) diff --git a/next.config.js b/next.config.mjs similarity index 95% rename from next.config.js rename to next.config.mjs index 621c8b5b..ac8490a3 100644 --- a/next.config.js +++ b/next.config.mjs @@ -1,5 +1,9 @@ -/* eslint-disable @typescript-eslint/no-var-requires */ -require('dotenv').config(); +import dotenv from 'dotenv'; +import { createRequire } from 'module'; + +dotenv.config(); + +const require = createRequire(import.meta.url); const pkg = require('./package.json'); const TRACKER_SCRIPT = '/script.js'; @@ -12,6 +16,7 @@ const corsMaxAge = process.env.CORS_MAX_AGE; const defaultLocale = process.env.DEFAULT_LOCALE; const disableLogin = process.env.DISABLE_LOGIN; const disableUI = process.env.DISABLE_UI; +const faviconURL = process.env.FAVICON_URL; const forceSSL = process.env.FORCE_SSL; const frameAncestors = process.env.ALLOWED_FRAME_URLS; const privateMode = process.env.PRIVATE_MODE; @@ -180,17 +185,17 @@ if (cloudMode && cloudUrl) { } /** @type {import('next').NextConfig} */ -const config = { +export default { reactStrictMode: false, env: { basePath, cloudMode, cloudUrl, - configUrl: '/config', currentVersion: pkg.version, defaultLocale, disableLogin, disableUI, + faviconURL, privateMode, }, basePath, @@ -237,5 +242,3 @@ const config = { return [...redirects]; }, }; - -module.exports = config; diff --git a/src/components/common/Favicon.tsx b/src/components/common/Favicon.tsx index ea3f31aa..c02fe74f 100644 --- a/src/components/common/Favicon.tsx +++ b/src/components/common/Favicon.tsx @@ -1,4 +1,4 @@ -import { GROUPED_DOMAINS } from '@/lib/constants'; +import { FAVICON_URL, GROUPED_DOMAINS } from '@/lib/constants'; function getHostName(url: string) { const match = url.match(/^(?:https?:\/\/)?(?:[^@\n]+@)?([^:/\n?=]+)/im); @@ -10,10 +10,10 @@ export function Favicon({ domain, ...props }) { return null; } + const url = process.env.faviconURL || FAVICON_URL; const hostName = domain ? getHostName(domain) : null; - const src = hostName - ? `https://icons.duckduckgo.com/ip3/${GROUPED_DOMAINS[hostName]?.domain || hostName}.ico` - : null; + const domainName = GROUPED_DOMAINS[hostName]?.domain || hostName; + const src = hostName ? url.replace(/\{\{\s*domain\s*}}/, domainName) : null; return hostName ? : null; } diff --git a/src/lib/constants.ts b/src/lib/constants.ts index f2a42fa7..2060811f 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -12,6 +12,7 @@ export const HOMEPAGE_URL = 'https://umami.is'; export const REPO_URL = 'https://github.com/umami-software/umami'; export const UPDATES_URL = 'https://api.umami.is/v1/updates'; export const TELEMETRY_PIXEL = 'https://i.umami.is/a.png'; +export const FAVICON_URL = 'https://icons.duckduckgo.com/ip3/{{domain}}.ico'; export const DEFAULT_LOCALE = process.env.defaultLocale || 'en-US'; export const DEFAULT_THEME = 'light';