Türkçe çeviri özelliği eklendi (GLM 4.6 ile çeviri yapılıyor)
This commit is contained in:
@@ -9,7 +9,12 @@ import { promises as fs } from 'fs';
|
||||
import { v4 as uuidV4 } from 'uuid';
|
||||
import Epub from 'epub-gen';
|
||||
|
||||
const requiredEnv = ['SUPABASE_URL', 'SUPABASE_SERVICE_ROLE_KEY', 'JWT_SECRET'];
|
||||
const requiredEnv = [
|
||||
'SUPABASE_URL',
|
||||
'SUPABASE_SERVICE_ROLE_KEY',
|
||||
'JWT_SECRET',
|
||||
'ZAI_GLM_API_KEY',
|
||||
];
|
||||
requiredEnv.forEach((key) => {
|
||||
if (!process.env[key]) {
|
||||
console.error(`Missing required environment variable: ${key}`);
|
||||
@@ -25,6 +30,7 @@ const allowedOrigins = ORIGIN.split(',').map((origin) => origin.trim());
|
||||
const USERS_TABLE = process.env.SUPABASE_USERS_TABLE || 'users';
|
||||
const JWT_SECRET = process.env.JWT_SECRET;
|
||||
import { supabase } from './src/services/supabaseClient.js';
|
||||
import { translateWithGlm } from './src/services/glmClient.js';
|
||||
|
||||
app.use(
|
||||
cors({
|
||||
@@ -248,6 +254,21 @@ authRouter.post('/forgot-password', async (req, res) => {
|
||||
|
||||
app.use('/auth', authRouter);
|
||||
|
||||
app.post('/translate', async (req, res) => {
|
||||
const { text } = req.body || {};
|
||||
if (!text || !text.trim()) {
|
||||
return res.status(400).json({ message: 'Çevrilecek metin bulunamadı.' });
|
||||
}
|
||||
|
||||
try {
|
||||
const translated = await translateWithGlm(text);
|
||||
return res.json({ text: translated });
|
||||
} catch (error) {
|
||||
console.error('GLM çeviri hatası:', error);
|
||||
return res.status(500).json({ message: error.message || 'Çeviri tamamlanamadı.' });
|
||||
}
|
||||
});
|
||||
|
||||
app.post('/generate-epub', async (req, res) => {
|
||||
const { text, meta, cover } = req.body || {};
|
||||
if (!text || !text.trim()) {
|
||||
|
||||
142
server/src/services/glmClient.js
Normal file
142
server/src/services/glmClient.js
Normal file
@@ -0,0 +1,142 @@
|
||||
const GLM_API_KEY = process.env.ZAI_GLM_API_KEY || process.env.ANTHROPIC_API_KEY;
|
||||
const GLM_MODEL = process.env.ANTHROPIC_MODEL || process.env.ZAI_GLM_MODEL || 'glm-4.6';
|
||||
const resolveEndpoint = () => {
|
||||
const base =
|
||||
process.env.ANTHROPIC_BASE_URL || process.env.ZAI_GLM_API_URL || 'https://api.z.ai/api/anthropic';
|
||||
const normalized = base.replace(/\/$/, '');
|
||||
if (/\/messages$/.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
if (/\/v\d+$/.test(normalized)) {
|
||||
return `${normalized}/messages`;
|
||||
}
|
||||
if (/\/v\d+\/.+/.test(normalized)) {
|
||||
return normalized;
|
||||
}
|
||||
return `${normalized}/v1/messages`;
|
||||
};
|
||||
|
||||
const GLM_API_URL = resolveEndpoint();
|
||||
const IS_ANTHROPIC_STYLE = /anthropic/.test(GLM_API_URL);
|
||||
|
||||
const SYSTEM_PROMPT =
|
||||
'You are a professional localization editor. Translate any given English or mixed-language text into fluent, publication-ready Turkish. Keep the original meaning, respect formatting, and avoid adding explanations.';
|
||||
|
||||
const extractContent = (payload) => {
|
||||
if (!payload) return '';
|
||||
if (Array.isArray(payload.content)) {
|
||||
return payload.content
|
||||
.map((item) => {
|
||||
if (!item) return '';
|
||||
if (typeof item === 'string') return item;
|
||||
if (Array.isArray(item.text)) {
|
||||
return item.text.map((inner) => inner?.text || inner || '').join('');
|
||||
}
|
||||
if (typeof item.text === 'string') return item.text;
|
||||
if (Array.isArray(item.content)) {
|
||||
return item.content
|
||||
.map((inner) => (typeof inner === 'string' ? inner : inner?.text || ''))
|
||||
.join('');
|
||||
}
|
||||
return '';
|
||||
})
|
||||
.filter(Boolean)
|
||||
.join('\n')
|
||||
.trim();
|
||||
}
|
||||
if (Array.isArray(payload.output)) {
|
||||
return payload.output
|
||||
.map((item) => item.content || item.text || '')
|
||||
.filter(Boolean)
|
||||
.join('')
|
||||
.trim();
|
||||
}
|
||||
if (Array.isArray(payload.choices) && payload.choices.length > 0) {
|
||||
const choice = payload.choices[0];
|
||||
if (choice.message?.content) {
|
||||
if (Array.isArray(choice.message.content)) {
|
||||
return choice.message.content
|
||||
.map((c) => (typeof c === 'string' ? c : c.text || ''))
|
||||
.join('')
|
||||
.trim();
|
||||
}
|
||||
return `${choice.message.content}`.trim();
|
||||
}
|
||||
if (choice.text) {
|
||||
return `${choice.text}`.trim();
|
||||
}
|
||||
}
|
||||
if (payload.data?.output_text?.length) {
|
||||
return payload.data.output_text.join('\n').trim();
|
||||
}
|
||||
if (typeof payload.content === 'string') {
|
||||
return payload.content.trim();
|
||||
}
|
||||
if (typeof payload.text === 'string') {
|
||||
return payload.text.trim();
|
||||
}
|
||||
return '';
|
||||
};
|
||||
|
||||
export const translateWithGlm = async (text) => {
|
||||
if (!GLM_API_KEY) {
|
||||
throw new Error('ZAI_GLM_API_KEY veya ANTHROPIC_API_KEY tanımlı değil.');
|
||||
}
|
||||
|
||||
const prompt =
|
||||
`Aşağıdaki metni Türkçe'ye çevir. Yalnızca çeviriyi döndür:\n\n"""${text}"""`;
|
||||
|
||||
let body;
|
||||
if (IS_ANTHROPIC_STYLE) {
|
||||
body = {
|
||||
model: GLM_MODEL,
|
||||
max_tokens: 1024,
|
||||
temperature: 0.1,
|
||||
system: SYSTEM_PROMPT,
|
||||
messages: [
|
||||
{
|
||||
role: 'user',
|
||||
content: [{ type: 'text', text: prompt }],
|
||||
},
|
||||
],
|
||||
};
|
||||
} else {
|
||||
body = {
|
||||
model: GLM_MODEL,
|
||||
max_tokens: 1024,
|
||||
temperature: 0.1,
|
||||
messages: [
|
||||
{ role: 'system', content: SYSTEM_PROMPT },
|
||||
{ role: 'user', content: prompt },
|
||||
],
|
||||
};
|
||||
}
|
||||
|
||||
const response = await fetch(GLM_API_URL, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'Content-Type': 'application/json',
|
||||
Authorization: `Bearer ${GLM_API_KEY}`,
|
||||
...(IS_ANTHROPIC_STYLE && {
|
||||
'x-api-key': GLM_API_KEY,
|
||||
'anthropic-version': process.env.ANTHROPIC_VERSION || '2023-06-01',
|
||||
}),
|
||||
},
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
|
||||
const payload = await response.json().catch(() => ({}));
|
||||
if (!response.ok) {
|
||||
const message =
|
||||
payload?.error?.message ||
|
||||
payload?.msg ||
|
||||
`GLM isteği başarısız oldu (status: ${response.status})`;
|
||||
throw new Error(message);
|
||||
}
|
||||
|
||||
const translated = extractContent(payload);
|
||||
if (!translated) {
|
||||
throw new Error('GLM çıktısı boş döndü.');
|
||||
}
|
||||
return translated;
|
||||
};
|
||||
Reference in New Issue
Block a user