AeThex-Connect/astro-site/src/components/aethex/AeThexProvider.jsx

275 lines
6.9 KiB
JavaScript

/**
* AeThex Provider
* Main context provider for AeThex Connect - handles auth, chat, and real-time features
*/
import React, { createContext, useContext, useState, useEffect, useCallback } from 'react';
import { io } from 'socket.io-client';
const API_URL = import.meta.env?.VITE_API_URL || 'http://localhost:5000';
const API_BASE = `${API_URL}/api`;
const AeThexContext = createContext(null);
export function useAeThex() {
return useContext(AeThexContext);
}
export function AeThexProvider({ children }) {
// Auth state
const [user, setUser] = useState(null);
const [token, setTokenState] = useState(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState(null);
// Socket state
const [socket, setSocket] = useState(null);
const [connected, setConnected] = useState(false);
// Chat state
const [servers, setServers] = useState([]);
const [channels, setChannels] = useState([]);
const [messages, setMessages] = useState([]);
const [currentServer, setCurrentServer] = useState(null);
const [currentChannel, setCurrentChannel] = useState(null);
const [onlineUsers, setOnlineUsers] = useState([]);
// Token management
const setToken = useCallback((newToken) => {
setTokenState(newToken);
if (newToken) {
localStorage.setItem('aethex_token', newToken);
} else {
localStorage.removeItem('aethex_token');
}
}, []);
// API request helper
const apiRequest = useCallback(async (endpoint, options = {}) => {
const headers = {
'Content-Type': 'application/json',
...options.headers,
};
const currentToken = token || localStorage.getItem('aethex_token');
if (currentToken) {
headers['Authorization'] = `Bearer ${currentToken}`;
}
const response = await fetch(`${API_BASE}${endpoint}`, {
...options,
headers,
});
const data = await response.json();
if (!response.ok) {
throw new Error(data.error || 'Request failed');
}
return data;
}, [token]);
// Auth functions
const login = useCallback(async (email, password) => {
setError(null);
setLoading(true);
try {
const response = await apiRequest('/auth/login', {
method: 'POST',
body: JSON.stringify({ email, password }),
});
if (response.success) {
setToken(response.data.token);
setUser(response.data.user);
return { success: true };
}
throw new Error(response.error || 'Login failed');
} catch (err) {
setError(err.message);
return { success: false, error: err.message };
} finally {
setLoading(false);
}
}, [apiRequest, setToken]);
const register = useCallback(async (email, password, username, displayName) => {
setError(null);
setLoading(true);
try {
const response = await apiRequest('/auth/register', {
method: 'POST',
body: JSON.stringify({ email, password, username, displayName }),
});
if (response.success) {
setToken(response.data.token);
setUser(response.data.user);
return { success: true };
}
throw new Error(response.error || 'Registration failed');
} catch (err) {
setError(err.message);
return { success: false, error: err.message };
} finally {
setLoading(false);
}
}, [apiRequest, setToken]);
const demoLogin = useCallback(async () => {
setError(null);
setLoading(true);
try {
const response = await apiRequest('/auth/demo', {
method: 'POST',
});
if (response.success) {
setToken(response.data.token);
setUser(response.data.user);
return { success: true };
}
throw new Error(response.error || 'Demo login failed');
} catch (err) {
setError(err.message);
return { success: false, error: err.message };
} finally {
setLoading(false);
}
}, [apiRequest, setToken]);
const logout = useCallback(() => {
setToken(null);
setUser(null);
if (socket) {
socket.disconnect();
setSocket(null);
}
setConnected(false);
}, [socket, setToken]);
// Socket connection
const connectSocket = useCallback((authToken) => {
if (socket) {
socket.disconnect();
}
const newSocket = io(API_URL, {
auth: { token: authToken },
reconnection: true,
reconnectionDelay: 1000,
reconnectionAttempts: 10,
transports: ['websocket', 'polling']
});
newSocket.on('connect', () => {
console.log('✓ Connected to AeThex Connect');
setConnected(true);
});
newSocket.on('disconnect', () => {
console.log('✗ Disconnected from AeThex Connect');
setConnected(false);
});
newSocket.on('message:new', (message) => {
setMessages(prev => [...prev, message]);
});
newSocket.on('presence:online', (users) => {
setOnlineUsers(users);
});
setSocket(newSocket);
return newSocket;
}, [socket]);
// Chat functions
const sendMessage = useCallback((content) => {
if (!socket || !connected || !currentChannel) return;
socket.emit('channel:message', {
channelId: currentChannel.id,
content
});
}, [socket, connected, currentChannel]);
const joinChannel = useCallback((channelId) => {
if (!socket || !connected) return;
socket.emit('channel:join', { channelId });
}, [socket, connected]);
// Check auth on mount
useEffect(() => {
const checkAuth = async () => {
const storedToken = localStorage.getItem('aethex_token');
if (storedToken) {
setTokenState(storedToken);
try {
const response = await fetch(`${API_BASE}/auth/me`, {
headers: { 'Authorization': `Bearer ${storedToken}` }
});
const data = await response.json();
if (data.success) {
setUser(data.data);
connectSocket(storedToken);
} else {
localStorage.removeItem('aethex_token');
}
} catch (err) {
console.error('Auth check failed:', err);
localStorage.removeItem('aethex_token');
}
}
setLoading(false);
};
checkAuth();
return () => {
if (socket) {
socket.disconnect();
}
};
}, []);
// Connect socket when user logs in
useEffect(() => {
if (user && token && !socket) {
connectSocket(token);
}
}, [user, token]);
const value = {
// Auth
user,
loading,
error,
isAuthenticated: !!user,
login,
register,
demoLogin,
logout,
// Socket
socket,
connected,
// Chat
servers,
channels,
messages,
currentServer,
currentChannel,
onlineUsers,
setCurrentServer,
setCurrentChannel,
setMessages,
sendMessage,
joinChannel,
// API helper
apiRequest
};
return (
<AeThexContext.Provider value={value}>
{children}
</AeThexContext.Provider>
);
}