This commit is contained in:
@@ -1,11 +0,0 @@
|
||||
"use client";
|
||||
|
||||
import { SessionProvider } from "next-auth/react";
|
||||
|
||||
export default function SessionWrapper({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
return <SessionProvider>{children}</SessionProvider>;
|
||||
}
|
||||
@@ -1,3 +1,5 @@
|
||||
"use server";
|
||||
|
||||
import { format } from "util";
|
||||
|
||||
async function callZhipuAPI(
|
||||
@@ -3,8 +3,8 @@
|
||||
import {
|
||||
folderCreateInput,
|
||||
folderUpdateInput,
|
||||
} from "../../../generated/prisma/models";
|
||||
import prisma from "../db";
|
||||
} from "../../../../generated/prisma/models";
|
||||
import prisma from "../../db";
|
||||
|
||||
export async function getFoldersByOwner(owner: string) {
|
||||
const folders = await prisma.folder.findMany({
|
||||
@@ -3,8 +3,8 @@
|
||||
import {
|
||||
text_pairCreateInput,
|
||||
text_pairUpdateInput,
|
||||
} from "../../../generated/prisma/models";
|
||||
import prisma from "../db";
|
||||
} from "../../../../generated/prisma/models";
|
||||
import prisma from "../../db";
|
||||
|
||||
export async function createTextPair(data: text_pairCreateInput) {
|
||||
await prisma.text_pair.create({
|
||||
@@ -1,6 +1,6 @@
|
||||
"use server";
|
||||
|
||||
import { getLLMAnswer } from "../ai";
|
||||
import { getLLMAnswer } from "./ai";
|
||||
|
||||
export const genIPA = async (text: string) => {
|
||||
return (
|
||||
|
||||
58
src/lib/browser/localStorageOperators.ts
Normal file
58
src/lib/browser/localStorageOperators.ts
Normal file
@@ -0,0 +1,58 @@
|
||||
import {
|
||||
TranslationHistoryArraySchema,
|
||||
TranslationHistorySchema,
|
||||
} from "@/lib/interfaces";
|
||||
import z from "zod";
|
||||
import { shallowEqual } from "../utils";
|
||||
|
||||
export const getLocalStorageOperator = <T extends z.ZodTypeAny>(
|
||||
key: string,
|
||||
schema: T,
|
||||
) => {
|
||||
return {
|
||||
get: (): z.infer<T> => {
|
||||
try {
|
||||
const item = globalThis.localStorage.getItem(key);
|
||||
|
||||
if (!item) return [];
|
||||
|
||||
const rawData = JSON.parse(item) as z.infer<T>;
|
||||
const result = schema.safeParse(rawData);
|
||||
|
||||
if (result.success) {
|
||||
return result.data;
|
||||
} else {
|
||||
console.error(
|
||||
"Invalid data structure in localStorage:",
|
||||
result.error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to parse ${key} data:`, e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
set: (data: z.infer<T>) => {
|
||||
if (!globalThis.localStorage) return;
|
||||
globalThis.localStorage.setItem(key, JSON.stringify(data));
|
||||
return data;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
const MAX_HISTORY_LENGTH = 50;
|
||||
export const tlso = getLocalStorageOperator<
|
||||
typeof TranslationHistoryArraySchema
|
||||
>("translator", TranslationHistoryArraySchema);
|
||||
|
||||
export const tlsoPush = (item: z.infer<typeof TranslationHistorySchema>) => {
|
||||
const oldHistory = tlso.get();
|
||||
if (oldHistory.some((v) => shallowEqual(v, item))) return oldHistory;
|
||||
|
||||
const newHistory = [...oldHistory, item].slice(-MAX_HISTORY_LENGTH);
|
||||
tlso.set(newHistory);
|
||||
|
||||
return newHistory;
|
||||
};
|
||||
@@ -1,5 +1,4 @@
|
||||
import { ProsodyOptions } from "edge-tts-universal";
|
||||
import { EdgeTTS } from "edge-tts-universal/browser";
|
||||
import { ProsodyOptions, EdgeTTS } from "edge-tts-universal/browser";
|
||||
|
||||
export async function getTTSAudioUrl(
|
||||
text: string,
|
||||
@@ -1,22 +0,0 @@
|
||||
import {
|
||||
TranslationHistoryArraySchema,
|
||||
TranslationHistorySchema,
|
||||
} from "@/lib/interfaces";
|
||||
import { getLocalStorageOperator, shallowEqual } from "@/lib/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>) => {
|
||||
const oldHistory = tlso.get();
|
||||
if (oldHistory.some((v) => shallowEqual(v, item))) return oldHistory;
|
||||
|
||||
const newHistory = [...oldHistory, item].slice(-MAX_HISTORY_LENGTH);
|
||||
tlso.set(newHistory);
|
||||
|
||||
return newHistory;
|
||||
};
|
||||
127
src/lib/utils.ts
127
src/lib/utils.ts
@@ -1,130 +1,3 @@
|
||||
import { EdgeTTS, ProsodyOptions } from "edge-tts-universal/browser";
|
||||
import { env } from "process";
|
||||
import z from "zod";
|
||||
import { NextResponse } from "next/server";
|
||||
|
||||
export function inspect(word: string) {
|
||||
const goto = (url: string) => {
|
||||
window.open(url, "_blank");
|
||||
};
|
||||
return () => {
|
||||
word = word.toLowerCase();
|
||||
goto(`https://www.youdao.com/result?word=${word}&lang=en`);
|
||||
};
|
||||
}
|
||||
|
||||
export function urlGoto(url: string) {
|
||||
window.open(url, "_blank");
|
||||
}
|
||||
const API_KEY = env.ZHIPU_API_KEY;
|
||||
export 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 " + 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 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;
|
||||
}
|
||||
}
|
||||
|
||||
export const getLocalStorageOperator = <T extends z.ZodTypeAny>(
|
||||
key: string,
|
||||
schema: T,
|
||||
) => {
|
||||
return {
|
||||
get: (): z.infer<T> => {
|
||||
try {
|
||||
const item = globalThis.localStorage.getItem(key);
|
||||
|
||||
if (!item) return [];
|
||||
|
||||
const rawData = JSON.parse(item) as z.infer<T>;
|
||||
const result = schema.safeParse(rawData);
|
||||
|
||||
if (result.success) {
|
||||
return result.data;
|
||||
} else {
|
||||
console.error(
|
||||
"Invalid data structure in localStorage:",
|
||||
result.error,
|
||||
);
|
||||
return [];
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(`Failed to parse ${key} data:`, e);
|
||||
return [];
|
||||
}
|
||||
},
|
||||
set: (data: z.infer<T>) => {
|
||||
if (!globalThis.localStorage) return;
|
||||
globalThis.localStorage.setItem(key, JSON.stringify(data));
|
||||
return data;
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export function handleAPIError(error: unknown, message: string) {
|
||||
console.error(message, error);
|
||||
return NextResponse.json(
|
||||
{ error: "服务器内部错误", message },
|
||||
{ status: 500 },
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
export const letsFetch = (
|
||||
url: string,
|
||||
onSuccess: (message: string) => void,
|
||||
onError: (message: string) => void,
|
||||
onFinally: () => void,
|
||||
) => {
|
||||
return fetch(url)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.status === "success") {
|
||||
onSuccess(data.message);
|
||||
} else if (data.status === "error") {
|
||||
onError(data.message);
|
||||
} else {
|
||||
onError("Unknown error");
|
||||
}
|
||||
})
|
||||
.finally(onFinally);
|
||||
};
|
||||
|
||||
export function isNonNegativeInteger(str: string): boolean {
|
||||
return /^\d+$/.test(str);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user