liuq 4 månader sedan
förälder
incheckning
1953604a5d
3 ändrade filer med 193 tillägg och 15 borttagningar
  1. 55 14
      src/components/NavigationTV.vue
  2. 3 1
      src/data/navigation.json
  3. 135 0
      src/utils/homeAssistantAuth.js

+ 55 - 14
src/components/NavigationTV.vue

@@ -79,34 +79,75 @@ const switchTab = (index) => {
   focusedIndex.value = -1
 }
 
-const handleLinkClick = (event, link) => {
+const handleLinkClick = async (event, link) => {
+  // 阻止默认行为
+  event.preventDefault();
+  event.stopPropagation();
+  
   if (link.autoLogin) {
     console.log('========================================');
     console.log('[前端] 检测到自动登录链接');
     console.log('[前端] 链接名称:', link.name);
-    console.log('[前端] 自动登录端点:', link.autoLoginEndpoint);
+    console.log('[前端] 自动登录方式:', link.autoLoginMethod || 'backend');
     
     try {
-      const autoLoginUrl = getAutoLoginUrl(link.autoLoginEndpoint);
-      console.log('[前端] 完整自动登录URL:', autoLoginUrl);
-      console.log('[前端] 准备在新标签页打开后端端点...');
-      
-      // 阻止默认行为
-      event.preventDefault();
-      event.stopPropagation();
-      
-      // 在新标签页打开自动登录链接
-      window.open(autoLoginUrl, '_blank', 'noopener,noreferrer');
+      // 前端自动登录方式(推荐)
+      if (link.autoLoginMethod === 'frontend') {
+        console.log('[前端] 使用纯前端自动登录');
+        
+        // 动态导入自动登录工具
+        const { autoLoginHomeAssistant } = await import('../utils/homeAssistantAuth.js');
+        
+        // 获取配置
+        const baseUrl = link.url.startsWith('http') ? link.url : `http://${link.url}`;
+        const username = link.autoLoginUsername || 'guest';
+        const password = link.autoLoginPassword || 'guest-888';
+        
+        // 执行自动登录
+        await autoLoginHomeAssistant(baseUrl, username, password);
+        
+      } else if (link.autoLoginMethod === 'direct') {
+        // 直接跳转(利用 Trusted Networks)
+        console.log('[前端] 直接跳转(Trusted Networks)');
+        const targetUrl = link.url.startsWith('http') ? link.url : `http://${link.url}`;
+        window.open(targetUrl, '_blank', 'noopener,noreferrer');
+        
+      } else {
+        // 使用后端代理方式(原有方式)
+        console.log('[前端] 使用后端代理自动登录');
+        const autoLoginUrl = getAutoLoginUrl(link.autoLoginEndpoint);
+        console.log('[前端] 完整自动登录URL:', autoLoginUrl);
+        window.open(autoLoginUrl, '_blank', 'noopener,noreferrer');
+      }
       
     } catch (error) {
       console.error('[前端] 处理自动登录时出错:', error);
-      alert('自动登录失败: ' + error.message);
+      
+      // 如果是 CORS 错误,提示用户
+      if (error.message && (error.message.includes('CORS') || error.message.includes('Failed to fetch'))) {
+        alert(
+          '自动登录失败:跨域限制\n\n' +
+          '由于浏览器安全策略,无法直接访问 Home Assistant API。\n\n' +
+          '建议:\n' +
+          '1. 使用 Trusted Networks 配置(推荐)\n' +
+          '2. 或使用后端代理模式\n\n' +
+          '现在将直接跳转到 Home Assistant 登录页面'
+        );
+      } else {
+        alert('自动登录失败: ' + error.message + '\n\n将跳转到登录页面');
+      }
+      
+      // 失败后直接跳转
+      const targetUrl = link.url.startsWith('http') ? link.url : `http://${link.url}`;
+      window.open(targetUrl, '_blank', 'noopener,noreferrer');
     }
     
     console.log('========================================');
   } else {
-    // 普通链接,允许默认行为(已经在 href 中设置了 target="_blank")
+    // 普通链接
     console.log('[前端] 普通链接:', link.name, link.url);
+    const targetUrl = link.url.startsWith('http') ? link.url : `http://${link.url}`;
+    window.open(targetUrl, '_blank', 'noopener,noreferrer');
   }
 }
 

+ 3 - 1
src/data/navigation.json

@@ -25,7 +25,9 @@
           "background": "",
           "description": "智能自动化平台",
           "autoLogin": true,
-          "autoLoginEndpoint": "/api/auto-login/home-assistant"
+          "autoLoginMethod": "frontend",
+          "autoLoginUsername": "guest",
+          "autoLoginPassword": "guest-888"
         },
         {
           "name": "实验室系统",

+ 135 - 0
src/utils/homeAssistantAuth.js

@@ -0,0 +1,135 @@
+/**
+ * Home Assistant 前端自动登录工具
+ * 纯前端实现,无需后端服务器
+ */
+
+/**
+ * Home Assistant OAuth2 自动登录
+ * @param {string} baseUrl - Home Assistant 基础 URL (例如: http://222.243.138.146:8123)
+ * @param {string} username - 用户名
+ * @param {string} password - 密码
+ * @returns {Promise<boolean>} - 登录是否成功
+ */
+export async function autoLoginHomeAssistant(baseUrl, username, password) {
+  console.log('========================================');
+  console.log('[前端] Home Assistant 自动登录开始');
+  console.log('[前端] 目标 URL:', baseUrl);
+  console.log('[前端] 用户名:', username);
+  console.log('========================================');
+
+  try {
+    // ==========================================
+    // 步骤1: 创建登录流程
+    // ==========================================
+    console.log('[前端] 步骤1: 创建登录流程...');
+    
+    const flowResponse = await fetch(`${baseUrl}/auth/login_flow`, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        client_id: `${baseUrl}/`,
+        handler: ['homeassistant', null],
+        redirect_uri: `${baseUrl}/?auth_callback=1`
+      }),
+      credentials: 'include', // 包含 Cookie
+      mode: 'cors' // 允许跨域
+    });
+
+    if (!flowResponse.ok) {
+      throw new Error(`创建登录流程失败: HTTP ${flowResponse.status}`);
+    }
+
+    const flowData = await flowResponse.json();
+    console.log('[前端] 流程创建响应:', flowData);
+
+    const flowId = flowData.flow_id;
+    if (!flowId) {
+      throw new Error('无法获取 flow_id');
+    }
+
+    console.log('[前端] 获取到 flow_id:', flowId);
+
+    // ==========================================
+    // 步骤2: 提交用户名和密码
+    // ==========================================
+    console.log('[前端] 步骤2: 提交用户名和密码...');
+
+    const loginResponse = await fetch(`${baseUrl}/auth/login_flow/${flowId}`, {
+      method: 'POST',
+      headers: {
+        'Content-Type': 'application/json',
+      },
+      body: JSON.stringify({
+        username: username,
+        password: password,
+        client_id: `${baseUrl}/`
+      }),
+      credentials: 'include',
+      mode: 'cors'
+    });
+
+    if (!loginResponse.ok) {
+      throw new Error(`登录失败: HTTP ${loginResponse.status}`);
+    }
+
+    const loginData = await loginResponse.json();
+    console.log('[前端] 登录响应:', loginData);
+
+    // ==========================================
+    // 步骤3: 处理 Authorization Code
+    // ==========================================
+    if (loginData.result && loginData.type === 'create_entry') {
+      const authCode = loginData.result;
+      console.log('[前端] 登录成功!获取到 Authorization Code:', authCode);
+
+      // 构造重定向 URL
+      const redirectUrl = `${baseUrl}/?auth_callback=1&code=${encodeURIComponent(authCode)}`;
+      
+      console.log('[前端] 准备跳转到:', redirectUrl);
+      console.log('[前端] Home Assistant 前端将自动完成 Token 交换');
+      console.log('========================================');
+
+      // 跳转到 Home Assistant,完成登录
+      window.location.href = redirectUrl;
+      return true;
+    } else {
+      throw new Error('登录失败: 未返回 Authorization Code');
+    }
+
+  } catch (error) {
+    console.error('[前端] 自动登录失败:', error);
+    console.error('[前端] 错误详情:', error.message);
+    
+    // 如果是 CORS 错误,提供解决方案提示
+    if (error.message.includes('CORS') || error.message.includes('fetch')) {
+      console.error('[前端] ⚠️ 检测到 CORS 跨域错误!');
+      console.error('[前端] 解决方案:');
+      console.error('[前端] 1. 配置 Home Assistant 允许跨域请求');
+      console.error('[前端] 2. 使用 Trusted Networks(推荐)');
+      console.error('[前端] 3. 使用后端代理服务');
+      
+      alert(
+        '自动登录失败:跨域限制\n\n' +
+        '由于浏览器安全策略,无法直接访问 Home Assistant API。\n\n' +
+        '建议使用 Trusted Networks 配置实现自动登录。\n\n' +
+        '是否跳转到 Home Assistant 登录页面?'
+      );
+    }
+    
+    console.log('========================================');
+    return false;
+  }
+}
+
+/**
+ * 简化版:直接跳转(利用 Trusted Networks)
+ * @param {string} baseUrl - Home Assistant 基础 URL
+ */
+export function directJumpHomeAssistant(baseUrl) {
+  console.log('[前端] 直接跳转到 Home Assistant:', baseUrl);
+  console.log('[前端] 如果已配置 Trusted Networks,将自动登录');
+  window.location.href = baseUrl;
+}
+