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:
@@ -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',
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user