fix: remove hardcoded localhost URLs for production deployment
Test and Lint / backend-test (push) Successful in 1m11s
continuous-integration/drone/push Build is passing
Test and Lint / frontend-test (push) Successful in 2m17s
Version and Release / version-bump (push) Successful in 43s
Version and Release / trigger-drone (push) Successful in 3s

- Add URL utility functions for building resource URLs
- Update all components to use relative URLs in production
- Add production deployment documentation
- Update nginx config to proxy all required endpoints
- Add .env.production.example with proper configuration
This commit is contained in:
2025-07-13 21:14:12 +02:00
parent 0934695a69
commit a3638fe954
16 changed files with 201 additions and 19 deletions
+2 -1
View File
@@ -3,6 +3,7 @@ import { AlertTriangle } from 'lucide-react';
import { useQuery } from '@tanstack/react-query';
import { useTranslation } from 'react-i18next';
import { api } from '../config/api';
import { buildResourceUrl } from '../utils/url';
interface BrandingSettings {
branding_company_name?: string;
@@ -50,7 +51,7 @@ export const MaintenanceMode: React.FC = () => {
src={settings?.branding_logo_url ?
(settings.branding_logo_url.startsWith('http')
? settings.branding_logo_url
: `${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${settings.branding_logo_url}`)
: buildResourceUrl(settings.branding_logo_url))
: '/picpeak-logo-transparent.png'
}
alt={settings?.branding_company_name || 'PicPeak'}
@@ -4,6 +4,7 @@ import { Button, Card, Input } from '../common';
import { GALLERY_THEME_PRESETS, type ThemeConfig } from '../../contexts/ThemeContext';
import { settingsService } from '../../services/settings.service';
import { toast } from 'react-toastify';
import { buildResourceUrl } from '../../utils/url';
interface ThemeCustomizerProps {
value: ThemeConfig;
@@ -269,7 +270,7 @@ export const ThemeCustomizer: React.FC<ThemeCustomizerProps> = ({
<div className="flex items-center gap-4">
{localTheme.logoUrl && (
<img
src={localTheme.logoUrl.startsWith('http') ? localTheme.logoUrl : `${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${localTheme.logoUrl}`}
src={localTheme.logoUrl.startsWith('http') ? localTheme.logoUrl : buildResourceUrl(localTheme.logoUrl)}
alt="Custom logo"
className="h-16 w-auto object-contain"
/>
@@ -1,5 +1,6 @@
import React, { useState, useEffect } from 'react';
import { getAuthToken } from '../../config/api';
import { buildResourceUrl } from '../../utils/url';
interface AuthenticatedImageProps extends React.ImgHTMLAttributes<HTMLImageElement> {
src: string;
@@ -60,9 +61,8 @@ export const AuthenticatedImage: React.FC<AuthenticatedImageProps> = ({
// Use the src as-is since it should already be the correct endpoint
let imageUrl = src;
// Prepend API URL for absolute paths
const apiUrl = import.meta.env.VITE_API_URL || 'http://localhost:3001';
const fullImageUrl = imageUrl.startsWith('/') ? `${apiUrl}${imageUrl}` : imageUrl;
// Build full URL for the image
const fullImageUrl = imageUrl.startsWith('/') ? buildResourceUrl(imageUrl) : imageUrl;
console.log('Fetching authenticated image:', fullImageUrl);
const response = await fetch(fullImageUrl, {
@@ -1,12 +1,13 @@
import { useEffect } from 'react';
import { useQuery } from '@tanstack/react-query';
import { getApiBaseUrl, buildResourceUrl } from '../../utils/url';
export const DynamicFavicon: React.FC = () => {
const { data: settings } = useQuery({
queryKey: ['public-settings'],
queryFn: async () => {
try {
const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}/api/public/settings`);
const response = await fetch(`${getApiBaseUrl()}/public/settings`);
if (response.ok) {
return response.json();
}
@@ -30,7 +31,7 @@ export const DynamicFavicon: React.FC = () => {
link.type = 'image/png';
link.href = settings.branding_favicon_url.startsWith('http')
? settings.branding_favicon_url
: `${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${settings.branding_favicon_url}`;
: buildResourceUrl(settings.branding_favicon_url);
document.head.appendChild(link);
}
+2 -1
View File
@@ -1,6 +1,7 @@
import React, { useEffect, useState } from 'react';
import ReCAPTCHA from 'react-google-recaptcha';
import { useQuery } from '@tanstack/react-query';
import { getApiBaseUrl } from '../../utils/url';
interface ReCaptchaProps {
onChange: (token: string | null) => void;
@@ -20,7 +21,7 @@ export const ReCaptcha: React.FC<ReCaptchaProps> = ({
const { data: settings } = useQuery({
queryKey: ['public-settings'],
queryFn: async () => {
const response = await fetch(`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}/api/public/settings`);
const response = await fetch(`${getApiBaseUrl()}/public/settings`);
return response.json();
},
staleTime: 5 * 60 * 1000, // Cache for 5 minutes
@@ -7,6 +7,7 @@ import { useLocalizedDate } from '../../hooks/useLocalizedDate';
import { Button } from '../common';
import { DynamicFavicon } from '../common/DynamicFavicon';
import { useTheme } from '../../contexts/ThemeContext';
import { buildResourceUrl } from '../../utils/url';
interface GalleryLayoutProps {
event: {
@@ -122,7 +123,7 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
<div className="flex-shrink-0">
<img
src={brandingSettings?.logo_url ?
`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${brandingSettings.logo_url}` :
buildResourceUrl(brandingSettings.logo_url) :
'/picpeak-logo-transparent.png'
}
alt={brandingSettings?.company_name || 'PicPeak'}
@@ -277,7 +278,7 @@ export const GalleryLayout: React.FC<GalleryLayoutProps> = ({
<div className="mb-6">
<img
src={brandingSettings?.logo_url ?
`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${brandingSettings.logo_url}` :
buildResourceUrl(brandingSettings.logo_url) :
'/picpeak-logo-transparent.png'
}
alt={brandingSettings?.company_name || 'PicPeak'}
@@ -7,6 +7,7 @@ import { useTheme } from '../../../contexts/ThemeContext';
import { AuthenticatedImage } from '../../common';
import type { BaseGalleryLayoutProps } from './BaseGalleryLayout';
import type { Photo } from '../../../types';
import { buildResourceUrl } from '../../../utils/url';
interface HeroGalleryLayoutProps extends BaseGalleryLayoutProps {
eventName?: string;
@@ -97,7 +98,7 @@ export const HeroGalleryLayout: React.FC<HeroGalleryLayoutProps> = ({
<div className="mb-6">
<img
src={eventLogo ?
`${import.meta.env.VITE_API_URL || 'http://localhost:3001'}${eventLogo}` :
buildResourceUrl(eventLogo) :
'/picpeak-logo-transparent.png'
}
alt="Event logo"