Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Multi Agent Research</title> | |
| <script src="https://unpkg.com/@supabase/supabase-js@2"></script> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.prod.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
| <style> | |
| html, body { | |
| height: 100%; | |
| margin: 0; | |
| padding: 0; | |
| overflow: hidden; | |
| font-family: 'Roboto', sans-serif; | |
| } | |
| #app { | |
| height: 100%; | |
| display: flex; | |
| flex-direction: column; | |
| } | |
| .content-wrapper { | |
| flex-grow: 1; | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| iframe { | |
| position: absolute; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 100%; | |
| border: none; | |
| } | |
| .sidebar { | |
| position: fixed; | |
| left: 0; | |
| top: 0; | |
| bottom: 0; | |
| width: 250px; | |
| background-color: #2c3e50; | |
| color: white; | |
| display: flex; | |
| flex-direction: column; | |
| transition: transform 0.3s ease-in-out; | |
| transform: translateX(-100%); | |
| overflow-y: auto; | |
| padding-top: 60px; | |
| z-index: 1000; | |
| } | |
| .sidebar.open { | |
| transform: translateX(0); | |
| } | |
| .sidebar button { | |
| width: 80%; | |
| margin: 10px auto; | |
| padding: 15px; | |
| background-color: #3498db; | |
| color: white; | |
| border: none; | |
| border-radius: 10px; | |
| font-size: 14px; | |
| cursor: pointer; | |
| text-align: center; | |
| transition: all 0.3s ease; | |
| display: block; | |
| } | |
| .sidebar button:hover { | |
| background-color: #2980b9; | |
| transform: translateY(-2px); | |
| box-shadow: 0 4px 8px rgba(0,0,0,0.2); | |
| } | |
| .toggle-btn { | |
| position: fixed; | |
| left: 10px; | |
| top: 10px; | |
| background-color: #3498db; | |
| color: white; | |
| border: none; | |
| border-radius: 10px; | |
| font-size: 16px; | |
| cursor: pointer; | |
| padding: 7px 15px; | |
| z-index: 1001; | |
| transition: background-color 0.3s ease; | |
| } | |
| .toggle-btn:hover { | |
| background-color: #2980b9; | |
| } | |
| .category { | |
| width: 100%; | |
| margin-bottom: 20px; | |
| } | |
| .category-title { | |
| width: 80%; | |
| margin: 10px auto; | |
| padding: 10px; | |
| background-color: #34495e; | |
| color: white; | |
| border-radius: 10px; | |
| font-size: 16px; | |
| text-align: center; | |
| cursor: pointer; | |
| } | |
| .category-content { | |
| display: none; | |
| width: 100%; | |
| } | |
| .category-content.show { | |
| display: block; | |
| } | |
| .user-info { | |
| width: 80%; | |
| margin: 20px auto; | |
| text-align: center; | |
| } | |
| .user-name { | |
| margin-bottom: 10px; | |
| font-weight: bold; | |
| } | |
| .logout-btn { | |
| background-color: #e74c3c; | |
| } | |
| .logout-btn:hover { | |
| background-color: #c0392b; | |
| } | |
| @media (max-width: 768px) { | |
| .sidebar { | |
| width: 100%; | |
| } | |
| .sidebar button, .category-title, .user-info { | |
| width: 90%; | |
| } | |
| } | |
| @media (max-width: 480px) { | |
| .sidebar button, .category-title, .user-info { | |
| width: 95%; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app"> | |
| <div v-if="loading">Loading...</div> | |
| <div v-else-if="error">{{ error }}</div> | |
| <div v-else class="content-wrapper"> | |
| <div class="sidebar" :class="{ 'open': sidebarOpen }" id="sidebar"> | |
| <div v-for="(apps, category) in appData" :key="category" class="category"> | |
| <div class="category-title" @click="toggleCategory">{{ category }}</div> | |
| <div class="category-content"> | |
| <button v-for="app in apps" :key="app.name" @click="switchToApp(app.url)">{{ app.name }}</button> | |
| </div> | |
| </div> | |
| <div class="user-info"> | |
| <div class="user-name">{{ userName }}</div> | |
| <button class="logout-btn" @click="handleLogout">Logout</button> | |
| </div> | |
| </div> | |
| <button class="toggle-btn" @click="toggleSidebar">☰</button> | |
| <iframe :src="currentPage" id="iframe" allowfullscreen allow="clipboard-read; clipboard-write" sandbox="allow-scripts allow-same-origin allow-popups allow-forms allow-downloads"></iframe> | |
| </div> | |
| </div> | |
| <script> | |
| const { createApp } = Vue; | |
| const { createClient } = supabase; | |
| const supabaseClient = createClient('https://ftqmmutydpjseodidugl.supabase.co', 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6ImZ0cW1tdXR5ZHBqc2VvZGlkdWdsIiwicm9sZSI6ImFub24iLCJpYXQiOjE3MTQzMTE2MzQsImV4cCI6MjAyOTg4NzYzNH0.2h1uLIPCKrwF8f9enRChU_Q2qkhoQlR4gwlehL-h0a4'); | |
| function setCookie(name, value, days) { | |
| const expires = new Date(Date.now() + days * 864e5).toUTCString(); | |
| document.cookie = name + '=' + encodeURIComponent(JSON.stringify(value)) + '; expires=' + expires + '; path=/; SameSite=Strict'; | |
| console.log(`Cookie set: ${name}`, value); | |
| } | |
| function getCookie(name) { | |
| const value = `; ${document.cookie}`; | |
| const parts = value.split(`; ${name}=`); | |
| if (parts.length === 2) { | |
| const cookieValue = parts.pop().split(';').shift(); | |
| const decodedValue = JSON.parse(decodeURIComponent(cookieValue)); | |
| console.log(`Cookie retrieved: ${name}`, decodedValue); | |
| return decodedValue; | |
| } | |
| console.log(`Cookie not found: ${name}`); | |
| return null; | |
| } | |
| const app = createApp({ | |
| data() { | |
| return { | |
| appData: {}, | |
| currentPage: '', | |
| loading: true, | |
| error: null, | |
| sidebarOpen: false, | |
| userName: '' | |
| } | |
| }, | |
| methods: { | |
| async checkAuth() { | |
| try { | |
| console.log("Checking authentication"); | |
| const { data: { session }, error } = await supabaseClient.auth.getSession(); | |
| if (error) throw error; | |
| console.log("Session:", session); | |
| return session; | |
| } catch (error) { | |
| console.error("Not authenticated", error); | |
| return null; | |
| } | |
| }, | |
| redirectToLogin() { | |
| console.log("Redirecting to login"); | |
| window.location.href = '/'; | |
| }, | |
| async checkAllowlist(email) { | |
| try { | |
| console.log("Checking allowlist for email:", email); | |
| const response = await axios.post('/check-allowlist', { email }); | |
| console.log("Received response:", response.data); | |
| if (response.data.allowed) { | |
| console.log("Access allowed, setting app data"); | |
| this.appData = response.data.app_data; | |
| this.currentPage = this.currentPage || this.appData["Updated Tools"][0].url; | |
| // Store app_data and current page URL in cookies | |
| setCookie('app_data', this.appData, 7); // Store for 7 days | |
| setCookie('current_page', this.currentPage, 7); | |
| console.log("Stored app_data and current_page in cookies", this.appData, this.currentPage); | |
| } else { | |
| console.log("Access denied"); | |
| this.error = `Access Denied: Your email (${email}) is not on the allowlist. Server message: ${response.data.message || 'No additional information provided.'}`; | |
| } | |
| } catch (error) { | |
| console.error("Error checking allowlist:", error); | |
| let errorMessage = 'An error occurred while checking access.'; | |
| if (error.response) { | |
| // The request was made and the server responded with a status code | |
| // that falls out of the range of 2xx | |
| errorMessage += ` Server responded with status ${error.response.status}.`; | |
| if (error.response.data && error.response.data.message) { | |
| errorMessage += ` Message: ${error.response.data.message}`; | |
| } | |
| } else if (error.request) { | |
| // The request was made but no response was received | |
| errorMessage += ' No response received from server.'; | |
| } else { | |
| // Something happened in setting up the request that triggered an Error | |
| errorMessage += ` Error message: ${error.message}`; | |
| } | |
| this.error = `Error for email (${email}): ${errorMessage}`; | |
| } finally { | |
| console.log("Setting loading to false"); | |
| this.loading = false; | |
| } | |
| }, | |
| toggleSidebar() { | |
| this.sidebarOpen = !this.sidebarOpen; | |
| }, | |
| toggleCategory(event) { | |
| const content = event.target.nextElementSibling; | |
| content.classList.toggle('show'); | |
| }, | |
| switchToApp(url) { | |
| console.log("Switching to app:", url); | |
| this.currentPage = url; | |
| setCookie('current_page', url, 7); | |
| this.toggleSidebar(); | |
| }, | |
| async handleLogout() { | |
| const loadingElement = document.createElement('div'); | |
| loadingElement.textContent = 'Logging out...'; | |
| document.body.appendChild(loadingElement); | |
| try { | |
| const { error } = await supabaseClient.auth.signOut(); | |
| if (error) throw error; | |
| // Clear cookies | |
| document.cookie = 'app_data=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict'; | |
| document.cookie = 'current_page=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/; SameSite=Strict'; | |
| console.log("Cookies cleared"); | |
| localStorage.clear(); | |
| this.redirectToLogin(); | |
| } catch (error) { | |
| console.error("Logout failed:", error); | |
| alert("Logout failed. Please try again."); | |
| } finally { | |
| document.body.removeChild(loadingElement); | |
| } | |
| }, | |
| async fetchProfileInfo() { | |
| try { | |
| const { data: { user }, error } = await supabaseClient.auth.getUser(); | |
| if (error) throw error; | |
| this.userName = user.user_metadata.full_name || user.email || 'User'; | |
| await this.checkAllowlist(user.email); | |
| } catch (error) { | |
| console.error("Error fetching profile info:", error); | |
| this.error = 'Error loading profile information.'; | |
| } | |
| } | |
| }, | |
| async mounted() { | |
| console.log("Component mounted"); | |
| const storedAppData = getCookie('app_data'); | |
| const storedCurrentPage = getCookie('current_page'); | |
| if (storedAppData && storedCurrentPage) { | |
| console.log("Found stored data in cookies"); | |
| this.appData = storedAppData; | |
| this.currentPage = storedCurrentPage; | |
| this.loading = false; | |
| console.log("Set app data and current page from cookies:", this.appData, this.currentPage); | |
| } else { | |
| console.log("No stored data found in cookies"); | |
| const session = await this.checkAuth(); | |
| if (!session) { | |
| this.redirectToLogin(); | |
| } else { | |
| await this.fetchProfileInfo(); | |
| } | |
| } | |
| } | |
| }); | |
| app.mount('#app'); | |
| </script> | |
| </body> | |
| </html> | |