You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Cloud Run服务SSL证书不匹配导致Chrome扩展Fetch请求失败的排查及证书刷新方法咨询

Cloud Run服务SSL证书不匹配导致Chrome扩展Fetch请求失败的排查及证书刷新方法咨询

最近我碰到了一个棘手的问题:Chrome扩展里调用Google Cloud Run服务时,Fetch请求直接抛出TypeError: Failed to fetch错误。后来用curl直接测试服务端点,发现根源是SSL证书不匹配,报错信息如下:

curl: (60) SSL: no alternative certificate subject name matches target host name 'command-processor-[PROJECT_ID]-us-central1.run.app'

我的核心需求

让Chrome扩展的弹窗通过POST请求正常调用Cloud Run服务

已经排查验证过的内容

我逐一检查了这些可能的问题点,结果都没问题:

  • Cloud Run服务部署完全成功,没有出现“容器启动失败”类的错误
  • 扩展的manifest.json已经正确配置了host_permissions:https://*.run.app/
  • 扩展清单文件里也包含了"downloads"权限
  • 扩展Fetch请求里的URL和部署后的服务URL完全一致

看起来问题肯定出在Cloud Run服务本身的SSL证书配置或者映射上,但我很疑惑——这是Google默认分配的.run.app域名啊,怎么会出现证书不匹配的情况?

我想咨询的问题

  1. 为什么默认分配.run.app域名的Cloud Run服务会出现SSL证书不匹配的问题?
  2. 在Google Cloud控制台里,有哪些具体步骤可以调试这个问题?或者有没有办法强制刷新服务的SSL证书?

后端代码(index.js)

这是我用Cloud Functions Framework写的后端代码:

// index.js (Corrected for Google Cloud Functions Framework)
const functions = require('@google-cloud/functions-framework');
const { google } = require('googleapis');
// This is the correct way to use cors with this framework
const cors = require('cors')({ origin: true });

// IMPORTANT: Replace this with your actual API key if needed.
const API_KEY = '**************';

// This is the single entry point for your Cloud Function
functions.http("commandProcessor", (req, res) => {
  // The cors function handles the OPTIONS pre-flight request and then calls the callback.
  cors(req, res, async () => {
    try {
      // --- Route 1: Handle the new /summarize-chat endpoint ---
      if (req.path === '/summarize-chat') {
        if (req.method !== 'POST') {
          return res.status(405).send('Method Not Allowed');
        }
        console.log("Handling request for /summarize-chat");
        const transcript = req.body.transcript;
        if (!transcript) {
          return res.status(400).json({ error: 'No transcript provided.' });
        }
        const summary = `### Chat Session Summary\n**Date:** ${new Date().toISOString()}\n\n**Raw Transcript:**\n---\n${transcript}\n---`;
        return res.status(200).json({ summary: summary });
      }

      // --- Route 2: Handle all other requests as Drive commands (your original logic) ---
      console.log("Handling request for Drive command processor.");
      if (req.get('x-api-key') !== API_KEY) {
        console.error("Unauthorized request: API key missing or incorrect.");
        return res.status(401).send('Unauthorized');
      }

      if (req.method !== 'POST') {
        return res.status(405).send('Method Not Allowed. Only POST is supported.');
      }

      const commandObject = req.body;
      // ... (The rest of your original Drive command logic starts here and is unchanged)
      if (!commandObject || !commandObject.command || !commandObject.parameters) {
        console.error("Invalid command format received. Expected {command: 'CMD', parameters: [...]}.");
        return res.status(400).send("Invalid command format.");
      }

      const authHeader = req.get('Authorization');
      let userAuthToken = null;
      if (authHeader && authHeader.startsWith('Bearer ')) {
        userAuthToken = authHeader.substring(7);
      } else {
        console.warn("No Bearer token provided by client. This command might fail if it requires user-specific Drive access.");
      }

      console.log(`Cloud Run: Received raw command object: ${JSON.stringify(commandObject)}`);
      console.log(`Cloud Run: Command extracted for switch: ${commandObject.command.toUpperCase()}`);

      let result;
      switch (commandObject.command.toUpperCase()) {
        case 'DRIVE_CREATE_PROJECT':
          result = await createProjectInDrive(commandObject.parameters[0], userAuthToken);
          break;
        // Add your other cases for DRIVE_CREATE_FILE, DRIVE_MOVE_FILE, etc. back in here
        default:
          result = { success: false, message: `Unknown Drive command: ${commandObject.command}.` };
          break;
      }

      if (result.success) {
        res.status(200).json({ status: 'success', message: result.message, data: result.data });
      } else {
        res.status(500).json({ status: 'error', message: result.message });
      }
    } catch (error) {
      console.error('Unhandled error in function:', error);
      res.status(500).json({ status: 'error', message: `Internal server error: ${error.message}` });
    }
  });
});

// --- All existing Google Drive helper functions remain here, unchanged ---
async function getDriveClient(token) {
  // ... (your existing function)
}

async function createProjectInDrive(projectName, token) {
  // ... (your existing function)
}
// ... etc for all other helper functions.

Chrome扩展清单文件(manifest.json)

{
  "manifest_version": 3,
  "name": "My Life AI Drive Extension",
  "version": "1.2",
  "description": "A private extension to allow My Life ...",
  "host_permissions": ["https://*.run.app/"],
  "permissions": ["downloads"]
}

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 08:59:36