Fix double /api/ prefix in API endpoint calls
When VITE_API_URL=/api, axios baseURL is already set to /api. However, all endpoint calls included the /api/ prefix, creating double paths like /api/api/auth/login/. Removed /api/ prefix from 81 API endpoint calls across 22 files: - src/api/auth.ts - Fixed login, logout, me, refresh, hijack endpoints - src/api/client.ts - Fixed token refresh endpoint - src/api/profile.ts - Fixed all profile, email, password, MFA, sessions endpoints - src/hooks/*.ts - Fixed all remaining API calls (users, appointments, resources, etc) - src/pages/*.tsx - Fixed signup and email verification endpoints This ensures API requests use the correct path: /api/auth/login/ instead of /api/api/auth/login/ 🤖 Generated with Claude Code Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,19 +2,19 @@
|
||||
* Cookie utilities for cross-subdomain token storage
|
||||
*/
|
||||
|
||||
import { getCookieDomain } from './domain';
|
||||
|
||||
/**
|
||||
* Set a cookie with domain attribute for cross-subdomain access
|
||||
* Uses .lvh.me for local development (lvh.me supports subdomains, unlike localhost)
|
||||
* Dynamically determines the correct domain based on current environment
|
||||
*/
|
||||
export const setCookie = (name: string, value: string, days: number = 7) => {
|
||||
const expires = new Date();
|
||||
expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000);
|
||||
|
||||
// Set cookie with domain=.lvh.me for local dev, accessible across all subdomains
|
||||
// For localhost, don't set domain attribute - let it default to current host
|
||||
const hostname = window.location.hostname;
|
||||
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';
|
||||
const domainAttr = hostname.includes('lvh.me') ? `;domain=.lvh.me` : isLocalhost ? '' : `;domain=${hostname}`;
|
||||
// Get cookie domain dynamically (.lvh.me in dev, .smoothschedule.com in prod, localhost for localhost)
|
||||
const cookieDomain = getCookieDomain();
|
||||
const domainAttr = cookieDomain === 'localhost' ? '' : `;domain=${cookieDomain}`;
|
||||
|
||||
document.cookie = `${name}=${value};expires=${expires.toUTCString()}${domainAttr};path=/;SameSite=Lax`;
|
||||
};
|
||||
@@ -39,8 +39,7 @@ export const getCookie = (name: string): string | null => {
|
||||
* Delete a cookie
|
||||
*/
|
||||
export const deleteCookie = (name: string) => {
|
||||
const hostname = window.location.hostname;
|
||||
const isLocalhost = hostname === 'localhost' || hostname === '127.0.0.1';
|
||||
const domainAttr = hostname.includes('lvh.me') ? `;domain=.lvh.me` : isLocalhost ? '' : `;domain=${hostname}`;
|
||||
const cookieDomain = getCookieDomain();
|
||||
const domainAttr = cookieDomain === 'localhost' ? '' : `;domain=${cookieDomain}`;
|
||||
document.cookie = `${name}=;expires=Thu, 01 Jan 1970 00:00:00 UTC${domainAttr};path=/;`;
|
||||
};
|
||||
|
||||
137
frontend/src/utils/domain.ts
Normal file
137
frontend/src/utils/domain.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
/**
|
||||
* Domain Utility Functions
|
||||
* Provides dynamic domain detection for both development (lvh.me) and production environments
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get the base domain from the current hostname
|
||||
* Examples:
|
||||
* - platform.lvh.me:5173 → lvh.me
|
||||
* - demo.smoothschedule.com → smoothschedule.com
|
||||
* - localhost → localhost
|
||||
*/
|
||||
export const getBaseDomain = (): string => {
|
||||
const hostname = window.location.hostname;
|
||||
|
||||
// Handle localhost
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||
return 'localhost';
|
||||
}
|
||||
|
||||
// Extract base domain from hostname
|
||||
const parts = hostname.split('.');
|
||||
|
||||
// If only 2 parts, it's already the base domain
|
||||
if (parts.length === 2) {
|
||||
return hostname;
|
||||
}
|
||||
|
||||
// Otherwise, take the last 2 parts (e.g., smoothschedule.com from platform.smoothschedule.com)
|
||||
return parts.slice(-2).join('.');
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the current subdomain (if any)
|
||||
* Examples:
|
||||
* - platform.lvh.me → platform
|
||||
* - demo.smoothschedule.com → demo
|
||||
* - smoothschedule.com → null
|
||||
* - localhost → null
|
||||
*/
|
||||
export const getCurrentSubdomain = (): string | null => {
|
||||
const hostname = window.location.hostname;
|
||||
|
||||
// No subdomain for localhost
|
||||
if (hostname === 'localhost' || hostname === '127.0.0.1') {
|
||||
return null;
|
||||
}
|
||||
|
||||
const parts = hostname.split('.');
|
||||
|
||||
// If only 2 parts, no subdomain
|
||||
if (parts.length === 2) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Return the first part as subdomain
|
||||
return parts[0];
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we're on the root domain (no subdomain)
|
||||
*/
|
||||
export const isRootDomain = (): boolean => {
|
||||
const hostname = window.location.hostname;
|
||||
return hostname === 'localhost' || hostname === '127.0.0.1' || hostname.split('.').length === 2;
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we're on the platform subdomain
|
||||
*/
|
||||
export const isPlatformDomain = (): boolean => {
|
||||
const subdomain = getCurrentSubdomain();
|
||||
return subdomain === 'platform';
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if we're on a business subdomain (not root, not platform, not api)
|
||||
*/
|
||||
export const isBusinessSubdomain = (): boolean => {
|
||||
const subdomain = getCurrentSubdomain();
|
||||
return subdomain !== null && subdomain !== 'platform' && subdomain !== 'api';
|
||||
};
|
||||
|
||||
/**
|
||||
* Build a full URL with the given subdomain
|
||||
* Examples:
|
||||
* - buildSubdomainUrl('platform') → http://platform.lvh.me:5173 (in dev)
|
||||
* - buildSubdomainUrl('demo') → https://demo.smoothschedule.com (in prod)
|
||||
* - buildSubdomainUrl(null) → https://smoothschedule.com (root domain)
|
||||
*/
|
||||
export const buildSubdomainUrl = (subdomain: string | null, path: string = '/'): string => {
|
||||
const baseDomain = getBaseDomain();
|
||||
const protocol = window.location.protocol;
|
||||
const port = window.location.port ? `:${window.location.port}` : '';
|
||||
|
||||
if (subdomain) {
|
||||
return `${protocol}//${subdomain}.${baseDomain}${port}${path}`;
|
||||
} else {
|
||||
return `${protocol}//${baseDomain}${port}${path}`;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the cookie domain attribute (with leading dot for cross-subdomain access)
|
||||
* Examples:
|
||||
* - .lvh.me (in dev)
|
||||
* - .smoothschedule.com (in prod)
|
||||
* - localhost (for localhost)
|
||||
*/
|
||||
export const getCookieDomain = (): string => {
|
||||
const baseDomain = getBaseDomain();
|
||||
|
||||
// Don't use dot prefix for localhost
|
||||
if (baseDomain === 'localhost') {
|
||||
return 'localhost';
|
||||
}
|
||||
|
||||
// Use dot prefix for cross-subdomain access
|
||||
return `.${baseDomain}`;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the WebSocket URL for the current environment
|
||||
* Examples:
|
||||
* - ws://lvh.me:8000/ws/ (in dev)
|
||||
* - wss://smoothschedule.com/ws/ (in prod)
|
||||
*/
|
||||
export const getWebSocketUrl = (path: string = ''): string => {
|
||||
const baseDomain = getBaseDomain();
|
||||
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
|
||||
// In development, WebSocket server runs on port 8000
|
||||
const isDev = baseDomain === 'lvh.me' || baseDomain === 'localhost';
|
||||
const port = isDev ? ':8000' : '';
|
||||
|
||||
return `${protocol}//${baseDomain}${port}/ws/${path}`;
|
||||
};
|
||||
Reference in New Issue
Block a user