一、 部署方式

  • 登录 Cloudflare:
  • 前往 Cloudflare 仪表板 并登录。
  • 创建 Worker:
  • 在左侧菜单中,找到并点击 Workers & Pages。
  • 点击 创建应用程序 (Create Application),然后选择 创建 Worker (Create Worker)。
  • 为您的 Worker 设置一个唯一的子域名(例如 my-onedrive-tool),然后点击 部署 (Deploy)。
  • 编辑并粘贴代码:
  • 部署成功后,点击 编辑代码 (Edit code) 进入在线编辑器。
  • 编辑器中会有一段默认的 "Hello World" 模板代码,请将其 全部删除。
  • 将本仓库中的 worker.js 文件里的 全部代码 复制出来。
  • 将代码粘贴到 Cloudflare 在线编辑器中。
  • 保存并完成:
  • 点击编辑器右上角的 保存并部署 (Save and Deploy) 按钮。
  • 稍等片刻,部署就会完成。现在您可以通过访问您的 Worker URL (https://your-worker-name.your-subdomain.workers.dev) 来使用这个工具了!

    // --- Backend API Logic ---
    
    /**
     * Handles CORS preflight requests.
     * @returns {Response}
     */
    function handleOptions() {
    return new Response(null, {
      headers: getCorsHeaders(),
    });
    }
    
    /**
     * Creates a JSON response with appropriate headers.
     * @param {object} body - The response body.
     * @param {number} status - The HTTP status code.
     * @returns {Response}
     */
    function createJsonResponse(body, status) {
    return new Response(JSON.stringify(body, null, 2), {
      status: status,
      headers: {
        'Content-Type': 'application/json',
        ...getCorsHeaders(),
      },
    });
    }
    
    /**
     * Gets the required CORS headers.
     * @returns {object}
     */
    function getCorsHeaders() {
      return {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'POST, OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type',
      };
    }
    
    
    /**
     * Handles the token acquisition request from the frontend.
     * It exchanges the authorization code for an access token and refresh token.
     * @param {Request} request - The incoming request.
     * @returns {Promise<Response>}
     */
    async function handleTokenRequest(request) {
    const { code, redirect_uri, cloud_env, client_id, client_secret } = await request.json();
    
    if (!code || !redirect_uri || !cloud_env || !client_id || !client_secret) {
      return createJsonResponse({ error: '请求体中缺少必要参数。' }, 400);
    }
    
    const tokenUrl = cloud_env === 'china'
      ? 'https://login.chinacloudapi.cn/common/oauth2/v2.0/token'
      : 'https://login.microsoftonline.com/common/oauth2/v2.0/token';
    
    const body = new URLSearchParams({
      client_id: client_id,
      client_secret: client_secret,
      code: code,
      redirect_uri: redirect_uri,
      grant_type: 'authorization_code',
      scope: 'offline_access Files.ReadWrite.All Sites.Read.All',
    });
    
    try {
      const tokenResponse = await fetch(tokenUrl, {
        method: 'POST',
        headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
        body: body,
      });
    
      const tokenData = await tokenResponse.json();
    
      if (!tokenResponse.ok) {
        return createJsonResponse(tokenData, tokenResponse.status);
      }
    
      return createJsonResponse(tokenData, 200);
    } catch (error) {
      return createJsonResponse({ error: '从微软获取令牌失败。', details: error.message }, 500);
    }
    }
    
    /**
     * Handles the SharePoint Site ID request from the frontend.
     * It uses the provided access token to query the Microsoft Graph API.
     * @param {Request} request - The incoming request.
     * @returns {Promise<Response>}
     */
    async function handleSiteIdRequest(request) {
      const { accessToken, cloudEnv, hostname, sitePath } = await request.json();
    
      if (!accessToken || !cloudEnv || !hostname) {
          return createJsonResponse({ error: '查询 Site ID 的请求体中缺少必要参数。' }, 400);
      }
      
      const graphEndpoint = cloudEnv === 'china'
          ? 'https://microsoftgraph.chinacloudapi.cn'
          : 'https://graph.microsoft.com';
    
      // Construct the URL for the Graph API call
      const relativePath = sitePath && sitePath !== '/' ? `:${sitePath}` : '';
      const siteUrl = `${graphEndpoint}/v1.0/sites/${hostname}${relativePath}`;
    
      try {
          const siteResponse = await fetch(siteUrl, {
              method: 'GET',
              headers: {
                  'Authorization': `Bearer ${accessToken}`,
              },
          });
    
          const siteData = await siteResponse.json();
    
          if (!siteResponse.ok) {
              return createJsonResponse(siteData, siteResponse.status);
          }
    
          return createJsonResponse(siteData, 200);
      } catch (error) {
           return createJsonResponse({ error: '从微软 Graph 获取 Site ID 失败。', details: error.message }, 500);
      }
    }
    
    
    // --- Frontend HTML & JS ---
    /**
     * Generates the full HTML content for the tool's UI.
     * @returns {string} - The HTML content as a string.
     */
    function getHtmlContent() {
    return `
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
      <meta charset="UTF-8">
      <meta name="viewport" content="width=device-width, initial-scale=1.0">
      <title>一体化 OneDrive & SharePoint 工具</title>
      <script src="https://cdn.tailwindcss.com"></script>
      <link rel="preconnect" href="https://fonts.googleapis.com">
      <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
      <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
      <style>
          body { font-family: 'Inter', 'Noto Sans SC', sans-serif; }
          .break-all { word-break: break-all; }
          .copy-btn {
              position: absolute;
              top: 50%;
              right: 0.5rem;
              transform: translateY(-50%);
              padding: 0.25rem 0.5rem;
              font-size: 0.75rem;
              border-radius: 0.375rem;
              background-color: #4B5563; /* bg-gray-600 */
              color: white;
              cursor: pointer;
              border: none;
              opacity: 0.6;
              transition: opacity 0.2s;
          }
          .copy-btn:hover { opacity: 1; }
          .token-wrapper { position: relative; }
          .result-wrapper { position: relative; }
      </style>
    </head>
    <body class="bg-gray-100 dark:bg-gray-900 text-gray-800 dark:text-gray-200">
      <div class="container mx-auto p-4 md:p-8 max-w-4xl">
          <header class="text-center mb-8">
              <h1 class="text-3xl md:text-4xl font-bold text-gray-900 dark:text-white">一体化 OneDrive & SharePoint 工具</h1>
              <p class="mt-2 text-gray-600 dark:text-gray-400">一键获取刷新令牌和 SharePoint Site ID</p>
          </header>
          
          <div class="bg-red-100 dark:bg-red-900/30 border-l-4 border-red-500 text-red-700 dark:text-red-300 p-4 rounded-md mb-8" role="alert">
              <p class="font-bold">安全警告</p>
              <p>在此页面填写客户端密码 (Client Secret) 存在一定风险。请仅在您自己的电脑和信任的网络环境下使用此工具。</p>
          </div>
    
          <div class="bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 mb-8">
              <h2 class="text-2xl font-semibold mb-4 border-b pb-2 dark:border-gray-600">步骤 1: 应用配置 & 获取令牌</h2>
              <div class="grid grid-cols-1 gap-6">
                  <div>
                      <label for="clientId" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">应用 ID (Client ID)</label>
                      <input type="text" id="clientId" placeholder="您的Azure应用ID" class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
                  </div>
                  <div>
                      <label for="clientSecret" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">客户端密码 (Client Secret)</label>
                      <input type="password" id="clientSecret" placeholder="您的Azure应用客户端密码" class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
                  </div>
                   <div>
                      <label for="redirectUri" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">重定向 URI (Redirect URI)</label>
                      <div class="relative">
                          <input type="text" id="redirectUri" readonly class="w-full pl-3 pr-16 py-2 bg-gray-200 dark:bg-gray-600 border border-gray-300 dark:border-gray-500 rounded-md shadow-sm">
                          <button id="copyRedirectUriBtn" class="copy-btn">复制</button>
                      </div>
                      <p class="mt-1 text-xs text-gray-500 dark:text-gray-400">将此URL复制到您Azure应用的Web平台重定向URI中。</p>
                  </div>
                  <div>
                      <label for="cloudEnv" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">云环境</label>
                      <select id="cloudEnv" class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
                          <option value="global">Microsoft 全球云 (Global)</option>
                          <option value="china">由世纪互联运营的 Microsoft Azure (21Vianet)</option>
                      </select>
                  </div>
              </div>
               <div class="mt-6 text-center">
                   <button id="getAuthCodeBtn" class="px-8 py-3 bg-blue-600 text-white font-semibold rounded-md shadow-sm hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 dark:focus:ring-offset-gray-900 transition-colors text-lg">
                      开始认证
                  </button>
               </div>
          </div>
    
          <div id="result-container" class="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 hidden">
              <h2 class="text-2xl font-semibold mb-4 border-b pb-2 dark:border-gray-600">令牌结果</h2>
              <div id="spinner" class="hidden flex justify-center items-center my-4">
                  <svg class="animate-spin -ml-1 mr-3 h-8 w-8 text-blue-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
                  <span class="text-lg">正在从后端获取令牌...</span>
              </div>
              <div id="result-content"></div>
          </div>
    
          <div id="site-id-finder" class="mt-8 bg-white dark:bg-gray-800 rounded-lg shadow-md p-6 hidden">
              <h2 class="text-2xl font-semibold mb-4 border-b pb-2 dark:border-gray-600">步骤 2: 获取 SharePoint Site ID (可选)</h2>
              <p class="text-sm text-gray-500 dark:text-gray-400 mb-4">使用上方获取的访问令牌来查询 Site ID。</p>
              <div class="grid grid-cols-1 gap-6">
                  <div>
                      <label for="sharepointUrl" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">SharePoint 网站 URL</label>
                      <input type="url" id="sharepointUrl" placeholder="https://contoso.sharepoint.com/sites/MyTeamSite" class="w-full px-3 py-2 bg-gray-50 dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
                  </div>
              </div>
              <div class="text-center mt-6">
                  <button id="getSiteIdBtn" class="px-6 py-2 bg-green-600 text-white font-semibold rounded-md shadow-sm hover:bg-green-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-green-500 dark:focus:ring-offset-gray-900 transition-colors">获取 Site ID</button>
              </div>
              <div id="site-id-result-container" class="mt-6 hidden">
                  <div id="site-id-spinner" class="hidden flex justify-center items-center my-4">
                       <svg class="animate-spin -ml-1 mr-3 h-6 w-6 text-green-500" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24"><circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4"></circle><path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path></svg>
                      <span class="text-lg">正在查询 Site ID...</span>
                  </div>
                  <div id="site-id-result-content"></div>
              </div>
          </div>
    
      </div>
    
      <script>
          // --- DOM Elements ---
          const clientIdInput = document.getElementById('clientId');
          const clientSecretInput = document.getElementById('clientSecret');
          const cloudEnvSelect = document.getElementById('cloudEnv');
          const redirectUriInput = document.getElementById('redirectUri');
          const copyRedirectUriBtn = document.getElementById('copyRedirectUriBtn');
          const getAuthCodeBtn = document.getElementById('getAuthCodeBtn');
          const resultContainer = document.getElementById('result-container');
          const resultContent = document.getElementById('result-content');
          const spinner = document.getElementById('spinner');
    
          const siteIdFinder = document.getElementById('site-id-finder');
          const sharepointUrlInput = document.getElementById('sharepointUrl');
          const getSiteIdBtn = document.getElementById('getSiteIdBtn');
          const siteIdResultContainer = document.getElementById('site-id-result-container');
          const siteIdSpinner = document.getElementById('site-id-spinner');
          const siteIdResultContent = document.getElementById('site-id-result-content');
    
          // --- App State ---
          let appState = {
              accessToken: null,
              cloudEnv: null
          };
          
          const authEndpoints = {
              global: 'https://login.microsoftonline.com/common/oauth2/v2.0/authorize',
              china: 'https://login.chinacloudapi.cn/common/oauth2/v2.0/authorize',
          };
    
          // --- Utility Functions ---
          function copyToClipboard(text, button) {
              const textarea = document.createElement('textarea');
              textarea.value = text;
              document.body.appendChild(textarea);
              textarea.select();
              document.execCommand('copy');
              document.body.removeChild(textarea);
              const originalText = button.textContent;
              button.textContent = '已复制!';
              setTimeout(() => { button.textContent = originalText; }, 2000);
          }
    
          function showResult(title, content, isError = false) {
              spinner.classList.add('hidden');
              resultContainer.classList.remove('hidden');
              const titleColor = isError ? 'text-red-500 dark:text-red-400' : 'text-green-600 dark:text-green-400';
              let formattedContent = \`\${content}\`;
              if (isError) {
                  try {
                      const errorJson = JSON.parse(content);
                      formattedContent = \`<pre class="p-4 bg-gray-100 dark:bg-gray-900 rounded-md break-all font-mono text-sm">\${JSON.stringify(errorJson, null, 2)}</pre>\`;
                  } catch(e) {
                       formattedContent = \`<div class="p-4 bg-gray-100 dark:bg-gray-900 rounded-md break-all font-mono text-sm">\${content}</div>\`;
                  }
              }
              resultContent.innerHTML = \`<h4 class="font-bold text-lg mb-2 \${titleColor}">\${title}</h4>\${formattedContent}\`;
          }
    
          function showSiteIdResult(title, content, isError = false) {
              siteIdSpinner.classList.add('hidden');
              siteIdResultContainer.classList.remove('hidden');
              const titleColor = isError ? 'text-red-500 dark:text-red-400' : 'text-green-600 dark:text-green-400';
               let formattedContent = content;
              if (isError) {
                   try {
                      const errorJson = JSON.parse(content);
                      formattedContent = \`<pre class="p-4 bg-gray-100 dark:bg-gray-900 rounded-md break-all font-mono text-sm">\${JSON.stringify(errorJson, null, 2)}</pre>\`;
                  } catch(e) {
                       formattedContent = \`<div class="p-4 bg-gray-100 dark:bg-gray-900 rounded-md break-all font-mono text-sm">\${content}</div>\`;
                  }
              }
              siteIdResultContent.innerHTML = \`<h4 class="font-bold text-lg mb-2 \${titleColor}">\${title}</h4>\${formattedContent}\`;
          }
    
          // --- Core Logic ---
          function handleStartAuth() {
              const clientId = clientIdInput.value;
              const clientSecret = clientSecretInput.value;
    
              if (!clientId || !clientSecret) {
                  alert('请输入应用 ID 和客户端密码');
                  return;
              }
    
              localStorage.setItem('ms_graph_tool_config', JSON.stringify({
                  clientId: clientId,
                  clientSecret: clientSecret,
                  cloudEnv: cloudEnvSelect.value,
                  sharepointUrl: sharepointUrlInput.value
              }));
    
              const scope = 'offline_access Files.ReadWrite.All Sites.Read.All';
              const redirectUri = window.location.origin + window.location.pathname;
              const authUrl = \`\${authEndpoints[cloudEnvSelect.value]}?client_id=\${clientId}&scope=\${scope}&response_type=code&redirect_uri=\${redirectUri}\`;
              window.location.href = authUrl;
          }
    
          async function handleGetToken(code) {
                const config = JSON.parse(localStorage.getItem('ms_graph_tool_config') || '{}');
                if(!config.clientId || !config.clientSecret) {
                    showResult('错误', '找不到本地存储的ID和密钥,请重新填写。', true);
                    return;
                }
                spinner.classList.remove('hidden');
                resultContainer.classList.remove('hidden');
                resultContent.innerHTML = '';
                
                try {
                    const response = await fetch('./api/token', {
                        method: 'POST',
                        headers: {'Content-Type': 'application/json'},
                        body: JSON.stringify({
                            code: code,
                            redirect_uri: window.location.origin + window.location.pathname,
                            cloud_env: config.cloudEnv,
                            client_id: config.clientId,
                            client_secret: config.clientSecret
                        })
                    });
                    
                    const data = await response.json();
    
                    if (!response.ok) {
                        throw new Error(JSON.stringify(data));
                    }
                    
                    appState.accessToken = data.access_token;
                    appState.cloudEnv = config.cloudEnv;
    
                    const resultHtml = \`
                        <div class="space-y-4">
                            <div class="result-wrapper"><p class="font-semibold text-gray-700 dark:text-gray-300">刷新令牌 (Refresh Token):</p><div class="relative"><p class="text-sm p-3 bg-gray-100 dark:bg-gray-700 rounded break-all" id="refreshTokenText">\${data.refresh_token}</p><button class="copy-btn" onclick="copyToClipboard(document.getElementById('refreshTokenText').textContent, this)">复制</button></div></div>
                            <div class="result-wrapper"><p class="font-semibold text-gray-700 dark:text-gray-300">访问令牌 (Access Token):</p><div class="relative"><p class="text-sm p-3 bg-gray-100 dark:bg-gray-700 rounded break-all" id="accessTokenText">\${data.access_token}</p><button class="copy-btn" onclick="copyToClipboard(document.getElementById('accessTokenText').textContent, this)">复制</button></div></div>
                        </div>\`;
                    showResult('令牌获取成功!', resultHtml);
    
                    siteIdFinder.classList.remove('hidden');
    
                } catch (error) {
                     showResult('获取令牌失败', error.message, true);
                }
          }
          
          async function handleGetSiteId() {
              const fullUrl = sharepointUrlInput.value;
              if (!fullUrl) {
                  alert('请输入 SharePoint 网站 URL');
                  return;
              }
              if (!appState.accessToken) {
                   alert('访问令牌不可用。请先完成步骤1。');
                   return;
              }
    
              let hostname, sitePath;
              try {
                  const urlObject = new URL(fullUrl);
                  hostname = urlObject.hostname;
                  sitePath = urlObject.pathname;
              } catch (e) {
                  alert('输入的 SharePoint URL 格式无效。');
                  return;
              }
              
              const config = JSON.parse(localStorage.getItem('ms_graph_tool_config') || '{}');
              config.sharepointUrl = fullUrl;
              localStorage.setItem('ms_graph_tool_config', JSON.stringify(config));
    
              siteIdSpinner.classList.remove('hidden');
              siteIdResultContainer.classList.remove('hidden');
              siteIdResultContent.innerHTML = '';
    
              try {
                  const response = await fetch('./api/site-id', {
                      method: 'POST',
                      headers: {'Content-Type': 'application/json'},
                      body: JSON.stringify({
                          accessToken: appState.accessToken,
                          cloudEnv: appState.cloudEnv,
                          hostname: hostname,
                          sitePath: sitePath
                      })
                  });
    
                  const data = await response.json();
    
                  if (!response.ok) {
                      throw new Error(JSON.stringify(data));
                  }
                  
                  const siteId = data.id;
                  const resultHtml = \`
                      <div class="result-wrapper">
                        <p class="font-semibold text-gray-700 dark:text-gray-300">网站信息:</p>
                        <div class="relative text-sm p-3 bg-gray-100 dark:bg-gray-700 rounded break-all" id="siteIdText">
                          <p><strong>ID:</strong> \${siteId}</p>
                          <p><strong>名称:</strong> \${data.displayName}</p>
                          <p><strong>URL:</strong> <a href="\${data.webUrl}" target="_blank" class="text-blue-500 hover:underline">\${data.webUrl}</a></p>
                          <button class="copy-btn" style="top: 1rem; transform: none;" onclick="copyToClipboard('\${siteId}', this)">复制ID</button>
                        </div>
                      </div>
                  \`;
    
                  showSiteIdResult('Site ID 获取成功!', resultHtml);
    
              } catch (error) {
                  showSiteIdResult('获取 Site ID 失败', error.message, true);
              }
          }
    
          // --- Event Listeners & Page Load ---
          window.onload = () => {
              const savedConfig = JSON.parse(localStorage.getItem('ms_graph_tool_config') || '{}');
              if (savedConfig.clientId) clientIdInput.value = savedConfig.clientId;
              if (savedConfig.clientSecret) clientSecretInput.value = savedConfig.clientSecret;
              if (savedConfig.cloudEnv) cloudEnvSelect.value = savedConfig.cloudEnv;
              if (savedConfig.sharepointUrl) sharepointUrlInput.value = savedConfig.sharepointUrl;
    
              const redirectUri = window.location.origin + window.location.pathname;
              redirectUriInput.value = redirectUri;
    
              const urlParams = new URLSearchParams(window.location.search);
              const code = urlParams.get('code');
    
              if (code) {
                  handleGetToken(code);
                  window.history.replaceState({}, document.title, window.location.pathname);
              }
              
              getAuthCodeBtn.addEventListener('click', handleStartAuth);
              getSiteIdBtn.addEventListener('click', handleGetSiteId);
              copyRedirectUriBtn.addEventListener('click', (e) => {
                  e.preventDefault();
                  copyToClipboard(redirectUriInput.value, e.target);
              });
          };
      </script>
    </body>
    </html>
    `;
    }
    
    // --- Worker Main Logic & Router ---
    export default {
    async fetch(request) {
      const url = new URL(request.url);
    
      // Handle API routes
      if (url.pathname.startsWith('/api/')) {
          if (request.method === 'OPTIONS') {
              return handleOptions();
          }
          if (url.pathname === '/api/token' && request.method === 'POST') {
            return handleTokenRequest(request);
          }
          if (url.pathname === '/api/site-id' && request.method === 'POST') {
              return handleSiteIdRequest(request);
          }
      }
    
      // Serve HTML for the root path
      if (url.pathname === '/' && request.method === 'GET') {
        return new Response(getHtmlContent(), {
          headers: { 'Content-Type': 'text/html;charset=utf-8' },
        });
      }
      
      return new Response('未找到', { status: 404 });
    },
    };
    

    cf_onedrive_woker_js.txt

二、 创建应用

前提条件:注册 Azure 应用
https://portal.azure.com/#view/Microsoft_AAD_IAM/ActiveDirectoryMenuBlade/~/RegisteredApps

  • 在打开的页面,选择所在区域,点击创建应用
  • 登陆后选择"注册应用程序",输入"名称",选择"任何组织目录中的账户和个人"(注意这里不要看位置选择而是看文字,部分人可能是中间那个选项,不要选成单一租户或者其他选项,否则会导致登陆时出现问题),输入重定向 URL 为 https://your-worker-name.your-subdomain.workers.dev ,点击注册即可,然后可以得到 client_id
    onedrive-register-app.png
  • 注册好应用程序之后,选择"证书和密码",点击"新客户端密码",输入一串密码,选择时间为最长的那个,点击"添加"
    (注:在添加之后输入的密码之后会消失,请记录下来 client_secret 的值)
    onedrive-new-password.png
  • 选择 "API 权限",点击 "Microsoft Graph",在"选择权限"中输入 file,勾选 Files.read(注:Files.read 是只读最小权限,图中权限较大,也同样可以),点击"确定"
    onedrive-update-permission.png

三、操作步骤

获取令牌:

  • 打开您部署好的 Worker URL。
  • 在 步骤 1 的表单中,填入您的 应用 ID (Client ID) 和 客户端密码 (Client Secret)。
  • 根据您的账号类型,选择正确的 云环境。
  • 点击 开始认证 按钮,页面将跳转到微软登录页。
  • 登录并授予应用权限。
  • 页面会自动跳回,并显示获取成功的 刷新令牌 和 访问令牌。

获取 SharePoint Site ID:

  • 令牌获取成功后,步骤 2 的卡片会自动出现。
  • 在 SharePoint 网站 URL 输入框中,粘贴您需要查询的完整 SharePoint 网站地址(例如 https://contoso.sharepoint.com/sites/MyTeamSite)。
  • 点击 获取 Site ID 按钮。
  • 下方会显示查询到的网站信息,包括 Site ID、网站名称 和 URL。

⚠️ 安全警告
尽管此工具通过后端代理来处理 client_secret,但您仍然是在一个网页中输入您的凭据。请务必在您自己信任的电脑和网络环境下使用,并确保您部署的 Worker 是私密的。不要在公共场合或不安全的网络中使用。