diff --git a/scripts/main.js b/scripts/main.js index 244bfc9..d413f46 100644 --- a/scripts/main.js +++ b/scripts/main.js @@ -181,13 +181,25 @@ async function initializeSDK() { try { const serverAddress = elements.serverAddress.value.trim(); const robotId = elements.robotId.value.trim(); - const onceCode = elements.onceCode.value.trim(); + let onceCode = elements.onceCode.value.trim(); if (!serverAddress) { logger.error('请输入服务器地址'); return; } + if (!onceCode) { + logger.info('授权码为空,正在自动获取...'); + try { + onceCode = await fetchOnceCode(); + elements.onceCode.value = onceCode; + logger.info('授权码自动获取成功'); + } catch (err) { + logger.error('授权码自动获取失败', err.message); + return; + } + } + logger.info('正在初始化 SDK...'); const isSupported = await HwICSUiSdk.checkBrowserSupport(true); diff --git a/server/APIGW-javascript-sdk-2.0.6/licenses/license-crypto-js b/server/APIGW-javascript-sdk-2.0.6/licenses/license-crypto-js new file mode 100644 index 0000000..6a7c063 --- /dev/null +++ b/server/APIGW-javascript-sdk-2.0.6/licenses/license-crypto-js @@ -0,0 +1,24 @@ +# License + +[The MIT License (MIT)](http://opensource.org/licenses/MIT) + +Copyright (c) 2009-2013 Jeff Mott +Copyright (c) 2013-2016 Evan Vosberg + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/server/APIGW-javascript-sdk-2.0.6/licenses/license-node b/server/APIGW-javascript-sdk-2.0.6/licenses/license-node new file mode 100644 index 0000000..807debc --- /dev/null +++ b/server/APIGW-javascript-sdk-2.0.6/licenses/license-node @@ -0,0 +1,20 @@ +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/server/APIGW-javascript-sdk-2.0.6/node_demo.js b/server/APIGW-javascript-sdk-2.0.6/node_demo.js new file mode 100644 index 0000000..afd6621 --- /dev/null +++ b/server/APIGW-javascript-sdk-2.0.6/node_demo.js @@ -0,0 +1,37 @@ +var signer = require('./signer'); +var https = require("https"); +var sig = new signer.Signer(); +//Set the AK/SK to sign and authenticate the request. +// 认证用的ak和sk硬编码到代码中或者明文存储都有很大的安全风险,建议在配置文件或者环境变量中密文存放,使用时解密,确保安全; +// 本示例以ak和sk保存在环境变量中为例,运行本示例前请先在本地环境中设置环境变量HUAWEICLOUD_SDK_AK和HUAWEICLOUD_SDK_SK。 +sig.Key = process.env.HUAWEICLOUD_SDK_AK; +sig.Secret = process.env.HUAWEICLOUD_SDK_SK; + +//The following example shows how to set the request URL and parameters to query a VPC list. +//Specify a request method, such as GET, PUT, POST, DELETE, HEAD, and PATCH. +//Set request host. +//Set request URI. +//Set parameters for the request URL. +var r = new signer.HttpRequest("GET", "endpoint.example.com/v1/77b6a44cba5143ab91d13ab9a8ff44fd/vpcs?limie=1"); +//Add header parameters, for example, x-domain-id for invoking a global service and x-project-id for invoking a project-level service. +r.headers = {"Content-Type": "application/json"}; +//Add a body if you have specified the PUT or POST method. Special characters, such as the double quotation mark ("), contained in the body must be escaped. +r.body = ''; + +var opt = sig.Sign(r); +console.log(opt.headers["X-Sdk-Date"]); +console.log(opt.headers["Authorization"]); + +var req = https.request(opt, function (res) { + console.log(res.statusCode); + console.log('headers:', JSON.stringify(res.headers)); + res.on("data", function (chunk) { + console.log(chunk.toString()) + }) +}); + +req.on("error", function (err) { + console.log(err.message) +}); +req.write(r.body); +req.end(); diff --git a/server/APIGW-javascript-sdk-2.0.6/signer.js b/server/APIGW-javascript-sdk-2.0.6/signer.js new file mode 100644 index 0000000..33deb68 --- /dev/null +++ b/server/APIGW-javascript-sdk-2.0.6/signer.js @@ -0,0 +1,374 @@ +// HWS API Gateway Signature +(function (root, factory) { + "use strict"; + + /*global define*/ + if (typeof define === 'function' && define.amd) { + // AMD + define(['CryptoJS'], function (CryptoJS) { + var crypto_wrapper = { + hmacsha256: function (keyByte, message) { + return CryptoJS.HmacSHA256(message, keyByte).toString(CryptoJS.enc.Hex) + }, + HexEncodeSHA256Hash: function (body) { + return CryptoJS.SHA256(body) + } + }; + return factory(crypto_wrapper) + }); + } else if (typeof wx === 'object') { + // wechat + var CryptoJS = require('./js/hmac-sha256.js'); + var crypto_wrapper = { + hmacsha256: function (keyByte, message) { + return CryptoJS.HmacSHA256(message, keyByte).toString(CryptoJS.enc.Hex) + }, + HexEncodeSHA256Hash: function (body) { + return CryptoJS.SHA256(body) + } + }; + module.exports = factory(crypto_wrapper); + } else if (typeof module === 'object' && module.exports) { + // Node + var crypto = require('crypto'); + var crypto_wrapper = { + hmacsha256: function (keyByte, message) { + return crypto.createHmac('SHA256', keyByte).update(message).digest().toString('hex') + }, + HexEncodeSHA256Hash: function (body) { + return crypto.createHash('SHA256').update(body).digest().toString('hex') + } + }; + module.exports = factory(crypto_wrapper); + } else { + // Browser + var CryptoJS = root.CryptoJS + var crypto_wrapper = { + hmacsha256: function (keyByte, message) { + return CryptoJS.HmacSHA256(message, keyByte).toString(CryptoJS.enc.Hex) + }, + HexEncodeSHA256Hash: function (body) { + return CryptoJS.SHA256(body) + } + }; + root.signer = factory(crypto_wrapper); + } +}(this, function (crypto_wrapper) { + 'use strict'; + + var Algorithm = "SDK-HMAC-SHA256"; + var HeaderXDate = "X-Sdk-Date"; + var HeaderAuthorization = "Authorization"; + var HeaderContentSha256 = "x-sdk-content-sha256"; + + const hexTable = new Array(256); + for (var i = 0; i < 256; ++i) + hexTable[i] = '%' + ((i < 16 ? '0' : '') + i.toString(16)).toUpperCase(); + + const noEscape = [ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0 - 15 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 16 - 31 + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, // 32 - 47 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, // 48 - 63 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 64 - 79 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, // 80 - 95 + 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, // 96 - 111 + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0 // 112 - 127 + ]; + + // function urlEncode is based on https://github.com/nodejs/node/blob/master/lib/querystring.js + // Copyright Joyent, Inc. and other Node contributors. + function urlEncode(str) { + if (typeof str !== 'string') { + if (typeof str === 'object') + str = String(str); + else + str += ''; + } + var out = ''; + var lastPos = 0; + + for (var i = 0; i < str.length; ++i) { + var c = str.charCodeAt(i); + + // ASCII + if (c < 0x80) { + if (noEscape[c] === 1) + continue; + if (lastPos < i) + out += str.slice(lastPos, i); + lastPos = i + 1; + out += hexTable[c]; + continue; + } + + if (lastPos < i) + out += str.slice(lastPos, i); + + // Multi-byte characters ... + if (c < 0x800) { + lastPos = i + 1; + out += hexTable[0xC0 | (c >> 6)] + hexTable[0x80 | (c & 0x3F)]; + continue; + } + if (c < 0xD800 || c >= 0xE000) { + lastPos = i + 1; + out += hexTable[0xE0 | (c >> 12)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; + continue; + } + // Surrogate pair + ++i; + + if (i >= str.length) + throw new errors.URIError('ERR_INVALID_URI'); + + var c2 = str.charCodeAt(i) & 0x3FF; + + lastPos = i + 1; + c = 0x10000 + (((c & 0x3FF) << 10) | c2); + out += hexTable[0xF0 | (c >> 18)] + + hexTable[0x80 | ((c >> 12) & 0x3F)] + + hexTable[0x80 | ((c >> 6) & 0x3F)] + + hexTable[0x80 | (c & 0x3F)]; + } + if (lastPos === 0) + return str; + if (lastPos < str.length) + return out + str.slice(lastPos); + return out; + } + + function HttpRequest(method, url, headers, body) { + if (method === undefined) { + this.method = ""; + } else { + this.method = method; + } + if (url === undefined) { + this.host = ""; + this.uri = ""; + this.query = {}; + } else { + this.query = {}; + var host, path; + var i = url.indexOf("://"); + if (i !== -1) { + url = url.substr(i + 3) + } + var i = url.indexOf("?"); + if (i !== -1) { + var query_str = url.substr(i + 1); + url = url.substr(0, i); + var spl = query_str.split("&"); + for (var i in spl) { + var kv = spl[i]; + var index = kv.indexOf('='); + var key, value; + if (index >= 0) { + key = kv.substr(0, index); + value = kv.substr(index + 1); + } else { + key = kv; + value = ""; + } + if (key !== "") { + key = decodeURI(key); + value = decodeURI(value); + if (this.query[key] === undefined) { + this.query[key] = [value] + } else { + this.query[key].push(value) + } + } + } + } + var i = url.indexOf("/"); + if (i === -1) { + host = url; + path = "/"; + } else { + host = url.substr(0, i); + path = url.substr(i); + } + this.host = host; + this.uri = decodeURI(path); + } + if (headers === undefined) { + this.headers = {}; + } else { + this.headers = headers; + } + if (body === undefined) { + this.body = ""; + } else { + this.body = body; + } + } + + function findHeader(r, header) { + for (var k in r.headers) { + if (k.toLowerCase() === header.toLowerCase()) { + return r.headers[k] + } + } + return null; + } + +// Build a CanonicalRequest from a regular request string +// +// CanonicalRequest = +// HTTPRequestMethod + '\n' + +// CanonicalURI + '\n' + +// CanonicalQueryString + '\n' + +// CanonicalHeaders + '\n' + +// SignedHeaders + '\n' + +// HexEncode(Hash(RequestPayload)) + function CanonicalRequest(r, signedHeaders) { + var hexencode = findHeader(r, HeaderContentSha256); + if (hexencode === null) { + var data = RequestPayload(r); + hexencode = crypto_wrapper.HexEncodeSHA256Hash(data); + } + return r.method + "\n" + CanonicalURI(r) + "\n" + CanonicalQueryString(r) + "\n" + CanonicalHeaders(r, signedHeaders) + "\n" + signedHeaders.join(';') + "\n" + hexencode + } + + function CanonicalURI(r) { + var pattens = r.uri.split('/'); + var uri = []; + for (var k in pattens) { + var v = pattens[k]; + uri.push(urlEncode(v)) + } + var urlpath = uri.join('/'); + if (urlpath[urlpath.length - 1] !== '/') { + urlpath = urlpath + '/' + } + //r.uri = urlpath + return urlpath; + } + + function CanonicalQueryString(r) { + var keys = []; + for (var key in r.query) { + keys.push(key) + } + keys.sort(); + var a = []; + for (var i in keys) { + var key = urlEncode(keys[i]); + var value = r.query[keys[i]]; + if (Array.isArray(value)) { + value.sort(); + for (var iv in value) { + a.push(key + '=' + urlEncode(value[iv])) + } + } else { + a.push(key + '=' + urlEncode(value)) + } + } + return a.join('&'); + } + + function CanonicalHeaders(r, signedHeaders) { + var headers = {}; + for (var key in r.headers) { + headers[key.toLowerCase()] = r.headers[key]; + } + var a = []; + for (var i in signedHeaders) { + var value = headers[signedHeaders[i]]; + a.push(signedHeaders[i] + ':' + value.trim()) + } + return a.join('\n') + "\n" + } + + function SignedHeaders(r) { + var a = []; + for (var key in r.headers) { + a.push(key.toLowerCase()) + } + a.sort(); + return a + } + + function RequestPayload(r) { + return r.body + } + +// Create a "String to Sign". + function StringToSign(canonicalRequest, t) { + var bytes = crypto_wrapper.HexEncodeSHA256Hash(canonicalRequest); + return Algorithm + "\n" + t + "\n" + bytes + } + +// Create the HWS Signature. + function SignStringToSign(stringToSign, signingKey) { + return crypto_wrapper.hmacsha256(signingKey, stringToSign) + } + +// Get the finalized value for the "Authorization" header. The signature +// parameter is the output from SignStringToSign + function AuthHeaderValue(signature, Key, signedHeaders) { + return Algorithm + " Access=" + Key + ", SignedHeaders=" + signedHeaders.join(';') + ", Signature=" + signature + } + + function twoChar(s) { + if (s >= 10) { + return "" + s + } else { + return "0" + s + } + } + + function getTime() { + var date = new Date(); + return "" + date.getUTCFullYear() + twoChar(date.getUTCMonth() + 1) + twoChar(date.getUTCDate()) + "T" + + twoChar(date.getUTCHours()) + twoChar(date.getUTCMinutes()) + twoChar(date.getUTCSeconds()) + "Z" + } + + function Signer() { + this.Key = ""; + this.Secret = ""; + } + + Signer.prototype.Sign = function (r) { + var headerTime = findHeader(r, HeaderXDate); + if (headerTime === null) { + headerTime = getTime(); + r.headers[HeaderXDate] = headerTime + } + if (r.method !== "PUT" && r.method !== "PATCH" && r.method !== "POST") { + r.body = "" + } + var queryString = CanonicalQueryString(r); + if (queryString !== "") { + queryString = "?" + queryString + } + var options = { + hostname: r.host, + path: encodeURI(r.uri) + queryString, + method: r.method, + headers: r.headers + }; + if (findHeader(r, 'host') === null) { + r.headers.host = r.host; + } + var signedHeaders = SignedHeaders(r); + var canonicalRequest = CanonicalRequest(r, signedHeaders); + var stringToSign = StringToSign(canonicalRequest, headerTime); + var signature = SignStringToSign(stringToSign, this.Secret); + options.headers[HeaderAuthorization] = AuthHeaderValue(signature, this.Key, signedHeaders); + return options + }; + return { + HttpRequest: HttpRequest, + Signer: Signer, + urlEncode: urlEncode, + findHeader: findHeader, + SignedHeaders: SignedHeaders, + CanonicalRequest: CanonicalRequest, + StringToSign: StringToSign, + } +})); diff --git a/server/APIGW-javascript-sdk-2.0.6/test.js b/server/APIGW-javascript-sdk-2.0.6/test.js new file mode 100644 index 0000000..5956815 --- /dev/null +++ b/server/APIGW-javascript-sdk-2.0.6/test.js @@ -0,0 +1,10 @@ +var assert = require("assert"); +var signer = require('./signer') + +var s = "" +for (i = 0; i < 0x80; i++) { + s = s + signer.urlEncode(String.fromCharCode(i)) +} +console.log(s) +assert.equal(s, "%00%01%02%03%04%05%06%07%08%09%0A%0B%0C%0D%0E%0F%10%11%12%13%14%15%16%17%18%19%1A%1B%1C%1D%1E%1F%20%21%22%23%24%25%26%27%28%29%2A%2B%2C-.%2F0123456789%3A%3B%3C%3D%3E%3F%40ABCDEFGHIJKLMNOPQRSTUVWXYZ%5B%5C%5D%5E_%60abcdefghijklmnopqrstuvwxyz%7B%7C%7D~%7F") + diff --git a/server/index.js b/server/index.js index df3ab0d..a4bf546 100644 --- a/server/index.js +++ b/server/index.js @@ -2,7 +2,8 @@ require('dotenv').config(); const express = require('express'); const cors = require('cors'); -const crypto = require('crypto'); +const https = require('https'); +const signer = require('./APIGW-javascript-sdk-2.0.6/signer'); const app = express(); const PORT = 3001; @@ -11,172 +12,66 @@ app.use(cors()); app.use(express.json()); const METASTUDIO_API_BASE = 'https://metastudio.cn-north-4.myhuaweicloud.com'; -const PROJECT_ID = process.env.HUAWEI_PROJECT_ID || 'c67bf5e7d1f84d20a6eefff2c1ee9cd4'; -const IAM_TOKEN_URL = 'https://iam.cn-north-4.myhuaweicloud.com/v3/auth/tokens'; - -async function getIamToken() { - const username = process.env.HUAWEI_USERNAME; - const password = process.env.HUAWEI_PASSWORD; - const domainName = process.env.HUAWEI_DOMAIN_NAME || domainName; - const region = 'cn-north-4'; - - if (!username || !password || !domainName) { - throw new Error('请配置环境变量: HUAWEI_USERNAME, HUAWEI_PASSWORD, HUAWEI_DOMAIN_NAME'); - } - - const requestBody = { - "auth": { - "identity": { - "methods": ["password"], - "password": { - "user": { - "name": username, - "password": password, - "domain": { - "name": domainName - } - } - } - }, - "scope": { - "project": { - "name": region - } - } - } - }; - - const response = await fetch(IAM_TOKEN_URL, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify(requestBody) - }); - - if (!response.ok) { - throw new Error(`IAM认证失败: ${response.status}`); - } - - const token = response.headers.get('X-Subject-Token'); - return token; -} - -function buildAKSKSignature(method, host, uri, query, date, headers) { - const secretKey = process.env.HUAWEI_SECRET_KEY; - if (!secretKey) { - throw new Error('请配置环境变量: HUAWEI_SECRET_KEY'); - } - - const signingKey = 'sdk-hmac-sha256'; - const dateShort = date.toISOString().slice(0, 10).replace(/-/g, ''); - - const canonicalRequestParts = [ - method.toUpperCase(), - uri, - query || '', - `${headers.toLowerCase()}\n${host.toLowerCase()}` - ]; - const canonicalRequest = canonicalRequestParts.join('\n'); - - const stringToSign = [ - signingKey, - date, - `${dateShort}/${region}/metastudio/request`, - crypto.createHash('sha256').update(canonicalRequest).digest('hex') - ].join('\n'); - - const signature = crypto - .createHmac('sha256', secretKey) - .update(stringToSign) - .digest('base64'); - - return { - signature, - stringToSign, - dateShort - }; -} async function createOnceCodeWithAKSK() { const accessKey = process.env.HUAWEI_AK; const secretKey = process.env.HUAWEI_SK; - const region = 'cn-north-4'; + const projectId = process.env.HUAWEI_PROJECT_ID; if (!accessKey || !secretKey) { throw new Error('请配置环境变量: HUAWEI_AK 和 HUAWEI_SK'); } - - const host = 'metastudio.cn-north-4.myhuaweicloud.com'; - const uri = `/v1/${PROJECT_ID}/digital-human-chat/once-code`; - const method = 'POST'; - const date = new Date().toUTCString(); - const query = ''; - - const { signature, stringToSign, dateShort } = buildAKSKSignature( - method, host, uri, query, date, '' - ); - - const authHeader = `SDK-HMAC-SHA256 Access=${accessKey}, SignedHeaders=host;x-sdk-date, Signature=${signature}`; - - const response = await fetch(`${METASTUDIO_API_BASE}${uri}`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Sdk-Date': date, - 'Authorization': authHeader, - 'X-Project-Id': PROJECT_ID - }, - body: JSON.stringify({}) - }); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`API调用失败: ${response.status} - ${errorText}`); + if (!projectId) { + throw new Error('请配置环境变量: HUAWEI_PROJECT_ID'); } - const data = await response.json(); - return data.once_code; + const uri = `/v1/${projectId}/digital-human-chat/once-code`; + + const sig = new signer.Signer(); + sig.Key = accessKey; + sig.Secret = secretKey; + + const req = new signer.HttpRequest( + 'POST', + `${METASTUDIO_API_BASE}${uri}`, + { + 'content-type': 'application/json', + 'x-project-id': projectId + }, + '{}' + ); + + const options = sig.Sign(req); + + return new Promise((resolve, reject) => { + const request = https.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + if (res.statusCode !== 200) { + reject(new Error(`API调用失败: ${res.statusCode} - ${data}`)); + return; + } + const response = JSON.parse(data); + resolve(response.once_code); + }); + }); + + request.on('error', (e) => { + reject(e); + }); + + request.write('{}'); + request.end(); + }); } app.post('/api/once-code', async (req, res) => { try { - let onceCode; - - if (process.env.HUAWEI_AK && process.env.HUAWEI_SK) { - console.log('使用 AK/SK 认证获取授权码...'); - onceCode = await createOnceCodeWithAKSK(); - } else if (process.env.HUAWEI_USERNAME && process.env.HUAWEI_PASSWORD) { - console.log('使用 Token 认证获取授权码...'); - const token = await getIamToken(); - - const response = await fetch( - `${METASTUDIO_API_BASE}/v1/${PROJECT_ID}/digital-human-chat/once-code`, - { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - 'X-Auth-Token': token, - 'X-Project-Id': PROJECT_ID - }, - body: JSON.stringify({}) - } - ); - - if (!response.ok) { - const errorText = await response.text(); - throw new Error(`API调用失败: ${response.status} - ${errorText}`); - } - - const data = await response.json(); - onceCode = data.once_code; - } else { - return res.status(400).json({ - error_code: 'CONFIG_MISSING', - error_msg: '请配置环境变量 (AK/SK 或用户名密码)' - }); - } - + console.log('使用 AK/SK 认证获取授权码...'); + const onceCode = await createOnceCodeWithAKSK(); res.json({ once_code: onceCode }); } catch (error) { console.error('获取授权码失败:', error); @@ -193,7 +88,5 @@ app.get('/health', (req, res) => { app.listen(PORT, () => { console.log(`后端服务已启动: http://localhost:${PORT}`); - console.log('支持的认证方式:'); - console.log(' 1. AK/SK 认证: 设置 HUAWEI_AK 和 HUAWEI_SK'); - console.log(' 2. Token 认证: 设置 HUAWEI_USERNAME, HUAWEI_PASSWORD, HUAWEI_DOMAIN_NAME'); + console.log('使用 AK/SK 认证方式'); }); diff --git a/server/sdk.zip b/server/sdk.zip new file mode 100644 index 0000000..e5db0fe Binary files /dev/null and b/server/sdk.zip differ