From 185d054e925669312347b0bd0291ca2b58d7b24e Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:56:38 +0300 Subject: [PATCH 01/14] Add reliable storage image URL helper --- frontend/client/helpers/storageImageUrl.js | 26 ++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 frontend/client/helpers/storageImageUrl.js diff --git a/frontend/client/helpers/storageImageUrl.js b/frontend/client/helpers/storageImageUrl.js new file mode 100644 index 00000000..d7100aff --- /dev/null +++ b/frontend/client/helpers/storageImageUrl.js @@ -0,0 +1,26 @@ +import config from '../config'; + +const normalizeApiBase = () => String(config.apiBaseUrl || '/api').replace(/\/+$/, ''); + +const isStorageProxyUrl = (value = '') => + String(value || '').includes('/storage/image?url='); + +const isStoredUploadUrl = (value = '') => { + const raw = String(value || '').trim(); + if (!raw || raw.startsWith('data:') || raw.startsWith('blob:')) return false; + + try { + const parsed = new URL(raw, window.location.origin); + return /(?:^|\/)uploads\//i.test(parsed.pathname); + } catch (error0) { + return /(?:^|\/)uploads\//i.test(raw); + } +}; + +const toStorageImageProxyUrl = (value = '') => { + const raw = String(value || '').trim(); + if (!raw || !isStoredUploadUrl(raw) || isStorageProxyUrl(raw)) return ''; + return `${normalizeApiBase()}/storage/image?url=${encodeURIComponent(raw)}`; +}; + +export { isStoredUploadUrl, isStorageProxyUrl, toStorageImageProxyUrl }; From 69754aba62eab86dc3f284ab960712364db5b39f Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:56:49 +0300 Subject: [PATCH 02/14] Drive favicon from runtime app config --- frontend/client/helpers/runtimeBranding.js | 42 ++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 frontend/client/helpers/runtimeBranding.js diff --git a/frontend/client/helpers/runtimeBranding.js b/frontend/client/helpers/runtimeBranding.js new file mode 100644 index 00000000..5ac24f3e --- /dev/null +++ b/frontend/client/helpers/runtimeBranding.js @@ -0,0 +1,42 @@ +import axios from 'axios'; +import config from '../config'; +import { toStorageImageProxyUrl } from './storageImageUrl'; + +const setIconHref = (href) => { + const value = String(href || '').trim() || '/pwa-192x192.png'; + let icon = document.querySelector('link[rel="icon"]'); + if (!icon) { + icon = document.createElement('link'); + icon.setAttribute('rel', 'icon'); + document.head.appendChild(icon); + } + icon.setAttribute('type', 'image/png'); + icon.setAttribute('href', value); +}; + +const applyRuntimeBranding = (payload = {}) => { + if (payload.appName) { + config.brandName = String(payload.appName || config.brandName); + document.title = config.brandName; + } + + const appLogo = String(payload.appLogo || '').trim(); + if (appLogo) { + config.brandLogo = appLogo; + setIconHref(toStorageImageProxyUrl(appLogo) || appLogo); + } else { + setIconHref('/pwa-192x192.png'); + } +}; + +const installRuntimeBranding = () => { + if (typeof window === 'undefined' || typeof document === 'undefined') return; + + axios + .get('/app-config', { baseURL: config.apiBaseUrl }) + .then(({ data }) => applyRuntimeBranding(data?.payload || {})) + .catch(() => setIconHref('/pwa-192x192.png')); +}; + +export { applyRuntimeBranding }; +export default installRuntimeBranding; From 1871703b28f66a1ecf4e2de1bc0747d5964a92d4 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:57:08 +0300 Subject: [PATCH 03/14] Retry failed uploaded images through FTP proxy --- frontend/client/helpers/imageFallbacks.js | 29 +++++++++++++++++++---- 1 file changed, 24 insertions(+), 5 deletions(-) diff --git a/frontend/client/helpers/imageFallbacks.js b/frontend/client/helpers/imageFallbacks.js index 6ffec662..d4c83ff1 100644 --- a/frontend/client/helpers/imageFallbacks.js +++ b/frontend/client/helpers/imageFallbacks.js @@ -1,3 +1,5 @@ +import { toStorageImageProxyUrl } from './storageImageUrl'; + const FALLBACKS = [ { match: /(?:default-group-avatar\.png|\/assets\/icons\/group-avatar\.svg)(?:[?#].*)?$/i, @@ -20,6 +22,7 @@ const getLegacyFallback = (img) => { }; const getErrorFallback = (img) => { + const original = String(img?.dataset?.syncchatOriginalSrc || '').trim(); const legacyFallback = getLegacyFallback(img); if (legacyFallback) return legacyFallback; @@ -28,15 +31,22 @@ const getErrorFallback = (img) => { .toLowerCase(); if (alt === 'syncchat') return '/pwa-192x192.png'; + if (/(?:^|\/)uploads\/avatars\//i.test(original)) { + return '/assets/icons/user-avatar.svg'; + } + return ''; }; const bindImage = (img) => { if (!(img instanceof HTMLImageElement)) return; - const legacyFallback = getLegacyFallback(img); const current = String(img.getAttribute('src') || ''); + if (current && !img.dataset.syncchatOriginalSrc) { + img.dataset.syncchatOriginalSrc = current; + } + const legacyFallback = getLegacyFallback(img); if ( legacyFallback && current && @@ -50,11 +60,20 @@ const bindImage = (img) => { img.dataset.syncchatFallbackBound = '1'; img.addEventListener('error', () => { - const fallback = getErrorFallback(img); - if (!fallback) return; + const active = String(img.getAttribute('src') || '').trim(); + const original = String(img.dataset.syncchatOriginalSrc || active).trim(); + + if (img.dataset.syncchatStorageProxyTried !== '1') { + const proxyUrl = toStorageImageProxyUrl(original); + if (proxyUrl && proxyUrl !== active) { + img.dataset.syncchatStorageProxyTried = '1'; + img.src = proxyUrl; + return; + } + } - const active = String(img.getAttribute('src') || ''); - if (active.endsWith(fallback)) return; + const fallback = getErrorFallback(img); + if (!fallback || active.endsWith(fallback)) return; img.src = fallback; }); }; From 9e63a7b9e98c4bfb3a66b2e48a6918b5a0826148 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:57:28 +0300 Subject: [PATCH 04/14] Add safe public FTP image proxy --- backend/server/controllers/storagePublic.js | 111 ++++++++++++++++++++ 1 file changed, 111 insertions(+) create mode 100644 backend/server/controllers/storagePublic.js diff --git a/backend/server/controllers/storagePublic.js b/backend/server/controllers/storagePublic.js new file mode 100644 index 00000000..2ec3b070 --- /dev/null +++ b/backend/server/controllers/storagePublic.js @@ -0,0 +1,111 @@ +const path = require('path'); +const { getStorageConfig } = require('../helpers/storageConfig'); +const { readStorageFileToBuffer } = require('../helpers/storage'); + +const IMAGE_TYPES = new Map([ + ['.png', 'image/png'], + ['.jpg', 'image/jpeg'], + ['.jpeg', 'image/jpeg'], + ['.webp', 'image/webp'], + ['.gif', 'image/gif'], + ['.svg', 'image/svg+xml'], + ['.ico', 'image/x-icon'], + ['.avif', 'image/avif'], +]); + +const safePathname = (value = '') => { + const raw = String(value || '').trim(); + if (!raw) return ''; + try { + return decodeURIComponent(new URL(raw, 'https://syncchat.invalid').pathname); + } catch (error0) { + return ''; + } +}; + +const resolveConfiguredStorageUrl = async (rawUrl = '') => { + const config = await getStorageConfig(); + if (!config.enabled || !config.publicBaseUrl) { + const error = new Error('Image storage is not configured'); + error.statusCode = 503; + throw error; + } + + const raw = String(rawUrl || '').trim(); + if (!raw) { + const error = new Error('Image URL is required'); + error.statusCode = 400; + throw error; + } + + const base = config.publicBaseUrl.replace(/\/+$/, ''); + if (raw === base || raw.startsWith(`${base}/`)) return raw; + + const pathname = safePathname(raw); + if (!pathname || pathname.includes('..')) { + const error = new Error('Invalid image URL'); + error.statusCode = 400; + throw error; + } + + let basePathname = ''; + try { + basePathname = new URL(base).pathname.replace(/\/+$/, ''); + } catch (error0) { + basePathname = ''; + } + + let relative = ''; + if (basePathname && pathname.startsWith(`${basePathname}/`)) { + relative = pathname.slice(basePathname.length + 1); + } else { + const marker = '/uploads/'; + const index = pathname.toLowerCase().indexOf(marker); + if (index >= 0) relative = pathname.slice(index + marker.length); + } + + relative = String(relative || '').replace(/^\/+/, ''); + if (!relative || relative.includes('..')) { + const error = new Error('Image URL does not belong to configured storage'); + error.statusCode = 400; + throw error; + } + + return `${base}/${relative}`; +}; + +exports.image = async (req, res) => { + try { + const storageUrl = await resolveConfiguredStorageUrl(req.query?.url); + const extension = path.extname(safePathname(storageUrl)).toLowerCase(); + const contentType = IMAGE_TYPES.get(extension); + if (!contentType) { + res.status(415).json({ success: false, message: 'Unsupported image type' }); + return; + } + + const buffer = await readStorageFileToBuffer(storageUrl); + if (!buffer?.length) { + res.status(404).json({ success: false, message: 'Image not found' }); + return; + } + + const maxBytes = 20 * 1024 * 1024; + if (buffer.length > maxBytes) { + res.status(413).json({ success: false, message: 'Image is too large to proxy' }); + return; + } + + res.setHeader('Content-Type', contentType); + res.setHeader('Content-Length', String(buffer.length)); + res.setHeader('Cache-Control', 'public, max-age=3600, stale-while-revalidate=86400'); + res.status(200).send(buffer); + } catch (error0) { + res.status(error0.statusCode || 404).json({ + success: false, + message: error0.message || 'Image could not be loaded', + }); + } +}; + +module.exports.resolveConfiguredStorageUrl = resolveConfiguredStorageUrl; From ea1b11bed94b3d3a4bca7d398e1d1213e111881e Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:57:39 +0300 Subject: [PATCH 05/14] Expose public storage image fallback route --- backend/server/routes/storagePublic.js | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 backend/server/routes/storagePublic.js diff --git a/backend/server/routes/storagePublic.js b/backend/server/routes/storagePublic.js new file mode 100644 index 00000000..98853a47 --- /dev/null +++ b/backend/server/routes/storagePublic.js @@ -0,0 +1,6 @@ +const router = require('express').Router(); +const controller = require('../controllers/storagePublic'); + +router.get('/storage/image', controller.image); + +module.exports = router; From 2467ea848da31d2026386f149538155df962bb9a Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:57:56 +0300 Subject: [PATCH 06/14] Mount public storage image fallback route --- backend/server/routes/index.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/backend/server/routes/index.js b/backend/server/routes/index.js index 9ebb932f..c49a1008 100644 --- a/backend/server/routes/index.js +++ b/backend/server/routes/index.js @@ -33,6 +33,7 @@ const report = require('./report'); const contentControls = require('./contentControls'); const appConfig = require('./appConfig'); const callingConfig = require('./callingConfig'); +const storagePublic = require('./storagePublic'); const storageAdmin = require('./storageAdmin'); const callingAdmin = require('./callingAdmin'); const callingPushAdmin = require('./callingPushAdmin'); @@ -58,6 +59,7 @@ router.use(report); router.use(contentControls); router.use(appConfig); router.use(callingConfig); +router.use(storagePublic); router.use(storageAdmin); router.use(callingAdmin); router.use(callingPushAdmin); From 0317c1dc0d2cd71e09b280fb35b9da8b45568a65 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:58:11 +0300 Subject: [PATCH 07/14] Initialize app-config branding at runtime --- frontend/client/index.jsx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/frontend/client/index.jsx b/frontend/client/index.jsx index c23c157c..9dd17798 100644 --- a/frontend/client/index.jsx +++ b/frontend/client/index.jsx @@ -16,6 +16,7 @@ import installChatHttpReliability from './helpers/chatHttpReliability'; import installMentionAutocompleteV2 from './helpers/mentionAutocompleteV2'; import installChatToolsSettingsBridge from './helpers/chatToolsSettingsBridge'; import installImageFallbacks from './helpers/imageFallbacks'; +import installRuntimeBranding from './helpers/runtimeBranding'; import './styles/chatToolsTheme.css'; import './styles/layoutFixes.css'; @@ -43,6 +44,7 @@ installChatDraftV2(); installMentionAutocompleteV2(); installChatToolsSettingsBridge(); installImageFallbacks(); +installRuntimeBranding(); registerServiceWorker(); requestNotificationPermission(); From 426543bcc0d0ac323ae75353ce149833f07913b5 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:58:24 +0300 Subject: [PATCH 08/14] Restore local favicon bootstrap fallback --- frontend/client/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/client/index.html b/frontend/client/index.html index 93c6c6ea..3df51c1b 100644 --- a/frontend/client/index.html +++ b/frontend/client/index.html @@ -8,7 +8,7 @@ - + From 9f30dbb0fadbac807c3c7e16bc373fa54dd78c31 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:58:37 +0300 Subject: [PATCH 09/14] Remove hardcoded remote admin favicon --- frontend/admin/index.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/admin/index.html b/frontend/admin/index.html index 7d01aa70..98eba4a0 100644 --- a/frontend/admin/index.html +++ b/frontend/admin/index.html @@ -5,7 +5,7 @@ - + SyncChat Admin From 964c426fdff674893931701c3df879dd077ab8d2 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:58:53 +0300 Subject: [PATCH 10/14] Remove hardcoded social auth favicon host --- frontend/admin/socialAuth.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/admin/socialAuth.html b/frontend/admin/socialAuth.html index 4224b047..14399eff 100644 --- a/frontend/admin/socialAuth.html +++ b/frontend/admin/socialAuth.html @@ -5,7 +5,7 @@ - + SyncChat Admin — Social Login From f379380001760f3d86f7ffcf67c9fcd668ad381c Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:59:11 +0300 Subject: [PATCH 11/14] Stop redirecting brand assets to stale upload host --- vercel.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/vercel.json b/vercel.json index d8df1494..7e40086c 100644 --- a/vercel.json +++ b/vercel.json @@ -6,15 +6,15 @@ "devCommand": "npm run dev --prefix frontend", "outputDirectory": "frontend/client/public", "redirects": [ - { "source": "/favicon.ico", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, - { "source": "/favicon.png", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, - { "source": "/assets/images/favicon.png", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, + { "source": "/favicon.ico", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/favicon.png", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/assets/images/favicon.png", "destination": "/pwa-192x192.png", "permanent": false }, { "source": "/default-avatar.png", "destination": "/assets/icons/user-avatar.svg", "permanent": false }, { "source": "/assets/images/default-avatar.png", "destination": "/assets/icons/user-avatar.svg", "permanent": false }, { "source": "/default-group-avatar.png", "destination": "/assets/icons/group-avatar.svg", "permanent": false }, { "source": "/assets/images/default-group-avatar.png", "destination": "/assets/icons/group-avatar.svg", "permanent": false }, - { "source": "/error.ico", "destination": "https://syncchat.smshagor.com/uploads/error.ico", "permanent": false }, - { "source": "/assets/images/error.ico", "destination": "https://syncchat.smshagor.com/uploads/error.ico", "permanent": false }, + { "source": "/error.ico", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/assets/images/error.ico", "destination": "/pwa-192x192.png", "permanent": false }, { "source": "/assets/images/default-channel-avatar.png", "destination": "/assets/icons/channel-avatar.svg", "permanent": false } ], "rewrites": [ From 758c86c82cc3eab4d0d764bf834a6e72799947cb Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:59:27 +0300 Subject: [PATCH 12/14] Keep frontend fallback assets local --- frontend/vercel.json | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/frontend/vercel.json b/frontend/vercel.json index afd15d32..11315777 100644 --- a/frontend/vercel.json +++ b/frontend/vercel.json @@ -4,15 +4,15 @@ "buildCommand": "npm run build", "outputDirectory": "client/public", "redirects": [ - { "source": "/favicon.ico", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, - { "source": "/favicon.png", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, - { "source": "/assets/images/favicon.png", "destination": "https://syncchat.smshagor.com/uploads/favicon.png", "permanent": false }, + { "source": "/favicon.ico", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/favicon.png", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/assets/images/favicon.png", "destination": "/pwa-192x192.png", "permanent": false }, { "source": "/default-avatar.png", "destination": "/assets/icons/user-avatar.svg", "permanent": false }, { "source": "/assets/images/default-avatar.png", "destination": "/assets/icons/user-avatar.svg", "permanent": false }, { "source": "/default-group-avatar.png", "destination": "/assets/icons/group-avatar.svg", "permanent": false }, { "source": "/assets/images/default-group-avatar.png", "destination": "/assets/icons/group-avatar.svg", "permanent": false }, - { "source": "/error.ico", "destination": "https://syncchat.smshagor.com/uploads/error.ico", "permanent": false }, - { "source": "/assets/images/error.ico", "destination": "https://syncchat.smshagor.com/uploads/error.ico", "permanent": false }, + { "source": "/error.ico", "destination": "/pwa-192x192.png", "permanent": false }, + { "source": "/assets/images/error.ico", "destination": "/pwa-192x192.png", "permanent": false }, { "source": "/assets/images/default-channel-avatar.png", "destination": "/assets/icons/channel-avatar.svg", "permanent": false } ], "rewrites": [ From 1c6f298b413b52447552b168a77ea225167d0fb9 Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 16:59:49 +0300 Subject: [PATCH 13/14] Validate storage image fallback in backend build --- backend/package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/backend/package.json b/backend/package.json index 57cddbaa..cdd09d47 100644 --- a/backend/package.json +++ b/backend/package.json @@ -6,7 +6,7 @@ "scripts": { "predev": "node scripts/free-ports.js 8080", "dev": "nodemon --watch server --watch .env --ignore logs --ignore uploads server", - "build": "node --check server/index.js && node --check server/server.js && node --check server/bootstrap.js && node --check server/config.js && node --check server/db/sequelize.js && node --check server/db/connect.js && node --check server/db/models/inbox.js && node --check server/db/models/chat.js && node --check server/db/models/chatRoomCounter.js && node --check server/db/models/messageReceipt.js && node --check server/db/models/chatDraft.js && node --check server/db/models/messageRequest.js && node --check server/db/models/chatTopic.js && node --check server/db/models/e2eeDeviceKey.js && node --check server/db/models/resumableUpload.js && node --check server/db/models/chatAiConfig.js && node --check server/db/models/adminSocialAuthConfig.js && node --check server/db/models/profile.js && node --check server/db/models/group.js && node --check server/db/models/channel.js && node --check server/db/models/adminStorageConfig.js && node --check server/db/models/adminCallConfig.js && node --check server/db/models/callHistory.js && node --check server/db/models/profilePhoto.js && node --check server/db/models/nativePushDevice.js && node --check server/helpers/storageConfig.js && node --check server/helpers/callConfig.js && node --check server/helpers/callHistory.js && node --check server/helpers/callState.js && node --check server/helpers/avatarDefaults.js && node --check server/helpers/ensureAvatarDefaults.js && node --check server/helpers/ensureProfile.js && node --check server/helpers/privacy.js && node --check server/helpers/profilePhotos.js && node --check server/helpers/livekit.js && node --check server/helpers/nativePushConfig.js && node --check server/helpers/nativePush.js && node --check server/helpers/storage.js && node --check server/helpers/videoPipeline.js && node --check server/helpers/accountArchive.js && node --check server/helpers/accountExport.js && node --check server/helpers/logger.js && node --check server/helpers/chatAbuse.js && node --check server/helpers/chatMentions.js && node --check server/helpers/messageRequests.js && node --check server/helpers/chatReliability.js && node --check server/helpers/chatAiConfig.js && node --check server/helpers/socialAuthConfig.js && node --check server/helpers/e2eeKeyDirectory.js && node --check server/helpers/chatMaintenance.js && node --check server/middleware/chatSendIdempotency.js && node --check server/middleware/roomAccess.js && node --check server/controllers/avatar.js && node --check server/controllers/profile.js && node --check server/controllers/chatUpload.js && node --check server/controllers/chatSecurity.js && node --check server/controllers/chatDeletion.js && node --check server/controllers/chatV2.js && node --check server/controllers/chatAiAdmin.js && node --check server/controllers/chatSuggestions.js && node --check server/controllers/chatResumableUpload.js && node --check server/controllers/socialAuth.js && node --check server/controllers/socialAuthAdmin.js && node --check server/controllers/storageAdmin.js && node --check server/controllers/callingAdmin.js && node --check server/controllers/callingPushAdmin.js && node --check server/controllers/callingConfig.js && node --check server/controllers/nativePush.js && node --check server/controllers/accountStorage.js && node --check server/routes/profile.js && node --check server/routes/inbox.js && node --check server/routes/chat.js && node --check server/routes/chatV2.js && node --check server/routes/chatAiAdmin.js && node --check server/routes/socialAuthAdmin.js && node --check server/routes/storageAdmin.js && node --check server/routes/callingAdmin.js && node --check server/routes/callingPushAdmin.js && node --check server/routes/callingConfig.js && node --check server/routes/setting.js && node --check server/helpers/socketAdapter.js && node --check server/socket/auth.js && node --check server/socket/index.js && node --check server/socket/events/chatV2.js && node --check server/socket/events/calling.js && node --check server/socket/events/callRealtime.js && node --check server/socket/events/backgroundCalling.js && node --check server/socket/events/groupModeration.js && node --check server/socket/events/room.js && node --check server/routes/cron.js && node --check api/index.js", + "build": "node --check server/index.js && node --check server/server.js && node --check server/bootstrap.js && node --check server/config.js && node --check server/db/sequelize.js && node --check server/db/connect.js && node --check server/db/models/inbox.js && node --check server/db/models/chat.js && node --check server/db/models/chatRoomCounter.js && node --check server/db/models/messageReceipt.js && node --check server/db/models/chatDraft.js && node --check server/db/models/messageRequest.js && node --check server/db/models/chatTopic.js && node --check server/db/models/e2eeDeviceKey.js && node --check server/db/models/resumableUpload.js && node --check server/db/models/chatAiConfig.js && node --check server/db/models/adminSocialAuthConfig.js && node --check server/db/models/profile.js && node --check server/db/models/group.js && node --check server/db/models/channel.js && node --check server/db/models/adminStorageConfig.js && node --check server/db/models/adminCallConfig.js && node --check server/db/models/callHistory.js && node --check server/db/models/profilePhoto.js && node --check server/db/models/nativePushDevice.js && node --check server/helpers/storageConfig.js && node --check server/helpers/callConfig.js && node --check server/helpers/callHistory.js && node --check server/helpers/callState.js && node --check server/helpers/avatarDefaults.js && node --check server/helpers/ensureAvatarDefaults.js && node --check server/helpers/ensureProfile.js && node --check server/helpers/privacy.js && node --check server/helpers/profilePhotos.js && node --check server/helpers/livekit.js && node --check server/helpers/nativePushConfig.js && node --check server/helpers/nativePush.js && node --check server/helpers/storage.js && node --check server/helpers/videoPipeline.js && node --check server/helpers/accountArchive.js && node --check server/helpers/accountExport.js && node --check server/helpers/logger.js && node --check server/helpers/chatAbuse.js && node --check server/helpers/chatMentions.js && node --check server/helpers/messageRequests.js && node --check server/helpers/chatReliability.js && node --check server/helpers/chatAiConfig.js && node --check server/helpers/socialAuthConfig.js && node --check server/helpers/e2eeKeyDirectory.js && node --check server/helpers/chatMaintenance.js && node --check server/middleware/chatSendIdempotency.js && node --check server/middleware/roomAccess.js && node --check server/controllers/avatar.js && node --check server/controllers/profile.js && node --check server/controllers/chatUpload.js && node --check server/controllers/chatSecurity.js && node --check server/controllers/chatDeletion.js && node --check server/controllers/chatV2.js && node --check server/controllers/chatAiAdmin.js && node --check server/controllers/chatSuggestions.js && node --check server/controllers/chatResumableUpload.js && node --check server/controllers/socialAuth.js && node --check server/controllers/socialAuthAdmin.js && node --check server/controllers/storagePublic.js && node --check server/controllers/storageAdmin.js && node --check server/controllers/callingAdmin.js && node --check server/controllers/callingPushAdmin.js && node --check server/controllers/callingConfig.js && node --check server/controllers/nativePush.js && node --check server/controllers/accountStorage.js && node --check server/routes/profile.js && node --check server/routes/inbox.js && node --check server/routes/chat.js && node --check server/routes/chatV2.js && node --check server/routes/chatAiAdmin.js && node --check server/routes/socialAuthAdmin.js && node --check server/routes/storagePublic.js && node --check server/routes/storageAdmin.js && node --check server/routes/callingAdmin.js && node --check server/routes/callingPushAdmin.js && node --check server/routes/callingConfig.js && node --check server/routes/setting.js && node --check server/helpers/socketAdapter.js && node --check server/socket/auth.js && node --check server/socket/index.js && node --check server/socket/events/chatV2.js && node --check server/socket/events/calling.js && node --check server/socket/events/callRealtime.js && node --check server/socket/events/backgroundCalling.js && node --check server/socket/events/groupModeration.js && node --check server/socket/events/room.js && node --check server/routes/cron.js && node --check api/index.js", "start": "node server" }, "dependencies": { From e361e4de907d5a5661284a37cc6d956e33455ecd Mon Sep 17 00:00:00 2001 From: Shahanur Islam Shagor Date: Wed, 19 Aug 2026 17:01:13 +0300 Subject: [PATCH 14/14] Allow any configured logo format as favicon --- frontend/client/helpers/runtimeBranding.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/client/helpers/runtimeBranding.js b/frontend/client/helpers/runtimeBranding.js index 5ac24f3e..6fd14efe 100644 --- a/frontend/client/helpers/runtimeBranding.js +++ b/frontend/client/helpers/runtimeBranding.js @@ -10,7 +10,7 @@ const setIconHref = (href) => { icon.setAttribute('rel', 'icon'); document.head.appendChild(icon); } - icon.setAttribute('type', 'image/png'); + icon.removeAttribute('type'); icon.setAttribute('href', value); };