const loginScreen = document.getElementById("loginScreen"); const welcomeScreen = document.getElementById("welcomeScreen"); const chatScreen = document.getElementById("chatScreen"); const chatMessages = document.getElementById("chatMessages"); const welcomeInput = document.getElementById("welcomeInput"); const chatInput = document.getElementById("chatInput"); const loginBtn = document.getElementById("loginBtn"); const API_URL = "http://localhost:8000"; let isInChat = false; // Auth functions function getToken() { return localStorage.getItem("devden_token"); } function setToken(token) { localStorage.setItem("devden_token", token); } function clearToken() { localStorage.removeItem("devden_token"); } function showLoginScreen() { loginScreen.classList.remove("hidden"); welcomeScreen.classList.add("hidden"); chatScreen.classList.add("hidden"); } function showWelcomeScreen() { loginScreen.classList.add("hidden"); welcomeScreen.classList.remove("hidden"); chatScreen.classList.add("hidden"); welcomeInput.focus(); } function switchToChat() { loginScreen.classList.add("hidden"); welcomeScreen.classList.add("hidden"); chatScreen.classList.remove("hidden"); chatInput.focus(); isInChat = true; } async function checkAuth() { const token = getToken(); if (!token) { showLoginScreen(); return; } try { const response = await fetch(`${API_URL}/api/auth/me`, { headers: { Authorization: `Bearer ${token}` }, }); if (response.ok) { showWelcomeScreen(); } else { clearToken(); showLoginScreen(); } } catch (error) { console.error("Auth check failed:", error); showLoginScreen(); } } async function handleLogin() { loginBtn.disabled = true; loginBtn.textContent = "Redirecting..."; try { // Check if auth is configured const statusResponse = await fetch(`${API_URL}/api/auth/status`); const statusData = await statusResponse.json(); if (!statusData.configured) { alert( "Authentication not configured. Please set ENTRA_TENANT_ID, ENTRA_CLIENT_ID, and ENTRA_CLIENT_SECRET in your .env file.", ); loginBtn.disabled = false; loginBtn.textContent = "Sign in with Microsoft"; return; } // Get auth URL and redirect const response = await fetch(`${API_URL}/api/auth/login`); const data = await response.json(); if (data.auth_url) { window.location.href = data.auth_url; } else { throw new Error("No auth URL returned"); } } catch (error) { console.error("Login failed:", error); alert("Login failed: " + error.message); loginBtn.disabled = false; loginBtn.textContent = "Sign in with Microsoft"; } } async function handleCallback() { const params = new URLSearchParams(window.location.search); const code = params.get("code"); if (!code) return false; try { const response = await fetch(`${API_URL}/api/auth/callback`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code }), }); if (!response.ok) { const error = await response.json(); throw new Error(error.detail || "Callback failed"); } const data = await response.json(); setToken(data.token); // Clean up URL window.history.replaceState({}, "", "/"); return true; } catch (error) { console.error("Callback failed:", error); alert("Authentication failed: " + error.message); return false; } } // Chat functions function addMessage(text, type = "user") { const msg = document.createElement("div"); msg.className = `message ${type}`; msg.innerHTML = `
`; chatMessages.appendChild(msg); scrollToBottom(); } function showTyping() { const typing = document.createElement("div"); typing.className = "message assistant"; typing.id = "typing"; typing.innerHTML = ` `; chatMessages.appendChild(typing); scrollToBottom(); } function hideTyping() { const typing = document.getElementById("typing"); if (typing) typing.remove(); } function scrollToBottom() { chatMessages.scrollTop = chatMessages.scrollHeight; } function escapeHtml(text) { const div = document.createElement("div"); div.textContent = text; return div.innerHTML; } async function sendMessage(text) { if (!text.trim()) return; if (!isInChat) { switchToChat(); } addMessage(text, "user"); chatInput.value = ""; chatInput.disabled = true; showTyping(); try { const token = getToken(); const response = await fetch(`${API_URL}/api/chat/stream`, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${token}`, }, body: JSON.stringify({ message: text, provider: null, }), }); if (response.status === 401) { hideTyping(); clearToken(); showLoginScreen(); return; } if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } hideTyping(); // Create message element for streaming const msgDiv = document.createElement("div"); msgDiv.className = "message assistant"; const textDiv = document.createElement("div"); textDiv.className = "message-text"; msgDiv.appendChild(textDiv); chatMessages.appendChild(msgDiv); // Read stream const reader = response.body.getReader(); const decoder = new TextDecoder(); let assistantMessage = ""; while (true) { const { done, value } = await reader.read(); if (done) break; const chunk = decoder.decode(value); const lines = chunk.split("\n"); for (const line of lines) { if (line.startsWith("data: ")) { const data = JSON.parse(line.slice(6)); if (data.error) { textDiv.textContent = `Error: ${data.error}`; break; } if (data.chunk) { assistantMessage += data.chunk; textDiv.textContent = assistantMessage; scrollToBottom(); } if (data.done) { break; } } } } } catch (error) { hideTyping(); addMessage(`Error: ${error.message}`, "assistant"); } finally { chatInput.disabled = false; chatInput.focus(); } } // Event listeners loginBtn.addEventListener("click", handleLogin); welcomeInput.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); const text = welcomeInput.value.trim(); if (text) { sendMessage(text); } } }); chatInput.addEventListener("keydown", (e) => { if (e.key === "Enter") { e.preventDefault(); sendMessage(chatInput.value); } }); // Initialize async function init() { // Check for OAuth callback first const callbackSuccess = await handleCallback(); if (callbackSuccess) { showWelcomeScreen(); } else { await checkAuth(); } } init();