Skip to content

Commit

Permalink
Merge branch 'dev-server'
Browse files Browse the repository at this point in the history
  • Loading branch information
HlexNC committed Jul 3, 2024
2 parents 0916fb1 + 1cbebc7 commit c1ad4c9
Show file tree
Hide file tree
Showing 12 changed files with 237 additions and 142 deletions.
7 changes: 2 additions & 5 deletions client/.env.examle
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,6 @@ PORT=3000
# Server URL
REACT_APP_SERVER_URL=https://localhost:5000

# WebSocket URL
REACT_APP_WS_URL=wss://localhost:5001/ws

# SSL Certificate and Key (if needed for local development)
SSL_CRT_FILE=/etc/ssl/certs/localhost.pem
SSL_KEY_FILE=/etc/ssl/private/localhost-key.pem
# SSL_CRT_FILE=/etc/ssl/certs/localhost.pem
# SSL_KEY_FILE=/etc/ssl/private/localhost-key.pem
4 changes: 2 additions & 2 deletions client/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ COPY .env .env

EXPOSE 3000


CMD ["serve", "-s", "build", "-l", "3000", "--ssl-cert", "/etc/ssl/certs/localhost.pem", "--ssl-key", "/etc/ssl/private/localhost-key.pem"]
# CMD ["serve", "-s", "build", "-l", "3000", "--ssl-cert", "/etc/ssl/certs/localhost.pem", "--ssl-key", "/etc/ssl/private/localhost-key.pem"]
CMD ["serve", "-s", "build", "-l", "3000"]
174 changes: 84 additions & 90 deletions client/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,181 +4,175 @@ import Chat from './components/Chat';
import Navbar from './components/Navbar';
import './App.css';

const WSS_URL = process.env.REACT_APP_WSS_URL || `wss://${window.location.hostname}:5002`;
const WS_URL = process.env.REACT_APP_WS_URL || `ws://${window.location.hostname}:5001`;
const SERVER_URL = process.env.REACT_APP_SERVER_URL || 'https://localhost:5000';

function App() {
const [socketUrl, setSocketUrl] = useState(WSS_URL);
const App = () => {
const [useWebSocketProtocol, setUseWebSocketProtocol] = useState(true);
const [socketUrl, setSocketUrl] = useState(`${SERVER_URL.replace(/^http/, 'ws')}`);
const [sessionId, setSessionId] = useState(localStorage.getItem('sessionId') || null);
const [messages, setMessages] = useState(() => {
const savedMessages = localStorage.getItem('chatHistory');
return savedMessages ? JSON.parse(savedMessages) : [{ role: 'system', content: 'You are Devabot ✨, a funny helpful assistant.' }];
});
const [isConnected, setIsConnected] = useState(false);
const [fallbackAttempted, setFallbackAttempted] = useState(false);

const {
sendMessage: sendWebSocketMessage,
lastMessage,
readyState,
} = useWebSocket(socketUrl, {
onOpen: () => setIsConnected(true),
onClose: () => setIsConnected(false),
onError: (error) => console.error('WebSocket error:', error),
onMessage: (message) => handleMessage(message),
shouldReconnect: () => true,
reconnectAttempts: 20,
reconnectInterval: 3000,
});

const validateOrCreateSession = useCallback(() => {
if (sessionId) {
console.log('Validating existing session:', sessionId);
sendMessage(JSON.stringify({ action: 'validateSession', data: { sessionId: sessionId } }));
sendWebSocketMessage(JSON.stringify({ action: 'validateSession', data: { sessionId } }));
} else {
console.log('Creating new session');
sendMessage(JSON.stringify({ action: 'createSession' }));
sendWebSocketMessage(JSON.stringify({ action: 'createSession' }));
}
}, [sessionId]);

const handleOpen = useCallback(() => {
console.log('WebSocket connected');
setIsConnected(true);
});

const handleClose = useCallback(() => {
console.log('WebSocket disconnected');
setIsConnected(false);
if (!fallbackAttempted) {
console.log('Attempting fallback to WS');
setSocketUrl(WS_URL);
setFallbackAttempted(true);
}
}, [fallbackAttempted]);

const handleError = useCallback(
(error) => {
console.error('WebSocket error:', error);
if (!fallbackAttempted) {
console.log('Error occurred, attempting fallback to WS');
setSocketUrl(WS_URL);
setFallbackAttempted(true);
}
},
[fallbackAttempted]
);
}, [sessionId, sendWebSocketMessage]);

