Isolate user configuration data
Some checks failed
Build and Deploy / build-and-deploy (push) Failing after 5s

This commit is contained in:
zyc 2026-05-28 17:14:09 +08:00
parent e8f381f73a
commit c8a8560175
58 changed files with 1858 additions and 1172 deletions

File diff suppressed because it is too large Load Diff

File diff suppressed because one or more lines are too long

View File

@ -15,6 +15,7 @@ import socketInit from "@/socket/index";
import { isEletron } from "@/utils/getPath";
import { normalizeBearerToken, verifyAuthToken } from "@/lib/auth";
import { requestHasProjectAccess } from "@/lib/workspaceAccess";
import { runWithRequestContext } from "@/lib/requestContext";
const app = express();
const server = http.createServer(app);
@ -126,7 +127,7 @@ export default async function startServe(randomPort: Boolean = false) {
return res.status(403).send({ message: "无权访问该项目" });
}
next();
runWithRequestContext({ userId: authUser.id }, () => next());
});
const router = await import("@/router");

View File

@ -1,6 +1,6 @@
import { Request } from "express";
import jwt from "jsonwebtoken";
import u from "@/utils";
import db from "@/utils/db";
export interface AuthUser {
id: number;
@ -18,7 +18,7 @@ export function getCurrentUser(req: Request): AuthUser {
}
export async function getTokenKey(): Promise<string | null> {
const tokenData = await u.db("o_setting").where("key", "tokenKey").first();
const tokenData = await db("o_setting").where("key", "tokenKey").first();
return (tokenData?.value as string | undefined) ?? null;
}
@ -61,6 +61,6 @@ export function publicUser(user: { id?: number | null; name?: string | null; pho
export async function assertProjectAccess(projectId: number, userId: number): Promise<boolean> {
if (!Number.isFinite(projectId) || !Number.isFinite(userId)) return false;
const project = await u.db("o_project").where({ id: projectId, userId }).select("id").first();
const project = await db("o_project").where({ id: projectId, userId }).select("id").first();
return Boolean(project);
}

View File

@ -96,6 +96,60 @@ export default async (knex: Knex): Promise<void> => {
});
}
if (!(await knex.schema.hasTable("o_userVendorConfig"))) {
await knex.schema.createTable("o_userVendorConfig", (table) => {
table.integer("userId").notNullable();
table.string("vendorId").notNullable();
table.text("inputValues");
table.text("models");
table.integer("enable");
table.primary(["userId", "vendorId"]);
});
}
if (!(await knex.schema.hasTable("o_userAgentDeploy"))) {
await knex.schema.createTable("o_userAgentDeploy", (table) => {
table.integer("userId").notNullable();
table.string("key").notNullable();
table.string("model");
table.string("modelName");
table.text("vendorId");
table.integer("temperature");
table.integer("maxOutputTokens");
table.boolean("disabled").defaultTo(false);
table.primary(["userId", "key"]);
});
}
if (!(await knex.schema.hasTable("o_userSetting"))) {
await knex.schema.createTable("o_userSetting", (table) => {
table.integer("userId").notNullable();
table.string("key").notNullable();
table.text("value");
table.primary(["userId", "key"]);
});
}
if (!(await knex.schema.hasTable("o_userPrompt"))) {
await knex.schema.createTable("o_userPrompt", (table) => {
table.integer("userId").notNullable();
table.integer("promptId").notNullable();
table.text("useData");
table.primary(["userId", "promptId"]);
});
}
if (!(await knex.schema.hasTable("o_userModelPrompt"))) {
await knex.schema.createTable("o_userModelPrompt", (table) => {
table.integer("userId").notNullable();
table.text("vendorId").notNullable();
table.text("model").notNullable();
table.string("fileName");
table.text("path");
table.primary(["userId", "vendorId", "model"]);
});
}
await addColumn("o_project", "userId", "integer");
if ((await knex.schema.hasTable("o_project")) && (await knex.schema.hasColumn("o_project", "userId"))) {
await db("o_project")
@ -115,9 +169,51 @@ export default async (knex: Knex): Promise<void> => {
await addColumn("o_assets", "audioBindState", "integer");
await addColumn("o_modelPrompt", "fileName", "string");
await addColumn("o_modelPrompt", "path", "string");
const vendorDataSelect = await u.db("o_vendorConfig").whereIn("id", ["deepseek", "atlascloud"]).select("*");
const userVendorRows = await db("o_userVendorConfig" as any).count<{ count: number }[]>({ count: "*" });
if (Number(userVendorRows[0]?.count ?? 0) === 0) {
const vendorConfigs = await db("o_vendorConfig").select("*");
for (const vendor of vendorConfigs) {
if (!vendor.id) continue;
await db("o_userVendorConfig" as any).insert({
userId: 1,
vendorId: vendor.id,
inputValues: vendor.inputValues ?? "{}",
models: vendor.models ?? "[]",
enable: vendor.enable ?? 0,
});
}
}
await db("o_vendorConfig").update({ inputValues: "{}" });
const userPromptRows = await db("o_userPrompt" as any).count<{ count: number }[]>({ count: "*" });
if (Number(userPromptRows[0]?.count ?? 0) === 0) {
const prompts = await db("o_prompt").whereNotNull("useData").select("id", "useData");
for (const prompt of prompts) {
if (!prompt.id || !prompt.useData) continue;
await db("o_userPrompt" as any).insert({ userId: 1, promptId: prompt.id, useData: prompt.useData });
}
await db("o_prompt").update({ useData: null });
}
const userModelPromptRows = await db("o_userModelPrompt" as any).count<{ count: number }[]>({ count: "*" });
if (Number(userModelPromptRows[0]?.count ?? 0) === 0) {
const modelPrompts = await db("o_modelPrompt").select("vendorId", "model", "fileName", "path");
for (const item of modelPrompts) {
if (!item.vendorId || !item.model) continue;
await db("o_userModelPrompt" as any).insert({
userId: 1,
vendorId: item.vendorId,
model: item.model,
fileName: item.fileName,
path: item.path,
});
}
}
const vendorDataSelect = await db("o_vendorConfig").whereIn("id", ["deepseek", "atlascloud"]).select("*");
if (!vendorDataSelect.find((i) => i.id == "deepseek")) {
await u.db("o_vendorConfig").insert({
await db("o_vendorConfig").insert({
id: "deepseek",
inputValues: "{}",
models: "[]",
@ -125,7 +221,7 @@ export default async (knex: Knex): Promise<void> => {
});
}
if (!vendorDataSelect.find((i) => i.id == "atlascloud")) {
await u.db("o_vendorConfig").insert({
await db("o_vendorConfig").insert({
id: "atlascloud",
inputValues: "{}",
models: "[]",
@ -141,16 +237,15 @@ export default async (knex: Knex): Promise<void> => {
data: `你是一个音色匹配助手。\n你的任务是根据给定角色资产的名称与描述从候选音频列表中选出最合适的音色。\n匹配规则\n1. 优先根据角色性别、年龄、性格等特征与音色描述进行语义匹配;\n2. 同一角色仅可匹配一个音色;\n3. 若候选列表中没有合适的音色,则无需返回 audioId`,
});
//检测o_setting是否有agentUseMode
const agentUserMode = await u.db("o_setting").where("key", "agentUseMode").first();
const agentUserMode = await db("o_setting").where("key", "agentUseMode").first();
if (!agentUserMode) {
const allDeployData = await u
.db("o_agentDeploy")
const allDeployData = await db("o_agentDeploy")
.leftJoin("o_vendorConfig", "o_vendorConfig.id", "o_agentDeploy.vendorId")
.select("o_agentDeploy.*");
const advancedData = allDeployData.filter((item: any) => item.key?.includes(":"));
const notValModelData = advancedData.filter((item) => !item.modelName);
await u.db("o_setting").insert({
await db("o_setting").insert({
key: "agentUseMode",
value: notValModelData.length ? "0" : "1",
});
@ -239,9 +334,9 @@ async function tempOnsert(tsCode: string) {
const jsCode = transform(tsCode, { transforms: ["typescript"] }).code;
const exports = u.vm(jsCode);
const vendor = exports.vendor;
const data = await u.db("o_vendorConfig").where("id", vendor.id).first();
const data = await db("o_vendorConfig").where("id", vendor.id).first();
if (data) return;
await u.db("o_vendorConfig").insert({
await db("o_vendorConfig").insert({
id: vendor.id,
inputValues: JSON.stringify(vendor.inputValues ?? {}),
models: JSON.stringify([]),

View File

@ -44,6 +44,61 @@ export default async (knex: Knex, forceInit: boolean = false): Promise<void> =>
table.unique(["id"]);
},
},
// 用户级配置表:平台配置作为模板,敏感值与个人偏好写入用户表
{
name: "o_userVendorConfig",
builder: (table) => {
table.integer("userId").notNullable();
table.string("vendorId").notNullable();
table.text("inputValues");
table.text("models");
table.integer("enable");
table.primary(["userId", "vendorId"]);
},
},
{
name: "o_userAgentDeploy",
builder: (table) => {
table.integer("userId").notNullable();
table.string("key").notNullable();
table.string("model");
table.string("modelName");
table.text("vendorId");
table.integer("temperature");
table.integer("maxOutputTokens");
table.boolean("disabled").defaultTo(false);
table.primary(["userId", "key"]);
},
},
{
name: "o_userSetting",
builder: (table) => {
table.integer("userId").notNullable();
table.string("key").notNullable();
table.text("value");
table.primary(["userId", "key"]);
},
},
{
name: "o_userPrompt",
builder: (table) => {
table.integer("userId").notNullable();
table.integer("promptId").notNullable();
table.text("useData");
table.primary(["userId", "promptId"]);
},
},
{
name: "o_userModelPrompt",
builder: (table) => {
table.integer("userId").notNullable();
table.text("vendorId").notNullable();
table.text("model").notNullable();
table.string("fileName");
table.text("path");
table.primary(["userId", "vendorId", "model"]);
},
},
//项目表
{
name: "o_project",

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

@ -0,0 +1,16 @@
import { AsyncLocalStorage } from "node:async_hooks";
export interface RequestContext {
userId?: number;
}
export const requestContext = new AsyncLocalStorage<RequestContext>();
export function runWithRequestContext<T>(context: RequestContext, fn: () => T): T {
return requestContext.run(context, fn);
}
export function getContextUserId(): number | undefined {
const userId = requestContext.getStore()?.userId;
return Number.isFinite(userId) ? userId : undefined;
}

164
src/lib/userConfig.ts Normal file
View File

@ -0,0 +1,164 @@
import { Request } from "express";
import db from "@/utils/db";
import { getContextUserId } from "@/lib/requestContext";
type JsonRecord = Record<string, any>;
export type ModelPromptBinding = {
vendorId?: string | null;
model?: string | null;
fileName?: string | null;
path?: string | null;
scope: "user" | "global";
};
export function requireRequestUserId(req: Request): number {
const userId = Number((req as any).user?.id);
if (!Number.isFinite(userId)) throw new Error("未登录");
return userId;
}
export function parseJson<T>(value: string | null | undefined, fallback: T): T {
if (!value) return fallback;
try {
return JSON.parse(value) as T;
} catch {
return fallback;
}
}
async function upsert(table: string, where: JsonRecord, patch: JsonRecord) {
const existing = await db(table as any).where(where).first();
if (existing) {
await db(table as any).where(where).update(patch);
return;
}
await db(table as any).insert({ ...where, ...patch });
}
export async function getUserSettingValue(key: string, userId = getContextUserId()): Promise<string | undefined> {
if (userId) {
const userSetting = await db("o_userSetting" as any).where({ userId, key }).first();
if (userSetting) return userSetting.value ?? "";
}
const setting = await db("o_setting").where({ key }).first();
return setting?.value ?? undefined;
}
export async function setUserSettingValue(userId: number, key: string, value: string) {
await upsert("o_userSetting", { userId, key }, { value });
}
export async function getVendorConfigForUser(vendorId: string, userId = getContextUserId()) {
const base = await db("o_vendorConfig").where("id", vendorId).first();
if (!base) return null;
const userConfig = userId ? await db("o_userVendorConfig" as any).where({ userId, vendorId }).first() : null;
return {
...base,
inputValues: userConfig?.inputValues ?? "{}",
models: userConfig?.models ?? base.models ?? "[]",
enable: userConfig?.enable ?? base.enable ?? 0,
};
}
export async function getEnabledVendorIdsForUser(userId = getContextUserId()) {
const baseRows = await db("o_vendorConfig").select("id", "enable");
const userRows = userId ? await db("o_userVendorConfig" as any).where({ userId }).select("vendorId", "enable") : [];
const userEnableMap = new Map(userRows.map((row: any) => [row.vendorId, row.enable]));
return baseRows
.filter((row) => Number(userEnableMap.has(row.id) ? userEnableMap.get(row.id) : row.enable) === 1)
.map((row) => row.id!)
.filter(Boolean);
}
export async function setUserVendorConfig(userId: number, vendorId: string, patch: JsonRecord) {
await upsert("o_userVendorConfig", { userId, vendorId }, patch);
}
export async function getEditableVendorModels(userId: number, vendorId: string) {
const base = await db("o_vendorConfig").where("id", vendorId).first();
const userConfig = await db("o_userVendorConfig" as any).where({ userId, vendorId }).first();
return parseJson<any[]>(userConfig?.models ?? base?.models, []);
}
export async function getAgentDeployForUser(key: string, userId = getContextUserId()) {
const base = await db("o_agentDeploy").where({ key }).first();
if (!base) return null;
const userDeploy = userId ? await db("o_userAgentDeploy" as any).where({ userId, key }).first() : null;
return {
...base,
model: userDeploy?.model ?? base.model,
modelName: userDeploy?.modelName ?? base.modelName,
vendorId: userDeploy?.vendorId ?? base.vendorId,
temperature: userDeploy?.temperature ?? base.temperature,
maxOutputTokens: userDeploy?.maxOutputTokens ?? base.maxOutputTokens,
disabled: userDeploy?.disabled ?? base.disabled,
};
}
export async function getAllAgentDeployForUser(userId: number) {
const rows = await db("o_agentDeploy").select("*");
const userRows = await db("o_userAgentDeploy" as any).where({ userId }).select("*");
const userMap = new Map(userRows.map((row: any) => [row.key, row]));
return rows.map((row) => {
const userRow = userMap.get(row.key);
return {
...row,
model: userRow?.model ?? row.model,
modelName: userRow?.modelName ?? row.modelName,
vendorId: userRow?.vendorId ?? row.vendorId,
temperature: userRow?.temperature ?? row.temperature,
maxOutputTokens: userRow?.maxOutputTokens ?? row.maxOutputTokens,
disabled: userRow?.disabled ?? row.disabled,
};
});
}
export async function setUserAgentDeploy(userId: number, key: string, patch: JsonRecord) {
await upsert("o_userAgentDeploy", { userId, key }, patch);
}
export async function getPromptForUser(type: string, userId = getContextUserId()) {
const prompt = await db("o_prompt").where({ type }).first();
if (!prompt) return null;
const userPrompt = userId ? await db("o_userPrompt" as any).where({ userId, promptId: prompt.id }).first() : null;
return {
...prompt,
data: userPrompt?.useData ?? prompt.useData ?? prompt.data,
useData: userPrompt?.useData ?? null,
};
}
export async function setUserPrompt(userId: number, promptId: number, useData: string) {
await upsert("o_userPrompt", { userId, promptId }, { useData });
}
export async function getModelPromptBindingForUser(
vendorId: string,
model: string,
userId = getContextUserId(),
): Promise<ModelPromptBinding | null> {
if (userId) {
const userBinding = await db("o_userModelPrompt" as any).where({ userId, vendorId, model }).first();
if (userBinding) return { ...userBinding, scope: "user" };
}
const globalBinding = await db("o_modelPrompt").where({ vendorId, model }).first();
return globalBinding ? { ...globalBinding, scope: "global" } : null;
}
export async function getModelPromptBindingsForUser(vendorId: string, userId: number) {
const globalRows = await db("o_modelPrompt").where({ vendorId }).select("*");
const userRows = await db("o_userModelPrompt" as any).where({ userId, vendorId }).select("*");
const map = new Map<string, ModelPromptBinding>();
for (const row of globalRows) {
if (row.model) map.set(row.model, { ...row, scope: "global" });
}
for (const row of userRows) {
if (row.model) map.set(row.model, { ...row, scope: "user" });
}
return [...map.values()];
}
export async function setUserModelPromptBinding(userId: number, vendorId: string, model: string, patch: JsonRecord) {
await upsert("o_userModelPrompt", { userId, vendorId, model }, patch);
}

10
src/lib/userStorage.ts Normal file
View File

@ -0,0 +1,10 @@
import path from "path";
import getPath from "@/utils/getPath";
export function getUserStoragePath(userId: number, parts: string[] = []): string {
return path.join(getPath(["users", String(userId)]), ...parts);
}
export function getUserModelPromptRoot(userId: number): string {
return getUserStoragePath(userId, ["modelPrompt"]);
}

View File

@ -4,6 +4,7 @@ import { z } from "zod";
import { error, success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { tool, jsonSchema } from "ai";
import { getPromptForUser } from "@/lib/userConfig";
const router = express.Router();
// 获取资产
@ -49,13 +50,8 @@ export default router.post(
});
const audioList = audioData.map((i) => `- ID:${i.id} | 名称:${i.name} | 描述:${i.describe ?? "无"}`).join("\n");
const promptData = await u.db("o_prompt").where("type", "audioBindPrompt").first();
let audioBindPrompt = "" as string | undefined;
if (promptData && promptData.useData) {
audioBindPrompt = promptData.useData;
} else {
audioBindPrompt = promptData?.data ?? undefined;
}
const promptData = await getPromptForUser("audioBindPrompt");
const audioBindPrompt = promptData?.data ?? undefined;
const { text } = await u.Ai.Text("universalAi").invoke({
messages: [
{

View File

@ -3,6 +3,7 @@ import u from "@/utils";
import { z } from "zod";
import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { getEnabledVendorIdsForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,21 +13,22 @@ export default router.post(
}),
async (req, res) => {
const { type } = req.body;
const dataList = await u.db("o_vendorConfig").select("id").where("enable", 1);
if (!dataList || dataList.length === 0) {
const userId = requireRequestUserId(req);
const vendorIds = await getEnabledVendorIdsForUser(userId);
if (!vendorIds.length) {
return res.status(404).send({ error: "模型未找到" });
}
const modelList = await Promise.all(dataList.map((i) => u.vendor.getModelList(i.id!)));
const modelList = await Promise.all(vendorIds.map((id) => u.vendor.getModelList(id)));
const result = await Promise.all(
dataList.map(async (data, index) => {
const vendorData = await u.vendor.getVendor(data.id!);
vendorIds.map(async (id, index) => {
const vendorData = await u.vendor.getVendor(id);
const models = modelList[index];
const filtered =
type === "all"
? models.filter((item: { type: string }) => item.type !== "video")
: models.filter((item: { type: string }) => item.type === type);
return filtered.map((item: { name: string; modelName: string; type: string }) => ({
id: data.id,
id,
label: item.name,
value: item.modelName,
type: item.type,

View File

@ -4,6 +4,7 @@ import { z } from "zod";
import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { info } from "node:console";
import { getPromptForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -25,6 +26,7 @@ export default router.post(
}),
async (req, res) => {
const { trackId, projectId, info, model } = req.body;
const userId = requireRequestUserId(req);
//查询参数
const images = await Promise.all(
info.map(async (item: { id: number; sources: string }) => {
@ -85,13 +87,8 @@ export default router.post(
const [id, modelData] = model.split(/:(.+)/);
const projectData = await u.db("o_project").select("*").where({ id: projectId }).first();
const videoPrompt = await u.db("o_prompt").where("type", "videoPromptGeneration").first();
let videoPromptGeneration = "" as string | undefined;
if (videoPrompt && videoPrompt.useData) {
videoPromptGeneration = videoPrompt.useData;
} else {
videoPromptGeneration = videoPrompt?.data ?? undefined;
}
const videoPrompt = await getPromptForUser("videoPromptGeneration", userId);
const videoPromptGeneration = videoPrompt?.data ?? undefined;
const artStyle = projectData?.artStyle || "无";
const visualManual = u.getArtPrompt(artStyle, "art_skills", "art_storyboard_video");
const content = `

View File

@ -5,6 +5,8 @@ import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import fs from "fs/promises";
import path from "path";
import { getModelPromptBindingForUser, getPromptForUser, requireRequestUserId } from "@/lib/userConfig";
import { getUserModelPromptRoot } from "@/lib/userStorage";
const router = express.Router();
export default router.post(
@ -23,6 +25,7 @@ export default router.post(
}),
async (req, res) => {
const { trackId, projectId, info, model, mode } = req.body;
const userId = requireRequestUserId(req);
//查询参数
const images = await Promise.all(
@ -96,13 +99,13 @@ export default router.post(
const [id, modelData] = model.split(/:(.+)/);
const projectData = await u.db("o_project").select("*").where({ id: projectId }).first();
const videoPrompt = await u.db("o_prompt").where("type", "videoPromptGeneration").first();
const videoPrompt = await getPromptForUser("videoPromptGeneration", userId);
let videoPromptGeneration = "" as string | undefined;
const modelPromptData = await u.db("o_modelPrompt").where("vendorId", id).where("model", modelData).first();
const modelPromptData = await getModelPromptBindingForUser(id, modelData, userId);
//查询到 有绑定对应视频提示词
if (modelPromptData) {
const modelPromptRoot = u.getPath(["modelPrompt"]);
const modelPromptRoot = modelPromptData.scope === "user" ? getUserModelPromptRoot(userId) : u.getPath(["modelPrompt"]);
try {
const fullPath = path.join(modelPromptRoot, modelPromptData?.path!);
const content = await fs.readFile(fullPath, "utf-8");
@ -143,11 +146,7 @@ export default router.post(
//备选
if (!videoPromptGeneration) {
if (videoPrompt && videoPrompt.useData) {
videoPromptGeneration = videoPrompt.useData;
} else {
videoPromptGeneration = videoPrompt?.data ?? undefined;
}
videoPromptGeneration = videoPrompt?.data ?? undefined;
}
const artStyle = projectData?.artStyle || "无";

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import u from "@/utils";
import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import { getAgentDeployForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,7 +13,8 @@ export default router.post(
}),
async (req, res) => {
const { key } = req.body;
const data = await u.db("o_agentDeploy").select("o_agentDeploy.*").where("o_agentDeploy.key", key).first();
const userId = requireRequestUserId(req);
const data = await getAgentDeployForUser(key, userId);
const [id, modelName] = data ? data.modelName.split(/:(.+)/) : [];
const models = await u.vendor.getModelList(id);
const model = models.find((m) => m.modelName === modelName);

View File

@ -6,6 +6,7 @@ import { validateFields } from "@/middleware/middleware";
import { useSkill } from "@/utils/agent/skillsTools";
import { tool, jsonSchema } from "ai";
import { o_script } from "@/types/database";
import { getPromptForUser } from "@/lib/userConfig";
const router = express.Router();
@ -204,13 +205,8 @@ export default router.post(
return "无需回复用户任何内容";
},
});
const promptData = await u.db("o_prompt").where("type", "scriptAssetExtraction").first();
let scriptAssetExtraction = "" as string | undefined;
if (promptData && promptData.useData) {
scriptAssetExtraction = promptData.useData;
} else {
scriptAssetExtraction = promptData?.data ?? undefined;
}
const promptData = await getPromptForUser("scriptAssetExtraction");
const scriptAssetExtraction = promptData?.data ?? undefined;
const existingHint = existingAssetsList
? `\n\n【已有资产列表】${existingAssetsList}\n对于已有资产如果在剧本中出现只需在 existingAssetRefs 中给出资产名称和对应的 scriptIds 数组即可,无需重复生成 desc/type。对于新发现的资产不在已有列表中请在 newAssets 中给出完整信息。`
: "";

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import u from "@/utils";
import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import { getVendorConfigForUser, parseJson, requireRequestUserId, setUserAgentDeploy, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,33 +13,32 @@ export default router.post(
}),
async (req, res) => {
const { key } = req.body;
const vendorConfigData = await u.db("o_vendorConfig").where("id", "toonflow").first();
const userId = requireRequestUserId(req);
const vendorConfigData = await getVendorConfigForUser("toonflow", userId);
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.inputValues) return res.status(500).send(error("未找到模型配置数据"));
const inputValue = JSON.parse(vendorConfigData.inputValues!);
const inputValue = parseJson<Record<string, string>>(vendorConfigData.inputValues, {});
inputValue.apiKey = key;
await u
.db("o_vendorConfig")
.where("id", "toonflow")
.update({
inputValues: JSON.stringify(inputValue),
});
await setUserVendorConfig(userId, "toonflow", {
inputValues: JSON.stringify(inputValue),
enable: 1,
});
try {
const resText = await u.Ai.Text(`toonflow:claude-haiku-4-5-20251001`).invoke({
prompt: "1+1等于几,请直接回答2不要解释",
});
if (resText.text) {
await u.db("o_agentDeploy").where("key", "scriptAgent").update({
await setUserAgentDeploy(userId, "scriptAgent", {
model: "claude-sonnet-4-6",
modelName: "toonflow:claude-sonnet-4-6",
vendorId: "toonflow",
});
await u.db("o_agentDeploy").where("key", "productionAgent").update({
await setUserAgentDeploy(userId, "productionAgent", {
model: "claude-sonnet-4-6",
modelName: "toonflow:claude-sonnet-4-6",
vendorId: "toonflow",
});
await u.db("o_agentDeploy").where("key", "universalAi").update({
await setUserAgentDeploy(userId, "universalAi", {
model: "claude-haiku-4-5",
modelName: "toonflow:claude-haiku-4-5-20251001",
vendorId: "toonflow",
@ -48,10 +48,7 @@ export default router.post(
} catch (err) {
console.error(err);
inputValue.apiKey = "";
await u
.db("o_vendorConfig")
.where("id", "toonflow")
.update({ inputValues: JSON.stringify(inputValue) });
await setUserVendorConfig(userId, "toonflow", { inputValues: JSON.stringify(inputValue) });
res.status(400).send(error("KEY无效请重新输入"));
}
},

View File

@ -1,8 +1,9 @@
import express from "express";
import { success } from "@/lib/responseFormat";
import { error, success } from "@/lib/responseFormat";
import u from "@/utils";
import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserAgentDeploy } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -18,8 +19,11 @@ export default router.post(
maxOutputTokens: z.number().optional(),
}),
async (req, res) => {
const { id, name, model, modelName, vendorId, desc, temperature, maxOutputTokens } = req.body;
await u.db("o_agentDeploy").where({ id }).update({ id, name, model, modelName, vendorId, desc, temperature, maxOutputTokens });
const { id, model, modelName, vendorId, temperature, maxOutputTokens } = req.body;
const userId = requireRequestUserId(req);
const baseDeploy = await u.db("o_agentDeploy").where({ id }).first();
if (!baseDeploy?.key) return res.status(400).send(error("未找到Agent配置模板"));
await setUserAgentDeploy(userId, baseDeploy.key, { model, modelName, vendorId, temperature, maxOutputTokens });
res.status(200).send(success("配置成功"));
},
);

View File

@ -1,10 +1,11 @@
import express from "express";
import { success } from "@/lib/responseFormat";
import u from "@/utils";
import { getAllAgentDeployForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post("/", async (req, res) => {
const allData = await u.db("o_agentDeploy").leftJoin("o_vendorConfig", "o_vendorConfig.id", "o_agentDeploy.vendorId").select("o_agentDeploy.*");
const userId = requireRequestUserId(req);
const allData = await getAllAgentDeployForUser(userId);
const qrdinaryData = allData.filter((item: any) => !item.key?.includes(":"));
const advancedData = allData.filter((item: any) => item.key?.includes(":"));
res.status(200).send(success({ qrdinaryData, advancedData }));

View File

@ -1,11 +1,11 @@
import express from "express";
import { success, error } from "@/lib/responseFormat";
import u from "@/utils";
import { getUserSettingValue, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.get("/", async (req, res) => {
const useMode = await u.db("o_setting").where("key", "agentUseMode").first();
console.log("%c Line:9 🍓 useMode", "background:#33a5ff", useMode);
res.status(200).send(success(useMode?.value || "0"));
const userId = requireRequestUserId(req);
const useMode = await getUserSettingValue("agentUseMode", userId);
res.status(200).send(success(useMode || "0"));
});

View File

@ -3,6 +3,7 @@ import u from "@/utils";
import { z } from "zod";
import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserSettingValue } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,9 +13,8 @@ export default router.post(
}),
async (req, res) => {
const { agentUseMode } = req.body;
await u.db("o_setting").where("key", "agentUseMode").update({
value: agentUseMode,
});
const userId = requireRequestUserId(req);
await setUserSettingValue(userId, "agentUseMode", agentUseMode);
res.status(200).send(success("保存设置成功"));
},
);

View File

@ -2,10 +2,12 @@ import express from "express";
import { success, error } from "@/lib/responseFormat";
import u from "@/utils";
import initDB from "@/lib/initDB";
import { getUserSettingValue, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.get("/", async (req, res) => {
const switchAiDevTool = await u.db("o_setting").where("key", "switchAiDevTool").first();
res.status(200).send(success(switchAiDevTool?.value || "0"));
const userId = requireRequestUserId(req);
const switchAiDevTool = await getUserSettingValue("switchAiDevTool", userId);
res.status(200).send(success(switchAiDevTool || "0"));
});

View File

@ -3,6 +3,7 @@ import u from "@/utils";
import { z } from "zod";
import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserSettingValue } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,9 +13,8 @@ export default router.post(
}),
async (req, res) => {
const { switchAiDevTool } = req.body;
await u.db("o_setting").where("key", "switchAiDevTool").update({
value: switchAiDevTool,
});
const userId = requireRequestUserId(req);
await setUserSettingValue(userId, "switchAiDevTool", switchAiDevTool);
res.status(200).send(success("保存设置成功"));
},
);

View File

@ -1,9 +1,14 @@
import express from "express";
import { error, success } from "@/lib/responseFormat";
import u from "@/utils";
import { requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post("/", async (req, res) => {
await u.db("memories").del();
const userId = requireRequestUserId(req);
const projectIds = (await u.db("o_project").where({ userId }).select("id")).map((item) => item.id).filter(Boolean);
for (const projectId of projectIds) {
await u.db("memories").where("isolationKey", "like", `${projectId}:%`).del();
}
res.status(200).send(success(true));
});

View File

@ -1,21 +1,22 @@
import express from "express";
import { error, success } from "@/lib/responseFormat";
import u from "@/utils";
import { getUserSettingValue, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.get("/", async (req, res) => {
const settingData = await u
.db("o_setting")
.whereIn("key", [
"messagesPerSummary",
"shortTermLimit",
"summaryMaxLength",
"summaryLimit",
"ragLimit",
"deepRetrieveSummaryLimit",
"modelOnnxFile",
"modelDtype",
]);
const userId = requireRequestUserId(req);
const keys = [
"messagesPerSummary",
"shortTermLimit",
"summaryMaxLength",
"summaryLimit",
"ragLimit",
"deepRetrieveSummaryLimit",
"modelOnnxFile",
"modelDtype",
];
const settingData = await Promise.all(keys.map(async (key) => ({ key, value: await getUserSettingValue(key, userId) })));
if (!settingData) return res.status(400).send(error(`获取记忆配置失败`));
const memoryObj: Record<string, number | string | string[]> = {};

View File

@ -3,6 +3,7 @@ import u from "@/utils";
import { z } from "zod";
import { success } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserSettingValue } from "@/lib/userConfig";
const router = express.Router();
// 获取用户
@ -21,22 +22,18 @@ export default router.post(
async (req, res) => {
const { messagesPerSummary, shortTermLimit, summaryMaxLength, summaryLimit, ragLimit, deepRetrieveSummaryLimit, modelOnnxFile, modelDtype } =
req.body;
const userId = requireRequestUserId(req);
const upsert = async (key: string, value: string) => {
const exists = await u.db("o_setting").where("key", key).first();
if (exists) {
await u.db("o_setting").where("key", key).update({ value });
} else {
await u.db("o_setting").insert({ key, value });
}
await setUserSettingValue(userId, key, value);
};
await upsert("messagesPerSummary", messagesPerSummary);
await upsert("shortTermLimit", shortTermLimit);
await upsert("summaryMaxLength", summaryMaxLength);
await upsert("summaryLimit", summaryLimit);
await upsert("ragLimit", ragLimit);
await upsert("deepRetrieveSummaryLimit", deepRetrieveSummaryLimit);
await upsert("messagesPerSummary", String(messagesPerSummary));
await upsert("shortTermLimit", String(shortTermLimit));
await upsert("summaryMaxLength", String(summaryMaxLength));
await upsert("summaryLimit", String(summaryLimit));
await upsert("ragLimit", String(ragLimit));
await upsert("deepRetrieveSummaryLimit", String(deepRetrieveSummaryLimit));
await upsert("modelOnnxFile", JSON.stringify(modelOnnxFile));
await upsert("modelDtype", modelDtype);

View File

@ -3,6 +3,7 @@ import { error, success } from "@/lib/responseFormat";
import u from "@/utils";
import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserModelPromptBinding } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -15,13 +16,8 @@ export default router.post(
}),
async (req, res) => {
const { vendorId, model, path, fileName } = req.body;
const data = await u.db("o_modelPrompt").where("model", model).andWhere("vendorId", vendorId).select("*").first();
if (data) {
await u.db("o_modelPrompt").where("model", model).andWhere("vendorId", vendorId).update({ fileName, path });
res.status(200).send(success("绑定成功"));
} else {
await u.db("o_modelPrompt").insert({ vendorId, model, path, fileName });
res.status(200).send(success("绑定成功"));
}
const userId = requireRequestUserId(req);
await setUserModelPromptBinding(userId, vendorId, model, { fileName, path });
res.status(200).send(success("绑定成功"));
},
);

View File

@ -5,6 +5,8 @@ import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import fs from "fs/promises";
import path from "path";
import { requireRequestUserId } from "@/lib/userConfig";
import { getUserModelPromptRoot } from "@/lib/userStorage";
const router = express.Router();
@ -15,8 +17,9 @@ export default router.post(
}),
async (req, res) => {
const { path: filePath } = req.body;
const userId = requireRequestUserId(req);
const modelPromptRoot = u.getPath(["modelPrompt"]);
const modelPromptRoot = getUserModelPromptRoot(userId);
// 路径隧穿检测
const resolvedRoot = path.resolve(modelPromptRoot);

View File

@ -1,19 +1,21 @@
import express from "express";
import u from "@/utils";
import { success } from "@/lib/responseFormat";
import { getEnabledVendorIdsForUser, getModelPromptBindingsForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post("/", async (req, res) => {
const dataList = await u.db("o_vendorConfig").select("id").where("enable", 1);
if (!dataList || dataList.length === 0) {
const userId = requireRequestUserId(req);
const vendorIds = await getEnabledVendorIdsForUser(userId);
if (!vendorIds.length) {
return res.status(404).send({ error: "模型未找到" });
}
const data = await Promise.all(
dataList.map(async (item) => {
const vendor = u.vendor.getVendor(item.id!);
const promptList = await u.db("o_modelPrompt").andWhere("vendorId", vendor.id).select("*");
vendorIds.map(async (vendorId) => {
const vendor = u.vendor.getVendor(vendorId);
const promptList = await getModelPromptBindingsForUser(vendor.id, userId);
const promptMap = new Map(promptList.map((p) => [p.model, { fileName: p.fileName, path: p.path }]));
const models = await u.vendor.getModelList(item.id!);
const models = await u.vendor.getModelList(vendorId);
const filteredModels = models
.filter((m: any) => m.type === "video")
.map((m: any) => ({
@ -23,7 +25,7 @@ export default router.post("/", async (req, res) => {
...(promptMap.get(m.modelName) ? { ...promptMap.get(m.modelName) } : {}),
}));
return {
id: item.id,
id: vendorId,
name: vendor.name,
promptList: filteredModels,
};

View File

@ -4,10 +4,14 @@ import u from "@/utils";
import fg from "fast-glob";
import fs from "fs/promises";
import path from "path";
import { requireRequestUserId } from "@/lib/userConfig";
import { getUserModelPromptRoot } from "@/lib/userStorage";
const router = express.Router();
export default router.get("/", async (req, res) => {
const modelPromptRoot = u.getPath(["modelPrompt"]);
const userId = requireRequestUserId(req);
const modelPromptRoot = getUserModelPromptRoot(userId);
await fs.mkdir(modelPromptRoot, { recursive: true });
const entries = await fg("**/*.md", {
cwd: modelPromptRoot.replace(/\\/g, "/"),

View File

@ -5,6 +5,8 @@ import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import fs from "fs/promises";
import path from "path";
import { requireRequestUserId } from "@/lib/userConfig";
import { getUserModelPromptRoot } from "@/lib/userStorage";
const router = express.Router();
@ -17,8 +19,9 @@ export default router.post(
}),
async (req, res) => {
const { name, data, type } = req.body;
const userId = requireRequestUserId(req);
const modelPromptRoot = u.getPath(["modelPrompt"]);
const modelPromptRoot = getUserModelPromptRoot(userId);
const dir = path.join(modelPromptRoot, type);
await fs.mkdir(dir, { recursive: true });

View File

@ -5,6 +5,8 @@ import { z } from "zod";
import { validateFields } from "@/middleware/middleware";
import fs from "fs/promises";
import path from "path";
import { requireRequestUserId } from "@/lib/userConfig";
import { getUserModelPromptRoot } from "@/lib/userStorage";
const router = express.Router();
@ -17,8 +19,9 @@ export default router.post(
}),
async (req, res) => {
const { name, data, type } = req.body;
const userId = requireRequestUserId(req);
const modelPromptRoot = u.getPath(["modelPrompt"]);
const modelPromptRoot = getUserModelPromptRoot(userId);
const filePath = path.join(modelPromptRoot, type, `${name}.md`);
// 路径隧穿检测

View File

@ -1,16 +1,22 @@
import express from "express";
import u from "@/utils";
import { success, error } from "@/lib/responseFormat";
import { requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post("/", async (req, res) => {
const userId = requireRequestUserId(req);
const list = await u.db("o_prompt").select("*");
const userPrompts = await u.db("o_userPrompt").where({ userId }).select("*");
const userPromptMap = new Map(userPrompts.map((item) => [item.promptId, item.useData]));
const data = await Promise.all(
list.map(async (item) => {
const userData = item.id ? userPromptMap.get(item.id) : undefined;
return {
...item,
data: item.useData ? item.useData : item.data,
useData: userData ?? null,
data: userData ?? item.data,
};
}),
);

View File

@ -3,6 +3,7 @@ import u from "@/utils";
import { z } from "zod";
import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import { requireRequestUserId, setUserPrompt } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -12,9 +13,8 @@ export default router.post(
}),
async (req, res) => {
const { id, data } = req.body;
await u.db("o_prompt").where("id", id).update({
useData: data,
});
const userId = requireRequestUserId(req);
await setUserPrompt(userId, id, data);
res.status(200).send(success(123));
},
);

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { getEditableVendorModels, requireRequestUserId, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -44,18 +45,13 @@ export default router.post(
}),
async (req, res) => {
const { id, model } = req.body;
const userId = requireRequestUserId(req);
const models = await u.db("o_vendorConfig").where("id", id).first("models");
if (models?.models) {
const existingModels = JSON.parse(models.models);
existingModels.push(model);
await u
.db("o_vendorConfig")
.where("id", id)
.update({
models: JSON.stringify(existingModels),
});
}
const existingModels = await getEditableVendorModels(userId, id);
existingModels.push(model);
await setUserVendorConfig(userId, id, {
models: JSON.stringify(existingModels),
});
res.status(200).send(success("更新成功"));
},
);

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { getEditableVendorModels, requireRequestUserId, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -13,21 +14,16 @@ export default router.post(
}),
async (req, res) => {
const { id, modelName } = req.body;
const userId = requireRequestUserId(req);
const models = await u.db("o_vendorConfig").where("id", id).first("models");
if (models?.models) {
const existingModels = JSON.parse(models.models);
if (!existingModels.some((model: any) => model.modelName === modelName)) {
return res.status(400).send(error("基本模型不允许删除"));
}
const updatedModels = existingModels.filter((model: any) => model.modelName !== modelName);
await u
.db("o_vendorConfig")
.where("id", id)
.update({
models: JSON.stringify(updatedModels),
});
const existingModels = await getEditableVendorModels(userId, id);
if (!existingModels.some((model: any) => model.modelName === modelName)) {
return res.status(400).send(error("基本模型不允许删除"));
}
const updatedModels = existingModels.filter((model: any) => model.modelName !== modelName);
await setUserVendorConfig(userId, id, {
models: JSON.stringify(updatedModels),
});
res.status(200).send(success("更新成功"));
},
);

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { requireRequestUserId, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
"/",
@ -12,7 +13,8 @@ export default router.post(
}),
async (req, res) => {
const { id, enable } = req.body;
await u.db("o_vendorConfig").where("id", id).update({ enable });
const userId = requireRequestUserId(req);
await setUserVendorConfig(userId, id, { enable });
res.status(200).send(success("更新成功"));
},
);

View File

@ -1,22 +1,28 @@
import express from "express";
import { success } from "@/lib/responseFormat";
import u from "@/utils";
import { getVendorConfigForUser, parseJson, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
export default router.post("/", async (req, res) => {
const userId = requireRequestUserId(req);
const data = await u.db("o_vendorConfig").select("*");
const list = (
await Promise.all(
data.map(async (item) => {
const userConfig = await getVendorConfigForUser(item.id!, userId);
const vendor = u.vendor.getVendor(item.id!);
if (!vendor) {
await u.db("o_vendorConfig").where("id", item.id).delete();
return null
};
return {
...item,
inputValues: JSON.parse(item.inputValues ?? "{}"),
...userConfig,
inputValues: {
...(vendor.inputValues ?? {}),
...parseJson(userConfig?.inputValues, {}),
},
models: await u.vendor.getModelList(item.id!),
code: u.vendor.getCode(item.id!),
description: vendor.description ?? "",

View File

@ -4,6 +4,7 @@ import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { tool, jsonSchema } from "ai";
import { getVendorConfigForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
// 检查语言模型
@ -16,6 +17,7 @@ export default router.post(
}),
async (req, res) => {
const { modelName, type, id } = req.body;
const userId = requireRequestUserId(req);
try {
const requestFn: Record<string, { fnName: string; modelData?: any }> = {
@ -32,7 +34,7 @@ export default router.post(
},
video: { fnName: "videoRequest", modelData: {} },
} as const;
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
const vendorConfigData = await getVendorConfigForUser(id, userId);
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.models) return res.status(500).send(error("未找到模型列表"));

View File

@ -4,6 +4,7 @@ import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { tool, jsonSchema } from "ai";
import { getVendorConfigForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
// 检查语言模型
@ -17,9 +18,10 @@ export default router.post(
}),
async (req, res) => {
const { modelName, imageBase64, id, prompt } = req.body;
const userId = requireRequestUserId(req);
try {
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
const vendorConfigData = await getVendorConfigForUser(id, userId);
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.models) return res.status(500).send(error("未找到模型列表"));

View File

@ -4,6 +4,7 @@ import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { tool, jsonSchema } from "ai";
import { getVendorConfigForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
// 检查语言模型
@ -21,9 +22,10 @@ export default router.post(
}),
async (req, res) => {
const { modelName, messages, id } = req.body;
const userId = requireRequestUserId(req);
try {
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
const vendorConfigData = await getVendorConfigForUser(id, userId);
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.models) return res.status(500).send(error("未找到模型列表"));

View File

@ -4,6 +4,7 @@ import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { tool, jsonSchema } from "ai";
import { getVendorConfigForUser, requireRequestUserId } from "@/lib/userConfig";
const router = express.Router();
// 检查语言模型
@ -35,9 +36,10 @@ export default router.post(
}),
async (req, res) => {
const { modelName, id, mode, prompt, images, videos, audios } = req.body;
const userId = requireRequestUserId(req);
try {
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
const vendorConfigData = await getVendorConfigForUser(id, userId);
if (!vendorConfigData) return res.status(500).send(error("未找到该供应商配置"));
if (!vendorConfigData.models) return res.status(500).send(error("未找到模型列表"));

View File

@ -3,6 +3,7 @@ import { success, error } from "@/lib/responseFormat";
import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { getEditableVendorModels, requireRequestUserId, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -45,22 +46,18 @@ export default router.post(
}),
async (req, res) => {
const { id, modelName, model } = req.body;
const userId = requireRequestUserId(req);
const models = await u.db("o_vendorConfig").where("id", id).first("models");
if (models?.models) {
const existingModels = JSON.parse(models.models);
const modelIndex = existingModels.findIndex((m: any) => m.modelName !== modelName);
if (modelIndex === -1) {
existingModels.push(model);
}
const existingModels = await getEditableVendorModels(userId, id);
const modelIndex = existingModels.findIndex((m: any) => m.modelName === modelName);
if (modelIndex === -1) {
existingModels.push(model);
} else {
existingModels[modelIndex] = model;
await u
.db("o_vendorConfig")
.where("id", id)
.update({
models: JSON.stringify(existingModels),
});
}
await setUserVendorConfig(userId, id, {
models: JSON.stringify(existingModels),
});
res.status(200).send(success("更新成功"));
},
);

View File

@ -4,6 +4,7 @@ import { validateFields } from "@/middleware/middleware";
import u from "@/utils";
import { z } from "zod";
import { transform } from "sucrase";
import { requireRequestUserId, setUserVendorConfig } from "@/lib/userConfig";
const router = express.Router();
export default router.post(
@ -14,13 +15,11 @@ export default router.post(
}),
async (req, res) => {
const { id, inputValues } = req.body;
const userId = requireRequestUserId(req);
await u
.db("o_vendorConfig")
.where("id", id)
.update({
inputValues: JSON.stringify(inputValues),
});
await setUserVendorConfig(userId, id, {
inputValues: JSON.stringify(inputValues),
});
res.status(200).send(success("更新成功"));
},
);

View File

@ -3,6 +3,7 @@ import { Namespace, Socket } from "socket.io";
import * as agent from "@/agents/productionAgent/index";
import ResTool from "@/socket/resTool";
import { assertProjectAccess, verifyAuthToken } from "@/lib/auth";
import { runWithRequestContext } from "@/lib/requestContext";
export default (nsp: Namespace) => {
nsp.on("connection", async (socket: Socket) => {
@ -67,7 +68,7 @@ export default (nsp: Namespace) => {
};
try {
await agent.runDecisionAI(ctx);
await runWithRequestContext({ userId: authUser.id }, () => agent.runDecisionAI(ctx));
} catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) {
console.error("[productionAgent] chat error:", u.error(err).message);

View File

@ -3,6 +3,7 @@ import { Namespace, Socket } from "socket.io";
import * as agent from "@/agents/scriptAgent/index";
import ResTool from "@/socket/resTool";
import { assertProjectAccess, verifyAuthToken } from "@/lib/auth";
import { runWithRequestContext } from "@/lib/requestContext";
export default (nsp: Namespace) => {
nsp.on("connection", async (socket: Socket) => {
@ -52,7 +53,7 @@ export default (nsp: Namespace) => {
};
try {
await agent.runDecisionAI(ctx);
await runWithRequestContext({ userId: authUser.id }, () => agent.runDecisionAI(ctx));
} catch (err: any) {
if (err.name !== "AbortError" && !currentController.signal.aborted) {
console.error("[scriptAgent] chat error:", u.error(err).message);

View File

@ -151,6 +151,40 @@ export interface o_smsCode {
'sentAt': number;
'used'?: number | null;
}
export interface o_userAgentDeploy {
'disabled'?: boolean | null;
'key': string;
'maxOutputTokens'?: number | null;
'model'?: string | null;
'modelName'?: string | null;
'temperature'?: number | null;
'userId': number;
'vendorId'?: string | null;
}
export interface o_userModelPrompt {
'fileName'?: string | null;
'model': string;
'path'?: string | null;
'userId': number;
'vendorId': string;
}
export interface o_userPrompt {
'promptId': number;
'useData'?: string | null;
'userId': number;
}
export interface o_userSetting {
'key': string;
'userId': number;
'value'?: string | null;
}
export interface o_userVendorConfig {
'enable'?: number | null;
'inputValues'?: string | null;
'models'?: string | null;
'userId': number;
'vendorId': string;
}
export interface o_prompt {
'data'?: string | null;
'id'?: number;
@ -280,6 +314,11 @@ export interface DB {
"o_storyboard": o_storyboard;
"o_tasks": o_tasks;
"o_user": o_user;
"o_userAgentDeploy": o_userAgentDeploy;
"o_userModelPrompt": o_userModelPrompt;
"o_userPrompt": o_userPrompt;
"o_userSetting": o_userSetting;
"o_userVendorConfig": o_userVendorConfig;
"o_vendorConfig": o_vendorConfig;
"o_video": o_video;
"o_videoTrack": o_videoTrack;

View File

@ -4,6 +4,7 @@ import { getEmbedding, cosineSimilarity } from "./embedding";
import type { memories as MemoryRow } from "@/types/database";
import { tool, jsonSchema } from "ai";
import { z } from "zod";
import { getUserSettingValue } from "@/lib/userConfig";
// ── 可调配置默认值 ──
const DEFAULTS: {
@ -66,16 +67,10 @@ class Memory {
}
private async getConfigData<T extends Record<string, string | number>>(defaults: T): Promise<T> {
const keys = Object.keys(defaults) as (keyof T & string)[];
const rows = await u.db("o_setting").whereIn("key", keys);
const dbMap: Record<string, string | null> = {};
for (const row of rows) {
if (row.key != null) dbMap[row.key] = row.value ?? null;
}
const result = { ...defaults };
for (const key of keys) {
const raw = dbMap[key];
const raw = await getUserSettingValue(key);
if (raw == null) continue; // null / undefined 使用默认值
const num = Number(raw);
(result as Record<string, string | number>)[key] = Number.isNaN(num) ? raw : num;

View File

@ -3,6 +3,7 @@ import { devToolsMiddleware } from "@ai-sdk/devtools";
import axios from "axios";
import { transform } from "sucrase";
import u from "@/utils";
import { getAgentDeployForUser, getUserSettingValue, getVendorConfigForUser, parseJson } from "@/lib/userConfig";
type AiType =
| "scriptAgent"
@ -45,30 +46,30 @@ const AiTypeValues: AiType[] = [
];
async function resolveModelName(value: AiType | `${string}:${string}`): Promise<`${string}:${string}`> {
if (AiTypeValues.includes(value as AiType)) {
const agentUseModeVal = await u.db("o_setting").where("key", "agentUseMode").first();
const agentUseMode = await getUserSettingValue("agentUseMode");
//正常流程
//高级配置
if (agentUseModeVal?.value == "1") {
const agentDeployData = await u.db("o_agentDeploy").where("key", value).first();
if (agentUseMode == "1") {
const agentDeployData = await getAgentDeployForUser(value);
if (!agentDeployData?.modelName) throw new Error(`高级配置模式下,未找到对应的模型配置 ${value}`);
return agentDeployData?.modelName as `${number}:${string}`;
}
//简易配置
if (agentUseModeVal?.value == "0") {
if (agentUseMode == "0") {
const [mainly] = value!.split(/:(.+)/);
const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first();
const mainlyData = await getAgentDeployForUser(mainly);
if (!mainlyData?.modelName) throw new Error(`简易配置模式下,未找到部署配置 ${value}`);
return mainlyData?.modelName as `${number}:${string}`;
}
//未查到agentUseModeVal 维持原判断
const agentDeployData = await u.db("o_agentDeploy").where("key", value).first();
const agentDeployData = await getAgentDeployForUser(value);
let modelName = null;
if (!agentDeployData?.modelName) {
const [mainly] = agentDeployData!.key!.split(/:(.+)/);
const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first();
const mainlyData = await getAgentDeployForUser(mainly);
if (!mainlyData?.modelName) throw new Error(`未找到部署配置 ${value}`);
modelName = mainlyData.modelName;
}
@ -80,28 +81,28 @@ async function resolveModelName(value: AiType | `${string}:${string}`): Promise<
async function getModelConfig(value: AiType | `${string}:${string}`) {
if (AiTypeValues.includes(value as AiType)) {
const agentUseModeVal = await u.db("o_setting").where("key", "agentUseMode").first();
const agentUseMode = await getUserSettingValue("agentUseMode");
//正常流程
//高级配置
if (agentUseModeVal?.value == "1") {
const agentDeployData = await u.db("o_agentDeploy").where("key", value).first();
if (agentUseMode == "1") {
const agentDeployData = await getAgentDeployForUser(value);
if (!agentDeployData?.modelName) throw new Error(`高级配置模式下,未找到对应的模型配置 ${value}`);
return agentDeployData;
}
//简易配置
if (agentUseModeVal?.value == "0") {
if (agentUseMode == "0") {
const [mainly] = value!.split(/:(.+)/);
const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first();
const mainlyData = await getAgentDeployForUser(mainly);
if (!mainlyData?.modelName) throw new Error(`简易配置模式下,未找到部署配置 ${value}`);
return mainlyData;
}
//未查到 agentUseModelVal 维持原流程
const agentDeployData = await u.db("o_agentDeploy").where("key", value).first();
const agentDeployData = await getAgentDeployForUser(value);
if (!agentDeployData?.modelName) {
const [mainly] = agentDeployData!.key!.split(/:(.+)/);
const mainlyData = await u.db("o_agentDeploy").where("key", mainly).first();
const mainlyData = await getAgentDeployForUser(mainly);
if (!mainlyData?.modelName) throw new Error(`未找到部署配置 ${value}`);
return mainlyData;
}
@ -117,7 +118,7 @@ async function getVendorTemplateFn(
async function getVendorTemplateFn(fnName: Exclude<FnName, "textRequest">, modelName: `${string}:${string}`): Promise<(input: any) => any>;
async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${string}`): Promise<any> {
const [id, name] = modelName.split(/:(.+)/);
const vendorConfigData = await u.db("o_vendorConfig").where("id", id).first();
const vendorConfigData = await getVendorConfigForUser(id);
if (!vendorConfigData) throw new Error(`未找到供应商配置 id=${id}`);
const modelList = await u.vendor.getModelList(id);
const selectedModel = modelList.find((i: any) => i.modelName == name);
@ -126,7 +127,7 @@ async function getVendorTemplateFn(fnName: FnName, modelName: `${string}:${strin
const jsCode = transform(code, { transforms: ["typescript"] }).code;
const running = u.vm(jsCode);
if (running.vendor) {
Object.assign(running.vendor.inputValues, JSON.parse(vendorConfigData.inputValues ?? "{}"));
Object.assign(running.vendor.inputValues, parseJson(vendorConfigData.inputValues, {}));
running.vendor.models = modelList;
}
const fn = running[fnName];
@ -184,12 +185,12 @@ class AiText {
this.thinkLevel = thinkLevel;
}
private async resolveModel(middleware?: any | any[]) {
const switchAiDevTool = await u.db("o_setting").where("key", "switchAiDevTool").first();
const switchAiDevTool = await getUserSettingValue("switchAiDevTool");
const modelName = await resolveModelName(this.AiType);
const sdkFn = await getVendorTemplateFn("textRequest", modelName);
const baseModel = await sdkFn(this.think, this.thinkLevel);
const mws = [
...(switchAiDevTool?.value === "1" ? [devToolsMiddleware()] : []),
...(switchAiDevTool === "1" ? [devToolsMiddleware()] : []),
...(middleware ? (Array.isArray(middleware) ? middleware : [middleware]) : []),
];
return mws.length > 0 ? wrapLanguageModel({ model: baseModel, middleware: mws.length === 1 ? mws[0] : mws }) : baseModel;

View File

@ -2,6 +2,7 @@ import { EventEmitter } from "events";
import { o_novel } from "@/types/database";
import u from "@/utils";
import { stripThink } from "@/utils/stripThink";
import { getPromptForUser } from "@/lib/userConfig";
export interface EventType {
id: number;
event: string;
@ -27,13 +28,8 @@ class CleanNovel {
private async processChapter(novel: o_novel): Promise<EventType | null> {
try {
const prompt = await u.getPrompts("event");
const promptData = await u.db("o_prompt").where("type", "eventExtraction").first();
let eventExtraction = "" as string | undefined;
if (promptData && promptData.useData) {
eventExtraction = promptData.useData;
} else {
eventExtraction = promptData?.data ?? undefined;
}
const promptData = await getPromptForUser("eventExtraction");
const eventExtraction = promptData?.data ?? undefined;
const resData = await u.Ai.Text("universalAi").invoke({
system: eventExtraction ? JSON.stringify(eventExtraction) : (prompt as string),
messages: [

View File

@ -2,6 +2,7 @@ import { transform } from "sucrase";
import fs from "fs";
import path from "path";
import u from "@/utils";
import { getVendorConfigForUser, parseJson } from "@/lib/userConfig";
export function writeCode(id: string | number, tsCode: string) {
const rootDir = u.getPath("vendor")
@ -20,13 +21,13 @@ export function getCode(id: string): string {
}
export async function getModelList(id: string): Promise<Array<any>> {
const models = await u.db("o_vendorConfig").where("id", id).select("models").first();
if (!models || !models.models) return [];
const config = await getVendorConfigForUser(id);
if (!config) return [];
const code = getCode(id);
const jsCode = transform(code, { transforms: ["typescript"] }).code;
const vendorData = u.vm(jsCode);
if(!vendorData || !vendorData.vendor || !vendorData.vendor.models) return [];
const combined = [...JSON.parse(JSON.stringify(vendorData.vendor.models)), ...JSON.parse(models?.models ?? "[]")];
const combined = [...JSON.parse(JSON.stringify(vendorData.vendor.models)), ...parseJson<any[]>(config.models, [])];
const map = new Map<string, any>();
for (const m of combined) {
map.set(m.modelName, m);

View File

@ -1539,7 +1539,6 @@
},
"login": {
"slogan": "Intelligent Short Drama Creation Platform",
"tips": "Default Account: admin / admin123",
"settings": "Server Settings",
"requestAddress": "Request URL",
"username": "Username",

View File

@ -1386,7 +1386,6 @@
},
"login": {
"slogan": "ショードラ制作支援ツール",
"tips": "デフォルトアカウントadmin / admin123",
"settings": "サーバー設定",
"requestAddress": "リクエストアドレス",
"username": "ユーザー名",

View File

@ -1386,7 +1386,6 @@
},
"login": {
"slogan": "Умная платформа для создания коротких драм",
"tips": "Аккаунт по умолчанию: admin / admin123",
"settings": "Настройки сервера",
"requestAddress": "URL запроса",
"username": "Имя пользователя",

View File

@ -1387,7 +1387,6 @@
},
"login": {
"slogan": "แพลตฟอร์มสร้างละครสั้นอัจฉริยะ",
"tips": "บัญชีผู้ใช้เริ่มต้น: admin / admin123",
"settings": "การตั้งค่าเซิร์ฟเวอร์",
"requestAddress": "ที่อยู่คำขอ (Request Address)",
"username": "ชื่อผู้ใช้",

View File

@ -1377,7 +1377,6 @@
},
"login": {
"slogan": "Nền tảng sáng tạo phim ngắn thông minh",
"tips": "Tài khoản mặc định: admin / admin123",
"settings": "Cài đặt máy chủ",
"requestAddress": "Địa chỉ yêu cầu",
"username": "Tên người dùng",

View File

@ -1643,7 +1643,6 @@
},
"login": {
"slogan": "智能短剧创作平台",
"tips": "默认账号admin / admin123",
"settings": "服务器设置",
"requestAddress": "请求地址",
"username": "用户名",

View File

@ -1391,7 +1391,6 @@
},
"login": {
"slogan": "智慧短劇創作平台",
"tips": "預設帳號admin / admin123",
"settings": "伺服器設定",
"requestAddress": "請求網址",
"username": "使用者名稱",

View File

@ -147,12 +147,6 @@
</t-button>
</form>
<div v-if="!needsSmsCode" class="tips">
<span>默认账号</span>
<strong>admin</strong>
<span>/</span>
<strong>admin123</strong>
</div>
</section>
</section>
</main>
@ -953,30 +947,6 @@ onBeforeUnmount(() => {
inset 0 1px 0 rgba(255, 255, 255, 0.34);
}
.tips {
margin: auto 0 0;
padding-top: 32px;
display: flex;
align-items: center;
gap: 8px;
color: rgba(185, 188, 210, 0.68);
font-size: 13px;
font-weight: 560;
line-height: 1.45;
text-align: left;
strong {
padding: 3px 7px;
border: 1px solid rgba(185, 188, 210, 0.14);
border-radius: 6px;
color: var(--air-text-primary);
background: rgba(255, 255, 255, 0.045);
font-family: var(--air-font-number);
font-size: 12px;
font-weight: 750;
}
}
.settingBtn {
position: fixed;
right: 28px;
@ -1098,9 +1068,5 @@ onBeforeUnmount(() => {
grid-template-columns: 1fr;
}
.tips {
flex-wrap: wrap;
padding-top: 24px;
}
}
</style>