docs & cleanup: update stack info, remove dead code, fix backend bugs

- Fix AGENTS.md and 技术架构设计.md to reflect actual stack (Prisma+MySQL, static export, PM2)
- Remove 4 unused frontend components (page-transition, page-layout, image-upload, section-card)
- Fix card.tsx hardcoded colors → CSS variables
- Remove provider name from model selector, remove sensenova-u1-fast from models
- Fix analytics.controller.ts raw SQL table names (runtime bug)
- Add JWT auth guard to tools POST endpoint
- Fix AI gateway test: update env vars and model names
- Fix admin test: mock role structure for Prisma relation
This commit is contained in:
yuzhiran-dev
2026-05-29 10:26:50 +08:00
parent 417fb266d4
commit 6f3fe50ee0
15 changed files with 118 additions and 304 deletions
@@ -138,7 +138,7 @@ export class AnalyticsController {
if (type === 'users') {
const data = await this.prisma.$queryRaw<{ date: string; count: bigint }[]>`
SELECT DATE(created_at) as date, COUNT(*) as count
FROM User
FROM users
WHERE deleted_at IS NULL AND created_at >= ${startDate}
GROUP BY DATE(created_at)
ORDER BY date
@@ -149,7 +149,7 @@ export class AnalyticsController {
if (type === 'orders') {
const data = await this.prisma.$queryRaw<{ date: string; count: bigint; revenue: bigint }[]>`
SELECT DATE(created_at) as date, COUNT(*) as count, COALESCE(SUM(amount), 0) as revenue
FROM \`Order\`
FROM orders
WHERE status = 'PAID' AND created_at >= ${startDate}
GROUP BY DATE(created_at)
ORDER BY date
@@ -160,7 +160,7 @@ export class AnalyticsController {
if (type === 'sessions') {
const data = await this.prisma.$queryRaw<{ date: string; count: bigint }[]>`
SELECT DATE(created_at) as date, COUNT(*) as count
FROM sandbox_session
FROM sandbox_sessions
WHERE created_at >= ${startDate}
GROUP BY DATE(created_at)
ORDER BY date
@@ -51,7 +51,7 @@ describe('AdminService', () => {
id: 1,
username: 'admin',
passwordHash: '$2a$10$hashed',
role: 'superadmin',
role: { name: 'superadmin' },
status: 'ACTIVE',
};
+4 -4
View File
@@ -31,7 +31,7 @@ const MODEL_CATALOG: Record<string, ModelInfo> = {
'gpt-4': { id: 'gpt-4', provider: 'OpenAI', capabilities: ['chat', 'code', 'vision'], contextWindow: 32768 },
'deepseek-v4-flash': { id: 'deepseek-v4-flash', provider: '商汤科技', capabilities: ['chat', 'code'], contextWindow: 32768 },
'sensenova-6.7-flash-lite': { id: 'sensenova-6.7-flash-lite', provider: '商汤科技', capabilities: ['chat', 'code'], contextWindow: 32768 },
'sensenova-u1-fast': { id: 'sensenova-u1-fast', provider: '商汤科技', capabilities: ['chat', 'code', 'vision'], contextWindow: 65536 },
}
@Injectable()
@@ -65,7 +65,7 @@ export class AIGatewayService {
if (!apiUrl.endsWith('/chat/completions')) {
apiUrl = apiUrl.replace(/\/+$/, '') + '/chat/completions';
}
const models = ['deepseek-v4-flash', 'sensenova-6.7-flash-lite', 'sensenova-u1-fast'];
const models = ['deepseek-v4-flash', 'sensenova-6.7-flash-lite'];
for (const modelName of models) {
this.providers.set(modelName, new OpenAICompatibleProvider(
process.env.SENSENOVA_API_KEY!,
@@ -88,7 +88,7 @@ export class AIGatewayService {
'meituan/longcat-flash-lite': 'openai',
'deepseek-v4-flash': 'deepseek-v4-flash',
'sensenova-6.7-flash-lite': 'sensenova-6.7-flash-lite',
'sensenova-u1-fast': 'sensenova-u1-fast',
};
const providerKey = modelMap[model.toLowerCase()] || (model.includes('/') ? 'openai' : model);
@@ -125,7 +125,7 @@ export class AIGatewayService {
'meituan/longcat-flash-lite': 'openai',
'deepseek-v4-flash': 'deepseek-v4-flash',
'sensenova-6.7-flash-lite': 'sensenova-6.7-flash-lite',
'sensenova-u1-fast': 'sensenova-u1-fast',
};
const providerKey = modelMap[model.toLowerCase()] || (model.includes('/') ? 'openai' : model);
@@ -6,8 +6,8 @@ describe('AIGatewayService', () => {
beforeEach(async () => {
// Clear env before each test
delete process.env.DEEPSEEK_API_KEY;
delete process.env.DASHSCOPE_API_KEY;
delete process.env.OPENAI_API_KEY;
delete process.env.SENSENOVA_API_KEY;
const module: TestingModule = await Test.createTestingModule({
providers: [AIGatewayService],
@@ -70,8 +70,8 @@ describe('AIGatewayService', () => {
});
describe('model routing', () => {
it('should route deepseek models to deepseek provider (but fallback to mock)', async () => {
const result = await service.chat('deepseek-chat', [
it('should route deepseek-v4-flash to its provider (but fallback to mock)', async () => {
const result = await service.chat('deepseek-v4-flash', [
{ role: 'user', content: '你好' },
]);
@@ -79,8 +79,8 @@ describe('AIGatewayService', () => {
expect(result).toContain('宇之然 AI 助手');
});
it('should route qwen models to dashscope provider (but fallback to mock)', async () => {
const result = await service.chat('qwen-max', [
it('should route general model to openai provider (but fallback to mock)', async () => {
const result = await service.chat('general', [
{ role: 'user', content: '你好' },
]);
@@ -1,5 +1,6 @@
import { Controller, Get, Post, Body, Query } from '@nestjs/common';
import { ApiTags } from '@nestjs/swagger';
import { Controller, Get, Post, Body, Query, UseGuards } from '@nestjs/common';
import { ApiTags, ApiBearerAuth } from '@nestjs/swagger';
import { AuthGuard } from '@nestjs/passport';
import { ToolsService } from './tools.service';
@ApiTags('AI工具')
@@ -13,6 +14,8 @@ export class ToolsController {
}
@Post()
@UseGuards(AuthGuard('jwt'))
@ApiBearerAuth()
async create(@Body() body: { name: string; description?: string; url: string; icon?: string; categoryId?: number; tags?: string }) {
return this.toolsService.create(body);
}