const handleMessage = useCallback(
(message) => {
try {
const parsedData = JSON.parse(message.data);
console.log('Received WebSocket message:', parsedData);

switch (parsedData.action) {
case 'sessionValidated':
if (parsedData.valid) {
console.log('Session validated:', parsedData.sessionId);
} else {
console.log('Session invalid, creating new session');
}
// Always update the sessionId, whether it's validated or new
setSessionId(parsedData.sessionId);
localStorage.setItem('sessionId', parsedData.sessionId);
break;
break;

case 'assistantMessage':
const returned_message = parsedData.assistant_message;
const newMessage = { role: 'assistant', content: returned_message };
setMessages((prevMessages) => {
const updatedMessages = [...prevMessages, newMessage];
return updatedMessages;
});
const returnedMessage = parsedData.assistant_message;
const newMessage = { role: 'assistant', content: returnedMessage };
setMessages((prevMessages) => [...prevMessages, newMessage]);
break;

case 'chatHistory':
console.log('Received chat history');
setMessages(parsedData.messages);
localStorage.setItem('chatHistory', JSON.stringify(parsedData.messages));
break;

default:
console.log('Unknown WebSocket message received:', parsedData);
break;
}
} catch (error) {
console.error('Error parsing WebSocket message:', error);
}
},
[sessionId, setSessionId, setMessages]
}, [sessionId]
);

const { sendMessage, lastMessage, readyState, reconnect } = useWebSocket(socketUrl, {
onOpen: handleOpen,
onClose: handleClose,
onError: handleError,
onMessage: handleMessage,
shouldReconnect: (closeEvent) => !fallbackAttempted,
reconnectAttempts: 20,
reconnectInterval: 3000,
});

const clearHistory = useCallback(() => {
localStorage.removeItem('chatHistory');
setMessages([{ role: 'system', content: 'You are Devabot ✨, a funny helpful assistant.' }]);

if (isConnected) {
sendMessage(JSON.stringify({ action: 'deleteSession', data: { sessionId: sessionId } }));
sendWebSocketMessage(JSON.stringify({ action: 'deleteSession', data: { sessionId } }));
localStorage.removeItem('sessionId');
setSessionId(null);
// Instead of immediately creating a new session, we'll wait for the next connection
}
}, [isConnected, sendMessage, sessionId]);
}, [isConnected, sendWebSocketMessage, sessionId]);

const sendMessageHandler = async (message) => {
const newMessage = { role: 'user', content: message };
const updatedMessages = [...messages, newMessage];
setMessages(updatedMessages);
localStorage.setItem('chatHistory', JSON.stringify(updatedMessages));

if (isConnected) {
sendMessage(

if (useWebSocketProtocol && isConnected) {
sendWebSocketMessage(
JSON.stringify({
action: 'sendMessage',
data: {
sessionId: sessionId,
sessionId,
user_message: message,
},
})
);
} else {
console.error('WebSocket is not connected. Unable to send message.');
try {
const response = await fetch(`${SERVER_URL}/api/openai/assistant/sendMessage`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
sessionID: sessionId,
prompt: message,
}),
});

if (!response.ok) {
const errorData = await response.json();
console.error('Error response from server:', errorData);
throw new Error(`Error: ${response.status} ${response.statusText}`);
}

const data = await response.json();
console.log('Response data:', data);
const returnedMessage = data.assistant_message || data;
const newAssistantMessage = { role: 'assistant', content: returnedMessage };
setMessages((prevMessages) => [...prevMessages, newAssistantMessage]);
} catch (error) {
console.error('Error sending message:', error);
}
}
};

const getStatus = () => {
const currentProtocol = useWebSocketProtocol ? 'WSS' : 'HTTPS';
const protocolIndicator = currentProtocol === 'WSS' ? '🌐' : '🔒';
switch (readyState) {
case ReadyState.CONNECTING:
return '🔄 Connecting';
return `${protocolIndicator} 🔄 Connecting`;
case ReadyState.OPEN:
console.log('WebSocket Ready!');
return '🟢 Connected';
return `${protocolIndicator} 🟢 Connected`;
case ReadyState.CLOSING:
return '🟠 Closing';
return `${protocolIndicator} 🟠 Closing`;
case ReadyState.CLOSED:
return '🔴 Closed';
return currentProtocol === 'HTTPS' ? '🔒 🟢 HTTPS' : `${protocolIndicator} 🔴 Closed`;
default:
return '⚪ Unknown';
return `${protocolIndicator} ⚪ Unknown`;
}
};

useEffect(() => {
if (readyState === ReadyState.OPEN) {
setFallbackAttempted(false);
if (sessionId) {
validateOrCreateSession();
} else {
sendMessage(JSON.stringify({ action: 'createSession' }));
}
validateOrCreateSession();
}
}, [readyState, sessionId, validateOrCreateSession, sendMessage]);
}, [readyState, validateOrCreateSession]);

