重构了translator,写了点数据库、后端api路由
Some checks failed
continuous-integration/drone/push Build is failing

This commit is contained in:
2025-11-10 21:42:44 +08:00
parent b30f9fb0c3
commit d4f786c990
53 changed files with 1037 additions and 432 deletions

View File

@@ -0,0 +1,11 @@
"use client";
import { SessionProvider } from "next-auth/react";
export default function SessionWrapper({
children,
}: {
children: React.ReactNode;
}) {
return <SessionProvider>{children}</SessionProvider>;
}

11
src/lib/actions.ts Normal file
View File

@@ -0,0 +1,11 @@
"use server";
import { UserController } from "./db";
export async function loginAction(formData: FormData) {
const username = formData.get("username")?.toString();
const password = formData.get("password")?.toString();
if (username && password) await UserController.createUser(username, password);
}

63
src/lib/ai.ts Normal file
View File

@@ -0,0 +1,63 @@
import { format } from "util";
async function callZhipuAPI(
messages: { role: string; content: string }[],
model = "glm-4.6",
) {
const url = "https://open.bigmodel.cn/api/paas/v4/chat/completions";
const response = await fetch(url, {
method: "POST",
headers: {
Authorization: "Bearer " + process.env.ZHIPU_API_KEY,
"Content-Type": "application/json",
},
body: JSON.stringify({
model: model,
messages: messages,
temperature: 0.2,
thinking: {
type: "disabled",
},
}),
});
if (!response.ok) {
throw new Error(`API 调用失败: ${response.status}`);
}
return await response.json();
}
export async function getLLMAnswer(prompt: string) {
return (
await callZhipuAPI([
{
role: "user",
content: prompt,
},
])
).choices[0].message.content.trim() as string;
}
export async function simpleGetLLMAnswer(
prompt: string,
searchParams: URLSearchParams,
args: string[],
) {
if (args.some((arg) => typeof searchParams.get(arg) !== "string")) {
return Response.json({
status: "error",
message: "Missing required parameters",
});
}
return Response.json({
status: "success",
message: await getLLMAnswer(
format(
prompt,
...args.map((v) => searchParams.get(v)),
),
),
});
}

View File

@@ -24,14 +24,126 @@ export class UserController {
}
static async getUserByUsername(username: string) {
try {
const user = await pool.query("SELECT * FROM users WHERE username = $1", [username]);
const user = await pool.query("SELECT * FROM users WHERE username = $1", [
username,
]);
return user.rows[0];
} catch (e) {
console.log(e);
}
}
static async deleteUserById(id: number) {
try {
await pool.query("DELETE FROM users WHERE id = $1", [id]);
} catch (e) {
console.log(e);
}
}
}
export class FolderController {
static async getFolderById(id: number) {
try {
const folder = await pool.query("SELECT * FROM folders WHERE id = $1", [
id,
]);
return folder.rows[0];
} catch (e) {
console.log(e);
}
}
static async deleteFolderById(id: number) {
try {
await pool.query("DELETE FROM folders WHERE id = $1", [id]);
} catch (e) {
console.log(e);
}
}
static async getFoldersByOwner(owner: string) {
try {
const folders = await pool.query(
"SELECT * FROM folders WHERE owner = $1",
[owner],
);
return folders.rows;
} catch (e) {
console.log(e);
}
}
static async createFolder(name: string, owner: string) {
try {
return (
await pool.query("INSERT INTO folders (name, owner) VALUES ($1, $2)", [
name,
owner,
])
).rows[0];
} catch (e) {
console.log(e);
}
}
}
export class WordPairController {
static async createWordPair(
locale1: string,
locale2: string,
text1: string,
text2: string,
folderId: number,
) {
try {
await pool.query(
"INSERT INTO word_pairs (locale1, locale2, text1, text2, folder_id) VALUES ($1, $2, $3, $4, $5)",
[locale1, locale2, text1, text2, folderId],
);
} catch (e) {
console.log(e);
}
}
static async getWordPairById(id: number) {
try {
const wordPair = await pool.query(
"SELECT * FROM word_pairs WHERE id = $1",
[id],
);
return wordPair.rows[0];
} catch (e) {
console.log(e);
}
}
static async deleteWordPairById(id: number) {
try {
await pool.query("DELETE FROM word_pairs WHERE id = $1", [id]);
} catch (e) {
console.log(e);
}
}
static async updateWordPairById(
id: number,
locale1: string,
locale2: string,
text1: string,
text2: string,
) {
try {
await pool.query(
"UPDATE word_pairs SET locale1 = $1, locale2 = $2, text1 = $3, text2 = $4 WHERE id = $5",
[locale1, locale2, text1, text2, id],
);
} catch (e) {
console.log(e);
}
}
static async getWordPairsByFolderId(folderId: number) {
try {
const wordPairs = await pool.query(
"SELECT * FROM word_pairs WHERE folder_id = $1",
[folderId],
);
return wordPairs.rows;
} catch (e) {
console.log(e);
}
}
}

View File

@@ -0,0 +1,17 @@
import { TranslationHistoryArraySchema, TranslationHistorySchema } from "@/interfaces";
import { getLocalStorageOperator } from "@/utils";
import z from "zod";
const MAX_HISTORY_LENGTH = 50;
export const tlso = getLocalStorageOperator<typeof TranslationHistoryArraySchema>(
"translator",
TranslationHistoryArraySchema,
);
export const tlsoPush = (item: z.infer<typeof TranslationHistorySchema>) => {
tlso.set(
[...tlso.get(), item as z.infer<typeof TranslationHistorySchema>].slice(
-MAX_HISTORY_LENGTH,
),
);
};

16
src/lib/tts.ts Normal file
View File

@@ -0,0 +1,16 @@
import { ProsodyOptions } from "edge-tts-universal";
import { EdgeTTS } from "edge-tts-universal/browser";
export async function getTTSAudioUrl(
text: string,
short_name: string,
options: ProsodyOptions | undefined = undefined,
) {
const tts = new EdgeTTS(text, short_name, options);
try {
const result = await tts.synthesize();
return URL.createObjectURL(result.audio);
} catch (e) {
throw e;
}
}