diff --git a/js/mts.js b/js/mts.js new file mode 100755 index 0000000..009473e --- /dev/null +++ b/js/mts.js @@ -0,0 +1,86 @@ +#!/usr/bin/env node + +const fastify = require('fastify'); +const cors = require('@fastify/cors'); +const { getConfig, validateToken } = require('./utils/config'); +const { authenticate, errorHandler } = require('./utils/middleware'); +const { getSupportedLanguages, preloadModel, shutdown } = require('./utils/translator'); +const registerCoreRoutes = require('./routes/core'); +const registerTranslateRoutes = require('./routes/translate'); +const registerPluginRoutes = require('./routes/plugins'); + +// 获取配置 +const config = getConfig(); + +// 创建Fastify实例 +const server = fastify({ + logger: { + level: config.logLevel, + transport: { + target: 'pino-pretty', + options: { + translateTime: 'HH:MM:ss Z', + ignore: 'pid,hostname' + } + } + } +}); + +// 注册CORS中间件 +server.register(cors, { + origin: true, + methods: ['GET', 'POST', 'OPTIONS'], + allowedHeaders: ['Content-Type', 'Authorization', 'Key'] +}); + +// 设置错误处理器 +server.setErrorHandler(errorHandler); + +// 路由选项 +const routeOptions = { + authenticate: authenticate, + validateToken: validateToken +}; + +// 注册路由 +server.register(registerCoreRoutes, routeOptions); +server.register(registerTranslateRoutes, routeOptions); +server.register(registerPluginRoutes, routeOptions); + +// 启动服务器 +async function start() { + try { + // 输出服务信息 + console.log(`Service port: ${config.port}`); + + // 获取支持的语言列表 + const languages = getSupportedLanguages(); + + // 预加载常用语言对 + // await preloadModel('en', 'zh-Hans'); + + // 监听端口 + await server.listen({ port: config.port, host: config.host }); + } catch (err) { + server.log.error(err); + process.exit(1); + } +} + +// 处理进程退出 +process.on('SIGINT', async () => { + console.log('Shutting down server...'); + await shutdown(); + await server.close(); + process.exit(0); +}); + +process.on('SIGTERM', async () => { + console.log('Shutting down server...'); + await shutdown(); + await server.close(); + process.exit(0); +}); + +// 启动服务器 +start(); diff --git a/js/routes/core.js b/js/routes/core.js new file mode 100644 index 0000000..1b22c61 --- /dev/null +++ b/js/routes/core.js @@ -0,0 +1,40 @@ +const { getVersion } = require('../utils/config'); +const { getLoadedModels } = require('../utils/translator'); + +/** + * 注册核心路由 + * @param {Object} fastify Fastify实例 + * @param {Object} options 选项 + */ +function registerCoreRoutes(fastify, options) { + // 版本信息 + fastify.get('/version', async (request, reply) => { + return { version: getVersion() }; + }); + + // 健康检查 + fastify.get('/health', async (request, reply) => { + return { status: 'ok' }; + }); + + // 心跳检查 + fastify.get('/__heartbeat__', async (request, reply) => { + reply.type('text/plain'); + return 'Ready'; + }); + + // 负载均衡心跳检查 + fastify.get('/__lbheartbeat__', async (request, reply) => { + reply.type('text/plain'); + return 'Ready'; + }); + + // 获取已加载的模型列表 + fastify.get('/models', { + preHandler: options.authenticate + }, async (request, reply) => { + return { models: getLoadedModels() }; + }); +} + +module.exports = registerCoreRoutes; \ No newline at end of file diff --git a/js/routes/plugins.js b/js/routes/plugins.js new file mode 100644 index 0000000..d5715f2 --- /dev/null +++ b/js/routes/plugins.js @@ -0,0 +1,90 @@ +const { translate, batchTranslate } = require('../utils/translator'); + +/** + * 注册翻译插件兼容路由 + * @param {Object} fastify Fastify实例 + * @param {Object} options 选项 + */ +function registerPluginRoutes(fastify, options) { + // 沉浸式翻译插件API + fastify.post('/imme', async (request, reply) => { + // 验证token + const token = request.query.token; + if (!options.validateToken(token)) { + return reply.code(401).send({ error: 'Unauthorized', message: 'Invalid or missing API token' }); + } + + try { + const { source_lang, target_lang, text, texts } = request.body; + + // 处理批量翻译 + if (Array.isArray(texts) && texts.length > 0) { + const results = await batchTranslate(texts, source_lang || 'auto', target_lang || 'zh-Hans'); + return { data: results }; + } + + // 处理单个文本翻译 + if (text) { + const result = await translate(text, source_lang || 'auto', target_lang || 'zh-Hans'); + return { data: result }; + } + + return reply.code(400).send({ error: 'BadRequest', message: 'Missing text or texts parameter' }); + } catch (error) { + return reply.code(500).send({ error: 'TranslationError', message: error.message }); + } + }); + + // 简约翻译插件API + fastify.post('/kiss', async (request, reply) => { + // 验证token + const token = request.headers.key; + if (!options.validateToken(token)) { + return reply.code(401).send({ error: 'Unauthorized', message: 'Invalid or missing API token' }); + } + + try { + const { from, to, text, texts } = request.body; + + // 处理批量翻译 + if (Array.isArray(texts) && texts.length > 0) { + const results = await batchTranslate(texts, from || 'auto', to || 'zh-Hans'); + return { code: 200, text: results.join('\n') }; + } + + // 处理单个文本翻译 + if (text) { + const result = await translate(text, from || 'auto', to || 'zh-Hans'); + return { code: 200, text: result }; + } + + return reply.code(400).send({ code: 400, error: 'Missing text or texts parameter' }); + } catch (error) { + return reply.code(500).send({ code: 500, error: error.message }); + } + }); + + // 划词翻译插件API + fastify.post('/hcfy', async (request, reply) => { + // 验证token + const token = request.query.token; + if (!options.validateToken(token)) { + return reply.code(401).send({ error: 'Unauthorized', message: 'Invalid or missing API token' }); + } + + try { + const { text, from, to } = request.body; + + if (!text) { + return reply.code(400).send({ error: 'BadRequest', message: 'Missing text parameter' }); + } + + const result = await translate(text, from || 'auto', to || 'zh-Hans'); + return { translation: result }; + } catch (error) { + return reply.code(500).send({ error: 'TranslationError', message: error.message }); + } + }); +} + +module.exports = registerPluginRoutes; \ No newline at end of file diff --git a/js/routes/translate.js b/js/routes/translate.js new file mode 100644 index 0000000..1dd4fa5 --- /dev/null +++ b/js/routes/translate.js @@ -0,0 +1,104 @@ +const { translate, batchTranslate } = require('../utils/translator'); + +/** + * 注册翻译路由 + * @param {Object} fastify Fastify实例 + * @param {Object} options 选项 + */ +function registerTranslateRoutes(fastify, options) { + // 普通翻译API + fastify.post('/translate', { + preHandler: options.authenticate, + schema: { + body: { + type: 'object', + required: ['from', 'to', 'text'], + properties: { + from: { type: 'string' }, + to: { type: 'string' }, + text: { type: 'string' } + } + } + } + }, async (request, reply) => { + const { from, to, text } = request.body; + + try { + const result = await translate(text, from, to); + return { result }; + } catch (error) { + reply.code(500).send({ + error: 'TranslationError', + message: error.message + }); + } + }); + + // 批量翻译API + fastify.post('/translate/batch', { + preHandler: options.authenticate, + schema: { + body: { + type: 'object', + required: ['from', 'to', 'texts'], + properties: { + from: { type: 'string' }, + to: { type: 'string' }, + texts: { + type: 'array', + items: { type: 'string' } + } + } + } + } + }, async (request, reply) => { + const { from, to, texts } = request.body; + + try { + const results = await batchTranslate(texts, from, to); + return { results }; + } catch (error) { + reply.code(500).send({ + error: 'BatchTranslationError', + message: error.message + }); + } + }); + + // Google翻译兼容API + fastify.post('/language/translate/v2', { + preHandler: options.authenticate, + schema: { + body: { + type: 'object', + required: ['q', 'source', 'target'], + properties: { + q: { type: 'string' }, + source: { type: 'string' }, + target: { type: 'string' }, + format: { type: 'string', default: 'text' } + } + } + } + }, async (request, reply) => { + const { q, source, target } = request.body; + + try { + const translatedText = await translate(q, source, target); + return { + data: { + translations: [ + { translatedText } + ] + } + }; + } catch (error) { + reply.code(500).send({ + error: 'TranslationError', + message: error.message + }); + } + }); +} + +module.exports = registerTranslateRoutes; \ No newline at end of file diff --git a/js/utils/config.js b/js/utils/config.js new file mode 100644 index 0000000..b80ac54 --- /dev/null +++ b/js/utils/config.js @@ -0,0 +1,48 @@ +/** + * 配置管理模块 + */ + +// 默认配置 +const defaultConfig = { + port: process.env.PORT || 8989, + host: process.env.HOST || '0.0.0.0', + apiToken: process.env.CORE_API_TOKEN || '', + logLevel: process.env.LOG_LEVEL || 'info' +}; + +/** + * 获取配置 + * @returns {Object} 配置对象 + */ +function getConfig() { + return { ...defaultConfig }; +} + +/** + * 验证API令牌 + * @param {string} token 待验证的令牌 + * @returns {boolean} 是否有效 + */ +function validateToken(token) { + // 如果未设置API令牌,则不需要验证 + if (!defaultConfig.apiToken) { + return true; + } + + return token === defaultConfig.apiToken; +} + +/** + * 获取服务器版本 + * @returns {string} 版本号 + */ +function getVersion() { + const packageJson = require('../../package.json'); + return packageJson.version || 'unknown'; +} + +module.exports = { + getConfig, + validateToken, + getVersion +}; \ No newline at end of file diff --git a/js/utils/middleware.js b/js/utils/middleware.js new file mode 100644 index 0000000..38bc561 --- /dev/null +++ b/js/utils/middleware.js @@ -0,0 +1,40 @@ +const { validateToken } = require('./config'); + +/** + * 认证中间件 + * @param {Object} request Fastify请求对象 + * @param {Object} reply Fastify响应对象 + * @param {Function} done 完成回调 + */ +function authenticate(request, reply, done) { + const authHeader = request.headers.authorization; + const token = request.query.token; + + // 检查Authorization头或URL中的token参数 + if (validateToken(authHeader) || validateToken(token)) { + done(); + } else { + reply.code(401).send({ error: 'Unauthorized', message: 'Invalid or missing API token' }); + } +} + +/** + * 错误处理中间件 + * @param {Error} error 错误对象 + * @param {Object} request Fastify请求对象 + * @param {Object} reply Fastify响应对象 + */ +function errorHandler(error, request, reply) { + console.error(`Error processing request: ${error.message}`); + + // 返回适当的错误响应 + reply.code(error.statusCode || 500).send({ + error: error.name || 'InternalServerError', + message: error.message || 'An unknown error occurred' + }); +} + +module.exports = { + authenticate, + errorHandler +}; \ No newline at end of file diff --git a/js/utils/translator.js b/js/utils/translator.js new file mode 100644 index 0000000..4364f6d --- /dev/null +++ b/js/utils/translator.js @@ -0,0 +1,82 @@ +const Translator = require("@mtran/core"); + +// 缓存已加载的模型 +const loadedModels = new Set(); + +/** + * 获取支持的语言列表 + * @returns {Array} 支持的语言列表 + */ +function getSupportedLanguages() { + return Translator.GetSupportLanguages(); +} + +/** + * 预加载翻译模型 + * @param {string} from 源语言 + * @param {string} to 目标语言 + * @returns {Promise} + */ +async function preloadModel(from, to) { + const modelKey = `${from}_${to}`; + if (!loadedModels.has(modelKey)) { + await Translator.Preload(from, to); + loadedModels.add(modelKey); + console.log(`Successfully loaded model for language pair: ${modelKey}`); + } +} + +/** + * 翻译文本 + * @param {string} text 要翻译的文本 + * @param {string} from 源语言 + * @param {string} to 目标语言 + * @returns {Promise} 翻译结果 + */ +async function translate(text, from, to) { + // 如果模型未加载,先加载模型 + await preloadModel(from, to); + return Translator.Translate(text, from, to); +} + +/** + * 批量翻译文本 + * @param {string[]} texts 要翻译的文本数组 + * @param {string} from 源语言 + * @param {string} to 目标语言 + * @returns {Promise} 翻译结果数组 + */ +async function batchTranslate(texts, from, to) { + // 如果模型未加载,先加载模型 + await preloadModel(from, to); + + // 并行处理翻译请求 + const promises = texts.map(text => Translator.Translate(text, from, to)); + return Promise.all(promises); +} + +/** + * 关闭翻译引擎 + * @returns {Promise} + */ +async function shutdown() { + await Translator.Shutdown(); + console.log("Translation engine shutdown complete"); +} + +/** + * 获取已加载的模型列表 + * @returns {string[]} 已加载的模型列表 + */ +function getLoadedModels() { + return Array.from(loadedModels); +} + +module.exports = { + getSupportedLanguages, + preloadModel, + translate, + batchTranslate, + shutdown, + getLoadedModels +}; \ No newline at end of file diff --git a/package.json b/package.json index 3eb81b9..1c3e960 100644 --- a/package.json +++ b/package.json @@ -3,9 +3,9 @@ "homepage": "https://github.com/xxnuo", "version": "3.0.0", "type": "commonjs", - "main": "dist/mts.js", + "main": "js/mts.js", "bin": { - "mt": "dist/mts.js" + "mt": "js/mts.js" }, "keywords": [ "translation", @@ -21,6 +21,9 @@ "node": ">=18.0.0" }, "dependencies": { - "@mtran/core": "file:packages/mtran-core.tgz" + "@fastify/cors": "^11.0.1", + "@mtran/core": "file:packages/mtran-core.tgz", + "fastify": "^5.4.0", + "pino-pretty": "^13.0.0" } } diff --git a/start.sh b/start.sh new file mode 100755 index 0000000..6fb4705 --- /dev/null +++ b/start.sh @@ -0,0 +1,25 @@ +#!/bin/sh + +# 翻译服务器启动脚本 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) + +# 切换到脚本所在目录 +cd "$SCRIPT_DIR" || exit 1 + +# 检查是否设置了环境变量 +if [ -z "$PORT" ]; then + export PORT=8989 +fi + +if [ -z "$HOST" ]; then + export HOST=0.0.0.0 +fi + +# 启动服务 +echo "Starting MTranServer..." +node js/mts.js + +# 脚本结束 +exit 0 \ No newline at end of file diff --git a/test.sh b/test.sh new file mode 100755 index 0000000..104ef04 --- /dev/null +++ b/test.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# 测试运行脚本 + +# 获取脚本所在目录的绝对路径 +SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd) + +# 切换到脚本所在目录 +cd "$SCRIPT_DIR" || exit 1 + +# 设置环境变量 +export HOST=${HOST:-localhost} +export PORT=${PORT:-8989} +export CORE_API_TOKEN=${CORE_API_TOKEN:-} + +# 显示测试环境 +echo "测试环境:" +echo "HOST: $HOST" +echo "PORT: $PORT" +echo "TOKEN: ${CORE_API_TOKEN:-(未设置)}" +echo "" + +# 运行测试 +echo "=== 运行核心API测试 ===" +node tests/core-api.js +echo "" + +echo "=== 运行翻译API测试 ===" +node tests/translate-api.js +echo "" + +echo "=== 运行翻译插件兼容API测试 ===" +node tests/plugin-api.js +echo "" + +echo "所有测试完成!" \ No newline at end of file diff --git a/tests/core-api.js b/tests/core-api.js new file mode 100755 index 0000000..aec4cea --- /dev/null +++ b/tests/core-api.js @@ -0,0 +1,184 @@ +#!/usr/bin/env node + +/** + * 测试核心API + * 包括版本、健康检查、心跳检查和模型列表API + */ + +const http = require('http'); + +// 配置 +const config = { + host: process.env.HOST || 'localhost', + port: process.env.PORT || 8989, + token: process.env.CORE_API_TOKEN || '' +}; + +// 测试版本API +function testVersion() { + console.log('测试版本API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/version', + method: 'GET' + }; + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${data}`); + console.log('版本API测试完成\n'); + + // 测试下一个API + testHealth(); + }); + }); + + req.on('error', (error) => { + console.error(`版本API测试出错: ${error.message}`); + }); + + req.end(); +} + +// 测试健康检查API +function testHealth() { + console.log('测试健康检查API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/health', + method: 'GET' + }; + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${data}`); + console.log('健康检查API测试完成\n'); + + // 测试下一个API + testHeartbeat(); + }); + }); + + req.on('error', (error) => { + console.error(`健康检查API测试出错: ${error.message}`); + }); + + req.end(); +} + +// 测试心跳检查API +function testHeartbeat() { + console.log('测试心跳检查API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/__heartbeat__', + method: 'GET' + }; + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${data}`); + console.log('心跳检查API测试完成\n'); + + // 测试下一个API + testLBHeartbeat(); + }); + }); + + req.on('error', (error) => { + console.error(`心跳检查API测试出错: ${error.message}`); + }); + + req.end(); +} + +// 测试负载均衡心跳检查API +function testLBHeartbeat() { + console.log('测试负载均衡心跳检查API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/__lbheartbeat__', + method: 'GET' + }; + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${data}`); + console.log('负载均衡心跳检查API测试完成\n'); + + // 测试下一个API + testModels(); + }); + }); + + req.on('error', (error) => { + console.error(`负载均衡心跳检查API测试出错: ${error.message}`); + }); + + req.end(); +} + +// 测试模型列表API +function testModels() { + console.log('测试模型列表API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/models', + method: 'GET', + headers: {} + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Authorization'] = config.token; + } + + const req = http.request(options, (res) => { + let data = ''; + res.on('data', (chunk) => { + data += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${data}`); + console.log('模型列表API测试完成\n'); + + console.log('所有核心API测试完成'); + }); + }); + + req.on('error', (error) => { + console.error(`模型列表API测试出错: ${error.message}`); + }); + + req.end(); +} + +// 开始测试 +console.log('开始测试核心API...\n'); +testVersion(); \ No newline at end of file diff --git a/tests/plugin-api.js b/tests/plugin-api.js new file mode 100755 index 0000000..644b809 --- /dev/null +++ b/tests/plugin-api.js @@ -0,0 +1,241 @@ +#!/usr/bin/env node + +/** + * 测试翻译插件兼容API + * 包括沉浸式翻译、简约翻译和划词翻译API + */ + +const http = require('http'); + +// 配置 +const config = { + host: process.env.HOST || 'localhost', + port: process.env.PORT || 8989, + token: process.env.CORE_API_TOKEN || '' +}; + +// 测试沉浸式翻译API +function testImmeTranslate() { + console.log('测试沉浸式翻译API...'); + const tokenParam = config.token ? `?token=${config.token}` : ''; + const options = { + hostname: config.host, + port: config.port, + path: `/imme${tokenParam}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + const data = JSON.stringify({ + source_lang: 'en', + target_lang: 'zh-Hans', + text: 'Hello, world!' + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('沉浸式翻译API测试完成\n'); + + // 测试批量翻译 + testImmeBatchTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`沉浸式翻译API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试沉浸式翻译批量API +function testImmeBatchTranslate() { + console.log('测试沉浸式翻译批量API...'); + const tokenParam = config.token ? `?token=${config.token}` : ''; + const options = { + hostname: config.host, + port: config.port, + path: `/imme${tokenParam}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + const data = JSON.stringify({ + source_lang: 'en', + target_lang: 'zh-Hans', + texts: ['Hello, world!', 'How are you?'] + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('沉浸式翻译批量API测试完成\n'); + + // 测试下一个API + testKissTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`沉浸式翻译批量API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试简约翻译API +function testKissTranslate() { + console.log('测试简约翻译API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/kiss', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Key'] = config.token; + } + + const data = JSON.stringify({ + from: 'en', + to: 'zh-Hans', + text: 'Hello, world!' + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('简约翻译API测试完成\n'); + + // 测试批量翻译 + testKissBatchTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`简约翻译API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试简约翻译批量API +function testKissBatchTranslate() { + console.log('测试简约翻译批量API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/kiss', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Key'] = config.token; + } + + const data = JSON.stringify({ + from: 'en', + to: 'zh-Hans', + texts: ['Hello, world!', 'How are you?'] + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('简约翻译批量API测试完成\n'); + + // 测试下一个API + testHcfyTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`简约翻译批量API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试划词翻译API +function testHcfyTranslate() { + console.log('测试划词翻译API...'); + const tokenParam = config.token ? `?token=${config.token}` : ''; + const options = { + hostname: config.host, + port: config.port, + path: `/hcfy${tokenParam}`, + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + const data = JSON.stringify({ + text: 'Hello, world!', + from: 'en', + to: 'zh-Hans' + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('划词翻译API测试完成\n'); + + console.log('所有翻译插件兼容API测试完成'); + }); + }); + + req.on('error', (error) => { + console.error(`划词翻译API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 开始测试 +console.log('开始测试翻译插件兼容API...\n'); +testImmeTranslate(); \ No newline at end of file diff --git a/tests/translate-api.js b/tests/translate-api.js new file mode 100755 index 0000000..6ae9d0c --- /dev/null +++ b/tests/translate-api.js @@ -0,0 +1,160 @@ +#!/usr/bin/env node + +/** + * 测试翻译API + * 包括普通翻译、批量翻译和Google翻译兼容API + */ + +const http = require('http'); + +// 配置 +const config = { + host: process.env.HOST || 'localhost', + port: process.env.PORT || 8989, + token: process.env.CORE_API_TOKEN || '' +}; + +// 测试普通翻译API +function testTranslate() { + console.log('测试普通翻译API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/translate', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Authorization'] = config.token; + } + + const data = JSON.stringify({ + from: 'en', + to: 'zh-Hans', + text: 'Hello, world!' + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('普通翻译API测试完成\n'); + + // 测试下一个API + testBatchTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`普通翻译API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试批量翻译API +function testBatchTranslate() { + console.log('测试批量翻译API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/translate/batch', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Authorization'] = config.token; + } + + const data = JSON.stringify({ + from: 'en', + to: 'zh-Hans', + texts: ['Hello, world!', 'How are you?'] + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('批量翻译API测试完成\n'); + + // 测试下一个API + testGoogleTranslate(); + }); + }); + + req.on('error', (error) => { + console.error(`批量翻译API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 测试Google翻译兼容API +function testGoogleTranslate() { + console.log('测试Google翻译兼容API...'); + const options = { + hostname: config.host, + port: config.port, + path: '/language/translate/v2', + method: 'POST', + headers: { + 'Content-Type': 'application/json' + } + }; + + // 如果有token,添加到请求头 + if (config.token) { + options.headers['Authorization'] = config.token; + } + + const data = JSON.stringify({ + q: 'The Great Pyramid of Giza', + source: 'en', + target: 'zh-Hans', + format: 'text' + }); + + const req = http.request(options, (res) => { + let responseData = ''; + res.on('data', (chunk) => { + responseData += chunk; + }); + res.on('end', () => { + console.log(`状态码: ${res.statusCode}`); + console.log(`响应数据: ${responseData}`); + console.log('Google翻译兼容API测试完成\n'); + + console.log('所有翻译API测试完成'); + }); + }); + + req.on('error', (error) => { + console.error(`Google翻译兼容API测试出错: ${error.message}`); + }); + + req.write(data); + req.end(); +} + +// 开始测试 +console.log('开始测试翻译API...\n'); +testTranslate(); \ No newline at end of file