useEffect(() => {
localStorage.setItem('chatHistory', JSON.stringify(messages));
console.log('Saved messages to localStorage:', messages);
}, [messages]);

const toggleProtocol = () => {
setUseWebSocketProtocol((prev) => !prev);
};

return (
<div className="App">
<Navbar status={getStatus()} onReconnect={reconnect} onClearHistory={clearHistory} />
<Navbar
status={getStatus()}
onReconnect={() => setSocketUrl(`${SERVER_URL.replace(/^http/, 'ws')}`)}
onClearHistory={clearHistory}
useWebSocketProtocol={useWebSocketProtocol}
onToggleProtocol={toggleProtocol}
/>
<Chat sendMessage={sendMessageHandler} lastMessage={lastMessage} messages={messages} />
</div>
);
}
};

export default App;
22 changes: 19 additions & 3 deletions client/src/components/Navbar.css
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,28 @@
height: 60px;
}

.status-container {
display: flex;
align-items: center;
margin-left: auto;
}

.status {
cursor: pointer;
font-size: 2vh;
margin-right: 2vh;
margin-left: auto;
margin-right: 1vh;
}

.toggle-protocol {
cursor: pointer;
background-color: #007bff;
color: white;
border: none;
font-size: 2vh;
padding: 5px 10px;
border-radius: 5px;
margin-right: 1vh;
min-width: 100px; /* Ensure the button doesn't change size when text changes */
}

.clear-chat {
Expand All @@ -30,5 +47,4 @@
padding: 5px 10px;
border-radius: 5px;
margin-right: 5vh;
margin-left: 20px;
}
9 changes: 7 additions & 2 deletions client/src/components/Navbar.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@ import './Navbar.css';
import WebSocketStatus from './WebSocketStatus';
import logo from '../logo.svg';

const Navbar = ({ status, onReconnect, onClearHistory }) => {
const Navbar = ({ status, onReconnect, onClearHistory, useWebSocketProtocol, onToggleProtocol }) => {
return (
<div className="navbar">
<img src={logo} alt="Devablos Project Logo" className="logo" />
<WebSocketStatus status={status} reconnect={onReconnect} />
<div className="status-container">
<WebSocketStatus status={status} reconnect={onReconnect} />
<button className="toggle-protocol" onClick={onToggleProtocol}>
{useWebSocketProtocol ? 'WebSockets' : 'HTTPS'}
</button>
</div>
<button className="clear-chat" onClick={onClearHistory}>🗑️ Clear Chat</button>
</div>
);
Expand Down
11 changes: 5 additions & 6 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ services:
platform: linux/amd64
ports:
- "3000:3000"
environment:
- HTTPS=true
- SSL_CRT_FILE=/etc/ssl/certs/localhost.pem
- SSL_KEY_FILE=/etc/ssl/private/localhost-key.pem
# environment:
# - HTTPS=true
# - SSL_CRT_FILE=/etc/ssl/certs/localhost.pem
# - SSL_KEY_FILE=/etc/ssl/private/localhost-key.pem
volumes:
- D:/Devablos-Project-V2/.certs:/etc/ssl/certs
- D:/Devablos-Project-V2/.private:/etc/ssl/private
Expand All @@ -22,8 +22,7 @@ services:
image: hlexnc/devablos-project-v2-server
platform: linux/amd64
ports:
- "5001:5001"
- "5002:5002"
- "5000:5000"
volumes:
- D:/Devablos-Project-V2/.certs:/etc/ssl/certs
- D:/Devablos-Project-V2/.private:/etc/ssl/private
Expand Down
9 changes: 3 additions & 6 deletions server/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@
# Port number
PORT=5000

# WebSocket port
WS_PORT=5001

# WebSocket Secure port
WSS_PORT=5001

# CORS origin
CORS_ORIGIN=https://localhost:3000

Expand All @@ -23,3 +17,6 @@ OPENWEATHER_API_KEY=your_openweather_api_key
# SSL key and cert paths
SSL_KEY_PATH=/etc/ssl/private/localhost-key.pem
SSL_CERT_PATH=/etc/ssl/certs/localhost.pem

# Use SSL
USE_SSL=true
4 changes: 2 additions & 2 deletions server/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,6 @@ COPY . .

COPY .env .env

EXPOSE 5001 5002
EXPOSE 5000

CMD ["npm", "run", "start:pain"]
CMD ["npm", "start"]
Loading

0 comments on commit c1ad4c9

Please sign in to comment.