P8 平台轻量化改造 + SSG修复 + 编程导师 + 文档完善

- Prisma: Tool 模型加 affiliateLink;免费用户沙盒 10→5 次/日
- 后端: Tools API + /admin/tools CRUD 5 端点;Practices 完整模块
- 导航: 主菜单隐藏企业版/社区(URL 可访问)
- 首页: 重定位为 AI 工具指南;新增精选工具区块;Feature 重写
- 工具页: affiliateLink 绿色推荐 Badge
- SSG 修复: config.ts 构建时直连 localhost:4000,页面 108→127
- 沙盒: 新增编程导师场景(苏格拉底教学法)
- 练习系统: Practices 多场景练习(含结构化评分)
- 技能广场: 6 个付费 Skill(标题大师/回款助手等)
- 管理后台: Models/Posts/Practices CRUD 页面
- 文档: README + progress.md 全面更新;AGENTS.md 同步定位
- 清理: .env.example 移除;tsbuildinfo gitignore
This commit is contained in:
yuzhiran-dev
2026-06-18 18:14:07 +08:00
parent fb8152401b
commit 0f215d2aad
103 changed files with 5240 additions and 778 deletions
@@ -0,0 +1,35 @@
import { ThrottlerStorage } from '@nestjs/throttler';
import Redis from 'ioredis';
export class RedisThrottlerStorage implements ThrottlerStorage {
private redis: Redis;
constructor() {
this.redis = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379', 10),
password: process.env.REDIS_PASSWORD || undefined,
keyPrefix: 'throttle:',
});
}
async increment(key: string, ttl: number, limit: number, blockDuration: number, throttlerName: string): Promise<{
totalHits: number;
timeToExpire: number;
isBlocked: boolean;
timeToBlockExpire: number;
}> {
const redisKey = `throttle:${throttlerName}:${key}`;
const total = await this.redis.incr(redisKey);
if (total === 1) {
await this.redis.pexpire(redisKey, ttl);
}
const ttlRemaining = await this.redis.pttl(redisKey);
return {
totalHits: total,
timeToExpire: Math.max(0, Math.ceil(ttlRemaining / 1000)),
isBlocked: total > limit,
timeToBlockExpire: total > limit ? Math.max(0, Math.ceil(ttlRemaining / 1000)) : 0,
};
}
}