feat: enhance router views functionality and permissions

- Added new router views schema and types for better structure and type safety.
- Implemented CRUD operations for router views including listing, updating, retrieving, and deleting views.
- Introduced permission checks to ensure users can only access and modify their own views.
- Updated prompts route to include additional permission checks for updating and retrieving prompts.
- Refactored common query tests to align with new configurations.
- Organized route imports for better maintainability.
This commit is contained in:
2025-12-31 17:55:13 +08:00
parent 8731801b52
commit c6715c2e35
9 changed files with 620 additions and 313 deletions

View File

@@ -13,4 +13,6 @@ import './file-listener/index.ts';
import './ai/index.ts';
import './prompts/index.ts'
import './prompts/index.ts'
import './views/index.ts';

View File

@@ -80,6 +80,9 @@ app.route({
if (existing.length === 0) {
ctx.throw(404, '没有找到对应的提示词');
}
if (existing[0].uid !== tokenUser.id) {
ctx.throw(403, '没有权限更新该提示词');
}
prompt = await db.update(schema.prompts).set({
...rest,
}).where(eq(schema.prompts.id, id)).returning();
@@ -108,4 +111,25 @@ app.route({
}
await db.delete(schema.prompts).where(eq(schema.prompts.id, id));
ctx.body = { success: true };
}).addTo(app);
app.route({
path: 'prompts',
key: 'get',
middleware: ['auth'],
description: '获取单个提示词, 参数: id 提示词ID',
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query.data || {};
if (!id) {
ctx.throw(400, 'id 参数缺失');
}
const existing = await db.select().from(schema.prompts).where(eq(schema.prompts.id, id)).limit(1);
if (existing.length === 0) {
ctx.throw(404, '没有找到对应的提示词');
}
if (existing[0].uid !== tokenUser.id) {
ctx.throw(403, '没有权限查看该提示词');
}
ctx.body = existing[0];
}).addTo(app);

View File

@@ -0,0 +1,37 @@
import { desc, eq, count, or, like, and, sql } from 'drizzle-orm';
import { schema, app, db } from '@/app.ts'
app.route({
path: 'views',
key: 'current',
middleware: ['auth'],
description: '获取包含指定viewId的视图'
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const uid = tokenUser.id;
const { viewId } = ctx.query.data || {};
if (!viewId) {
ctx.throw(400, 'viewId 参数缺失');
}
const view = await db.select()
.from(schema.routerViews)
.where(
and(
eq(schema.routerViews.uid, uid),
sql`EXISTS (
SELECT 1
FROM jsonb_array_elements(${schema.routerViews.views}) as item
WHERE item->>'id' = ${viewId}
)`
)
)
.orderBy(desc(schema.routerViews.updatedAt))
.limit(1);
if (view.length === 0) {
ctx.throw(404, '没有找到包含该viewId的视图');
}
ctx.body = view[0];
}).addTo(app);

View File

@@ -0,0 +1,2 @@
import './list.ts'
import './current.ts'

141
src/routes/views/list.ts Normal file
View File

@@ -0,0 +1,141 @@
import { desc, eq, count, or, like, and } from 'drizzle-orm';
import { schema, app, db } from '@/app.ts'
app.route({
path: 'views',
key: 'list',
middleware: ['auth'],
description: '获取视图列表',
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const uid = tokenUser.id;
const { page = 1, pageSize = 20, search, sort = 'DESC' } = ctx.query || {};
const offset = (page - 1) * pageSize;
const orderByField = sort === 'ASC' ? schema.routerViews.updatedAt : desc(schema.routerViews.updatedAt);
let whereCondition = eq(schema.routerViews.uid, uid);
if (search) {
whereCondition = and(
eq(schema.routerViews.uid, uid),
or(
like(schema.routerViews.title, `%${search}%`),
like(schema.routerViews.summary, `%${search}%`)
)
);
}
const [list, totalCount] = await Promise.all([
db.select()
.from(schema.routerViews)
.where(whereCondition)
.limit(pageSize)
.offset(offset)
.orderBy(orderByField),
db.select({ count: count() })
.from(schema.routerViews)
.where(whereCondition)
]);
ctx.body = {
list,
pagination: {
page,
current: page,
pageSize,
total: totalCount[0]?.count || 0,
},
};
return ctx;
}).addTo(app);
const viewUpdate = `创建或更新一个视图, 参数定义:
title: 视图标题, 必填
data: 数据, 对象, 选填
views: 视图查询数组, 选填
`;
app.route({
path: 'views',
key: 'update',
middleware: ['auth'],
description: viewUpdate,
}).define(async (ctx) => {
const { id, uid, updatedAt, ...rest } = ctx.query.data || {};
const tokenUser = ctx.state.tokenUser;
let view;
if (!id) {
view = await db.insert(schema.routerViews).values({
title: rest.title || '',
description: rest.description || '',
summary: rest.summary || '',
tags: rest.tags || [],
link: rest.link || '',
data: rest.data || { items: [] },
views: rest.views || [],
uid: tokenUser.id,
}).returning();
} else {
const existing = await db.select().from(schema.routerViews).where(eq(schema.routerViews.id, id)).limit(1);
if (existing.length === 0) {
ctx.throw(404, '没有找到对应的视图');
}
if (existing[0].uid !== tokenUser.id) {
ctx.throw(403, '没有权限更新该视图');
}
view = await db.update(schema.routerViews).set({
title: rest.title,
description: rest.description,
summary: rest.summary,
tags: rest.tags,
link: rest.link,
data: rest.data,
views: rest.views,
}).where(eq(schema.routerViews.id, id)).returning();
}
ctx.body = view;
}).addTo(app);
app.route({
path: 'views',
key: 'delete',
middleware: ['auth'],
description: '删除视图, 参数: id 视图ID',
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query.data || {};
if (!id) {
ctx.throw(400, 'id 参数缺失');
}
const existing = await db.select().from(schema.routerViews).where(eq(schema.routerViews.id, id)).limit(1);
if (existing.length === 0) {
ctx.throw(404, '没有找到对应的视图');
}
if (existing[0].uid !== tokenUser.id) {
ctx.throw(403, '没有权限删除该视图');
}
await db.delete(schema.routerViews).where(eq(schema.routerViews.id, id));
ctx.body = { success: true };
}).addTo(app);
app.route({
path: 'views',
key: 'get',
middleware: ['auth'],
description: '获取单个视图, 参数: id 视图ID',
}).define(async (ctx) => {
const tokenUser = ctx.state.tokenUser;
const { id } = ctx.query.data || {};
if (!id) {
ctx.throw(400, 'id 参数缺失');
}
const existing = await db.select().from(schema.routerViews).where(eq(schema.routerViews.id, id)).limit(1);
if (existing.length === 0) {
ctx.throw(404, '没有找到对应的视图');
}
if (existing[0].uid !== tokenUser.id) {
ctx.throw(403, '没有权限查看该视图');
}
ctx.body = existing[0];
}).addTo(app);