refactor: Centralize platform mode detection using IS_PLATFORM constant; use token from Auth context in WebSocket connection

This commit is contained in:
Haileyesus
2026-01-31 14:34:01 +03:00
parent 471892b2bd
commit cfd766819a
8 changed files with 25 additions and 16 deletions

View File

@@ -63,3 +63,10 @@ export const CODEX_MODELS = {
DEFAULT: 'gpt-5.2' DEFAULT: 'gpt-5.2'
}; };
/**
* Environment Flag: Is Platform
* Indicates if the app is running in Platform mode (hosted) or OSS mode (self-hosted)
*/
export const IS_PLATFORM = import.meta.env.VITE_IS_PLATFORM === 'true';

View File

@@ -1,5 +1,6 @@
import { X } from 'lucide-react'; import { X } from 'lucide-react';
import StandaloneShell from './StandaloneShell'; import StandaloneShell from './StandaloneShell';
import { IS_PLATFORM } from '../../shared/modelConstants';
/** /**
* Reusable login modal component for Claude, Cursor, and Codex CLI authentication * Reusable login modal component for Claude, Cursor, and Codex CLI authentication
@@ -27,15 +28,13 @@ function LoginModal({
const getCommand = () => { const getCommand = () => {
if (customCommand) return customCommand; if (customCommand) return customCommand;
const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true';
switch (provider) { switch (provider) {
case 'claude': case 'claude':
return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions'; return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions';
case 'cursor': case 'cursor':
return 'cursor-agent login'; return 'cursor-agent login';
case 'codex': case 'codex':
return isPlatform ? 'codex login --device-auth' : 'codex login'; return IS_PLATFORM ? 'codex login --device-auth' : 'codex login';
default: default:
return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions'; return isAuthenticated ? 'claude setup-token --dangerously-skip-permissions' : 'claude /exit --dangerously-skip-permissions';
} }

View File

@@ -6,6 +6,7 @@ import CodexLogo from './CodexLogo';
import LoginModal from './LoginModal'; import LoginModal from './LoginModal';
import { authenticatedFetch } from '../utils/api'; import { authenticatedFetch } from '../utils/api';
import { useAuth } from '../contexts/AuthContext'; import { useAuth } from '../contexts/AuthContext';
import { IS_PLATFORM } from '../../shared/modelConstants';
const Onboarding = ({ onComplete }) => { const Onboarding = ({ onComplete }) => {
const [currentStep, setCurrentStep] = useState(0); const [currentStep, setCurrentStep] = useState(0);
@@ -15,8 +16,7 @@ const Onboarding = ({ onComplete }) => {
const [error, setError] = useState(''); const [error, setError] = useState('');
const [activeLoginProvider, setActiveLoginProvider] = useState(null); const [activeLoginProvider, setActiveLoginProvider] = useState(null);
const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true'; const [selectedProject] = useState({ name: 'default', fullPath: IS_PLATFORM ? '/workspace' : '' });
const [selectedProject] = useState({ name: 'default', fullPath: isPlatform ? '/workspace' : '' });
const [claudeAuthStatus, setClaudeAuthStatus] = useState({ const [claudeAuthStatus, setClaudeAuthStatus] = useState({
authenticated: false, authenticated: false,

View File

@@ -4,6 +4,7 @@ import SetupForm from './SetupForm';
import LoginForm from './LoginForm'; import LoginForm from './LoginForm';
import Onboarding from './Onboarding'; import Onboarding from './Onboarding';
import { MessageSquare } from 'lucide-react'; import { MessageSquare } from 'lucide-react';
import { IS_PLATFORM } from '../../shared/modelConstants';
const LoadingScreen = () => ( const LoadingScreen = () => (
<div className="min-h-screen bg-background flex items-center justify-center p-4"> <div className="min-h-screen bg-background flex items-center justify-center p-4">
@@ -27,7 +28,7 @@ const LoadingScreen = () => (
const ProtectedRoute = ({ children }) => { const ProtectedRoute = ({ children }) => {
const { user, isLoading, needsSetup, hasCompletedOnboarding, refreshOnboardingStatus } = useAuth(); const { user, isLoading, needsSetup, hasCompletedOnboarding, refreshOnboardingStatus } = useAuth();
if (import.meta.env.VITE_IS_PLATFORM === 'true') { if (IS_PLATFORM) {
if (isLoading) { if (isLoading) {
return <LoadingScreen />; return <LoadingScreen />;
} }

View File

@@ -5,6 +5,7 @@ import { WebglAddon } from '@xterm/addon-webgl';
import { WebLinksAddon } from '@xterm/addon-web-links'; import { WebLinksAddon } from '@xterm/addon-web-links';
import '@xterm/xterm/css/xterm.css'; import '@xterm/xterm/css/xterm.css';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { IS_PLATFORM } from '../../shared/modelConstants';
const xtermStyles = ` const xtermStyles = `
.xterm .xterm-screen { .xterm .xterm-screen {
@@ -55,10 +56,9 @@ function Shell({ selectedProject, selectedSession, initialCommand, isPlainShell
if (isConnecting || isConnected) return; if (isConnecting || isConnected) return;
try { try {
const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true';
let wsUrl; let wsUrl;
if (isPlatform) { if (IS_PLATFORM) {
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
wsUrl = `${protocol}//${window.location.host}/shell`; wsUrl = `${protocol}//${window.location.host}/shell`;
} else { } else {

View File

@@ -16,6 +16,7 @@ import ProjectCreationWizard from './ProjectCreationWizard';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { useTaskMaster } from '../contexts/TaskMasterContext'; import { useTaskMaster } from '../contexts/TaskMasterContext';
import { useTasksSettings } from '../contexts/TasksSettingsContext'; import { useTasksSettings } from '../contexts/TasksSettingsContext';
import { IS_PLATFORM } from '../../shared/modelConstants';
// Move formatTimeAgo outside component to avoid recreation on every render // Move formatTimeAgo outside component to avoid recreation on every render
const formatTimeAgo = (dateString, currentTime, t) => { const formatTimeAgo = (dateString, currentTime, t) => {
@@ -622,7 +623,7 @@ function Sidebar({
<div className="md:p-4 md:border-b md:border-border"> <div className="md:p-4 md:border-b md:border-border">
{/* Desktop Header */} {/* Desktop Header */}
<div className="hidden md:flex items-center justify-between"> <div className="hidden md:flex items-center justify-between">
{import.meta.env.VITE_IS_PLATFORM === 'true' ? ( {IS_PLATFORM ? (
<a <a
href="https://cloudcli.ai/dashboard" href="https://cloudcli.ai/dashboard"
className="flex items-center gap-3 hover:opacity-80 transition-opacity group" className="flex items-center gap-3 hover:opacity-80 transition-opacity group"
@@ -673,7 +674,7 @@ function Sidebar({
style={isPWA && isMobile ? { paddingTop: '16px' } : {}} style={isPWA && isMobile ? { paddingTop: '16px' } : {}}
> >
<div className="flex items-center justify-between"> <div className="flex items-center justify-between">
{import.meta.env.VITE_IS_PLATFORM === 'true' ? ( {IS_PLATFORM ? (
<a <a
href="https://cloudcli.ai/dashboard" href="https://cloudcli.ai/dashboard"
className="flex items-center gap-3 active:opacity-70 transition-opacity" className="flex items-center gap-3 active:opacity-70 transition-opacity"

View File

@@ -1,5 +1,6 @@
import React, { createContext, useContext, useEffect, useState } from 'react'; import React, { createContext, useContext, useEffect, useState } from 'react';
import { api } from '../utils/api'; import { api } from '../utils/api';
import { IS_PLATFORM } from '../../shared/modelConstants';
const AuthContext = createContext({ const AuthContext = createContext({
user: null, user: null,
@@ -31,7 +32,7 @@ export const AuthProvider = ({ children }) => {
const [error, setError] = useState(null); const [error, setError] = useState(null);
useEffect(() => { useEffect(() => {
if (import.meta.env.VITE_IS_PLATFORM === 'true') { if (IS_PLATFORM) {
setUser({ username: 'platform-user' }); setUser({ username: 'platform-user' });
setNeedsSetup(false); setNeedsSetup(false);
checkOnboardingStatus(); checkOnboardingStatus();

View File

@@ -1,4 +1,6 @@
import { createContext, useContext, useEffect, useRef, useState } from 'react'; import { createContext, useContext, useEffect, useRef, useState } from 'react';
import { useAuth } from './AuthContext';
import { IS_PLATFORM } from '../../shared/modelConstants';
type WebSocketContextType = { type WebSocketContextType = {
ws: WebSocket | null; ws: WebSocket | null;
@@ -18,9 +20,8 @@ export const useWebSocket = () => {
}; };
const buildWebSocketUrl = (token: string | null) => { const buildWebSocketUrl = (token: string | null) => {
const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true';
const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:'; const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
if (isPlatform) return `${protocol}//${window.location.host}/ws`; // Platform mode: Use same domain as the page (goes through proxy) if (IS_PLATFORM) return `${protocol}//${window.location.host}/ws`; // Platform mode: Use same domain as the page (goes through proxy)
if (!token) return null; if (!token) return null;
return `${protocol}//${window.location.host}/ws?token=${encodeURIComponent(token)}`; // OSS mode: Use same host:port that served the page return `${protocol}//${window.location.host}/ws?token=${encodeURIComponent(token)}`; // OSS mode: Use same host:port that served the page
}; };
@@ -31,6 +32,7 @@ const useWebSocketProviderState = (): WebSocketContextType => {
const [messages, setMessages] = useState<any[]>([]); const [messages, setMessages] = useState<any[]>([]);
const [isConnected, setIsConnected] = useState(false); const [isConnected, setIsConnected] = useState(false);
const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null); const reconnectTimeoutRef = useRef<NodeJS.Timeout | null>(null);
const { token } = useAuth();
useEffect(() => { useEffect(() => {
connect(); connect();
@@ -49,10 +51,8 @@ const useWebSocketProviderState = (): WebSocketContextType => {
const connect = () => { const connect = () => {
if (unmountedRef.current) return; // Prevent connection if unmounted if (unmountedRef.current) return; // Prevent connection if unmounted
try { try {
const isPlatform = import.meta.env.VITE_IS_PLATFORM === 'true';
// Construct WebSocket URL // Construct WebSocket URL
const wsUrl = buildWebSocketUrl(isPlatform ? null : localStorage.getItem('authToken')); const wsUrl = buildWebSocketUrl(token);
if (!wsUrl) return console.warn('No authentication token found for WebSocket connection'); if (!wsUrl) return console.warn('No authentication token found for WebSocket connection');