diff --git a/.continue/prompts/comment.prompt b/.continue/prompts/comment.prompt index cd79c77..f556eb8 100644 --- a/.continue/prompts/comment.prompt +++ b/.continue/prompts/comment.prompt @@ -9,35 +9,26 @@ maxTokens: 8192 注释目标: 1. 顶部注释 - 模块/文件整体功能描述 - - 版本历史 - 使用场景 - 2. 类注释 - - 类的职责和设计意图 - 核心功能概述 - 设计模式解析 - 使用示例 - 3. 方法/函数注释 - 功能详细描述 - 输入参数解析 - 返回值说明 - 异常处理机制 - - 算法复杂度 - - 时间/空间性能分析 - 4. 代码块注释 - 逐行解释代码意图 - 关键语句原理阐述 - 高级语言特性解读 - 潜在的设计考量 - 注释风格要求: - 全程使用中文 - 专业、清晰、通俗易懂 - 面向初学者的知识传递 - 保持技术严谨性 - 输出约束: - 仅返回添加注释后的代码 - 注释与代码完美融合 diff --git a/.continue/prompts/explain.prompt b/.continue/prompts/explain.prompt index e771c02..fcce931 100644 --- a/.continue/prompts/explain.prompt +++ b/.continue/prompts/explain.prompt @@ -13,8 +13,6 @@ maxTokens: 8192 - 代码意图解析 - 技术原理阐述 - 数据结构解读 - - 算法复杂度分析 - - 可能的优化建议 输出规范: - 全中文专业技术文档注释 @@ -26,5 +24,5 @@ maxTokens: 8192 禁止: - 不返回无关说明 - 不进行无意义的介绍 -- strictly遵循技术分析本身 +- 严格遵循技术分析本身 \ No newline at end of file diff --git a/.continue/prompts/sci-post.prompt b/.continue/prompts/sci-post.prompt new file mode 100644 index 0000000..336287d --- /dev/null +++ b/.continue/prompts/sci-post.prompt @@ -0,0 +1,45 @@ +temperature: 0.5 +maxTokens: 8192 +--- + +角色定位: +- 专业领域科普作家 +- 知识传播与教育专家 +- 多媒体内容策划师 +写作目标: +1. 开篇导读 +- 话题背景介绍 +- 阅读难度预期 +2. 核心概念解析 +- 专业术语通俗化 +- 基础原理清晰化 +- 生活案例类比 +- 历史发展脉络 +3. 深度知识传递 +- 科学原理剖析 +- 技术发展前沿 +- 争议观点评述 +- 实践应用场景 +4. 互动与延展 +- 趣味实验设计 +- 思考问题引导 +- 扩展阅读推荐 +- 知识图谱构建 +写作风格要求: +- 全程使用平实的中文 +- 深入浅出、生动有趣 +- 严谨专业、符合科学 +- 分层递进、逻辑清晰 +输出标准: +- 确保内容准确性 +- 保持叙事连贯性 +- 突出知识实用性 +- 强调趣味性与启发性 +质量控制: +- 引用权威来源 +- 多角度交叉验证 +输出约束: +- 避免过度技术化表达 +- 规避未经验证的观点 +- 考虑不同年龄层次需求 + \ No newline at end of file diff --git a/apps/server/package.json b/apps/server/package.json index b706afa..84b8e99 100755 --- a/apps/server/package.json +++ b/apps/server/package.json @@ -29,7 +29,7 @@ "@nestjs/platform-socket.io": "^10.3.10", "@nestjs/schedule": "^4.1.0", "@nestjs/websockets": "^10.3.10", - "@nicestack/common": "workspace:*", + "@nice/common": "workspace:*", "@trpc/server": "11.0.0-rc.456", "@tus/file-store": "^1.5.1", "@tus/s3-store": "^1.6.2", diff --git a/apps/server/src/auth/auth.controller.ts b/apps/server/src/auth/auth.controller.ts index d82ab36..e35c9a5 100755 --- a/apps/server/src/auth/auth.controller.ts +++ b/apps/server/src/auth/auth.controller.ts @@ -1,6 +1,6 @@ import { Controller, Headers, Post, Body, UseGuards, Get, Req, HttpException, HttpStatus, BadRequestException, InternalServerErrorException, NotFoundException, UnauthorizedException, Logger } from '@nestjs/common'; import { AuthService } from './auth.service'; -import { AuthSchema, JwtPayload } from '@nicestack/common'; +import { AuthSchema, JwtPayload } from '@nice/common'; import { AuthGuard } from './auth.guard'; import { UserProfileService } from './utils'; import { z } from 'zod'; @@ -54,8 +54,10 @@ export class AuthController { throw error; } } + @UseGuards(AuthGuard) @Get('user-profile') async getUserProfile(@Req() request: Request) { + const payload: JwtPayload = (request as any).user; const { staff } = await UserProfileService.instance.getUserProfileById(payload.sub); return staff diff --git a/apps/server/src/auth/auth.guard.ts b/apps/server/src/auth/auth.guard.ts index ab4cac0..e9fda52 100644 --- a/apps/server/src/auth/auth.guard.ts +++ b/apps/server/src/auth/auth.guard.ts @@ -7,7 +7,7 @@ import { import { JwtService } from '@nestjs/jwt'; import { env } from '@server/env'; -import { JwtPayload } from '@nicestack/common'; +import { JwtPayload } from '@nice/common'; import { extractTokenFromHeader } from './utils'; @Injectable() @@ -16,7 +16,6 @@ export class AuthGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); const token = extractTokenFromHeader(request); - if (!token) { throw new UnauthorizedException(); } @@ -27,9 +26,6 @@ export class AuthGuard implements CanActivate { secret: env.JWT_SECRET } ); - - // 💡 We're assigning the payload to the request object here - // so that we can access it in our route handlers request['user'] = payload; } catch { throw new UnauthorizedException(); @@ -37,5 +33,5 @@ export class AuthGuard implements CanActivate { return true; } - + } \ No newline at end of file diff --git a/apps/server/src/auth/auth.service.ts b/apps/server/src/auth/auth.service.ts index e0b6c04..cd793ef 100755 --- a/apps/server/src/auth/auth.service.ts +++ b/apps/server/src/auth/auth.service.ts @@ -11,7 +11,7 @@ import { db, AuthSchema, JwtPayload, -} from '@nicestack/common'; +} from '@nice/common'; import * as argon2 from 'argon2'; import { JwtService } from '@nestjs/jwt'; import { redis } from '@server/utils/redis/redis.service'; diff --git a/apps/server/src/auth/utils.ts b/apps/server/src/auth/utils.ts index 1ebfb31..5e8cd9a 100644 --- a/apps/server/src/auth/utils.ts +++ b/apps/server/src/auth/utils.ts @@ -5,7 +5,7 @@ import { JwtPayload, RolePerms, ObjectType, -} from '@nicestack/common'; +} from '@nice/common'; import { JwtService } from '@nestjs/jwt'; import { env } from '@server/env'; import { redis } from '@server/utils/redis/redis.service'; @@ -22,8 +22,7 @@ interface TokenVerifyResult { error?: string; } export function extractTokenFromHeader(request: Request): string | undefined { - const [type, token] = extractTokenFromAuthorization(request.headers.authorization) - return type === 'Bearer' ? token : undefined; + return extractTokenFromAuthorization(request.headers.authorization) } export function extractTokenFromAuthorization(authorization: string): string | undefined { const [type, token] = authorization?.split(' ') ?? []; diff --git a/apps/server/src/models/app-config/app-config.router.ts b/apps/server/src/models/app-config/app-config.router.ts index 3adf36e..ece1b35 100644 --- a/apps/server/src/models/app-config/app-config.router.ts +++ b/apps/server/src/models/app-config/app-config.router.ts @@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { AppConfigService } from './app-config.service'; import { z, ZodType } from 'zod'; -import { Prisma } from '@nicestack/common'; +import { Prisma } from '@nice/common'; import { RealtimeServer } from '@server/socket/realtime/realtime.server'; const AppConfigUncheckedCreateInputSchema: ZodType = z.any() const AppConfigUpdateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/app-config/app-config.service.ts b/apps/server/src/models/app-config/app-config.service.ts index 7915667..733e620 100644 --- a/apps/server/src/models/app-config/app-config.service.ts +++ b/apps/server/src/models/app-config/app-config.service.ts @@ -3,7 +3,7 @@ import { db, ObjectType, Prisma, -} from '@nicestack/common'; +} from '@nice/common'; import { BaseService } from '../base/base.service'; diff --git a/apps/server/src/models/base/base.service.ts b/apps/server/src/models/base/base.service.ts index d6a1249..ffbcfc3 100644 --- a/apps/server/src/models/base/base.service.ts +++ b/apps/server/src/models/base/base.service.ts @@ -1,4 +1,4 @@ -import { db, Prisma, PrismaClient } from '@nicestack/common'; +import { db, Prisma, PrismaClient } from '@nice/common'; import { Operations, DelegateArgs, diff --git a/apps/server/src/models/base/base.tree.service.ts b/apps/server/src/models/base/base.tree.service.ts index 2a1cc7b..45ad185 100644 --- a/apps/server/src/models/base/base.tree.service.ts +++ b/apps/server/src/models/base/base.tree.service.ts @@ -1,4 +1,4 @@ -import { Prisma, PrismaClient } from '@nicestack/common'; +import { Prisma, PrismaClient } from '@nice/common'; import { BaseService } from "./base.service"; import { DataArgs, DelegateArgs, DelegateFuncs, DelegateReturnTypes, UpdateOrderArgs } from "./base.type"; diff --git a/apps/server/src/models/base/base.type.ts b/apps/server/src/models/base/base.type.ts index 5ac6b8a..878dffe 100644 --- a/apps/server/src/models/base/base.type.ts +++ b/apps/server/src/models/base/base.type.ts @@ -1,4 +1,4 @@ -import { db, Prisma, PrismaClient } from "@nicestack/common"; +import { db, Prisma, PrismaClient } from "@nice/common"; export type Operations = | 'aggregate' diff --git a/apps/server/src/models/base/row-cache.service.ts b/apps/server/src/models/base/row-cache.service.ts index 8430974..6150407 100644 --- a/apps/server/src/models/base/row-cache.service.ts +++ b/apps/server/src/models/base/row-cache.service.ts @@ -1,4 +1,4 @@ -import { UserProfile, RowModelRequest, RowRequestSchema } from "@nicestack/common"; +import { UserProfile, RowModelRequest, RowRequestSchema } from "@nice/common"; import { RowModelService } from "./row-model.service"; import { isFieldCondition, LogicalCondition, SQLBuilder } from "./sql-builder"; import EventBus from "@server/utils/event-bus"; diff --git a/apps/server/src/models/base/row-model.service.ts b/apps/server/src/models/base/row-model.service.ts index 6afd12a..406beab 100644 --- a/apps/server/src/models/base/row-model.service.ts +++ b/apps/server/src/models/base/row-model.service.ts @@ -1,5 +1,5 @@ import { Logger } from "@nestjs/common"; -import { UserProfile, db, RowModelRequest } from "@nicestack/common"; +import { UserProfile, db, RowModelRequest } from "@nice/common"; import { LogicalCondition, OperatorType, SQLBuilder } from './sql-builder'; export interface GetRowOptions { id?: string; diff --git a/apps/server/src/models/course/course.router.ts b/apps/server/src/models/course/course.router.ts index f39e0cb..f08d35c 100644 --- a/apps/server/src/models/course/course.router.ts +++ b/apps/server/src/models/course/course.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { CourseService } from './course.service'; import { z, ZodType } from 'zod'; const CourseCreateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/course/course.service.ts b/apps/server/src/models/course/course.service.ts index b5e534b..f21a249 100644 --- a/apps/server/src/models/course/course.service.ts +++ b/apps/server/src/models/course/course.service.ts @@ -6,7 +6,7 @@ import { ObjectType, Prisma, InstructorRole, -} from '@nicestack/common'; +} from '@nice/common'; @Injectable() export class CourseService extends BaseService { constructor() { diff --git a/apps/server/src/models/course/utils.ts b/apps/server/src/models/course/utils.ts index 555cf85..9d2092c 100644 --- a/apps/server/src/models/course/utils.ts +++ b/apps/server/src/models/course/utils.ts @@ -1,4 +1,4 @@ -import { db, EnrollmentStatus, PostType } from "@nicestack/common"; +import { db, EnrollmentStatus, PostType } from "@nice/common"; // 更新课程评价统计 export async function updateCourseReviewStats(courseId: string) { diff --git a/apps/server/src/models/department/department.controller.ts b/apps/server/src/models/department/department.controller.ts index 2f49d4e..7c4f063 100755 --- a/apps/server/src/models/department/department.controller.ts +++ b/apps/server/src/models/department/department.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { DepartmentService } from './department.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; @Controller('dept') export class DepartmentController { diff --git a/apps/server/src/models/department/department.router.ts b/apps/server/src/models/department/department.router.ts index a0890b4..2cd168c 100755 --- a/apps/server/src/models/department/department.router.ts +++ b/apps/server/src/models/department/department.router.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { DepartmentService } from './department.service'; // assuming it's in the same directory -import { DepartmentMethodSchema, Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { DepartmentMethodSchema, Prisma, UpdateOrderSchema } from '@nice/common'; import { z, ZodType } from 'zod'; import { DepartmentRowService } from './department.row.service'; diff --git a/apps/server/src/models/department/department.row.service.ts b/apps/server/src/models/department/department.row.service.ts index c4056c8..b97f459 100644 --- a/apps/server/src/models/department/department.row.service.ts +++ b/apps/server/src/models/department/department.row.service.ts @@ -8,7 +8,7 @@ import { DepartmentMethodSchema, ObjectType, UserProfile, -} from '@nicestack/common'; +} from '@nice/common'; import { date, z } from 'zod'; import { RowCacheService } from '../base/row-cache.service'; import { isFieldCondition } from '../base/sql-builder'; diff --git a/apps/server/src/models/department/department.service.ts b/apps/server/src/models/department/department.service.ts index 3d23d09..68c02eb 100755 --- a/apps/server/src/models/department/department.service.ts +++ b/apps/server/src/models/department/department.service.ts @@ -6,7 +6,7 @@ import { getUniqueItems, ObjectType, Prisma, -} from '@nicestack/common'; +} from '@nice/common'; import { BaseTreeService } from '../base/base.tree.service'; import { z } from 'zod'; import { mapToDeptSimpleTree, getStaffsByDeptIds } from './utils'; diff --git a/apps/server/src/models/department/utils.ts b/apps/server/src/models/department/utils.ts index 4295fff..096a6cc 100644 --- a/apps/server/src/models/department/utils.ts +++ b/apps/server/src/models/department/utils.ts @@ -1,4 +1,4 @@ -import { UserProfile, db, DeptSimpleTreeNode, TreeDataNode } from "@nicestack/common"; +import { UserProfile, db, DeptSimpleTreeNode, TreeDataNode } from "@nice/common"; /** * 将部门数据映射为DeptSimpleTreeNode结构 diff --git a/apps/server/src/models/enrollment/enrollment.router.ts b/apps/server/src/models/enrollment/enrollment.router.ts index d11c095..b14e2f2 100644 --- a/apps/server/src/models/enrollment/enrollment.router.ts +++ b/apps/server/src/models/enrollment/enrollment.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { EnrollmentService } from './enrollment.service'; import { z, ZodType } from 'zod'; import { EnrollSchema, UnenrollSchema } from './enroll.schema'; diff --git a/apps/server/src/models/enrollment/enrollment.service.ts b/apps/server/src/models/enrollment/enrollment.service.ts index 3c5af86..63f87ef 100644 --- a/apps/server/src/models/enrollment/enrollment.service.ts +++ b/apps/server/src/models/enrollment/enrollment.service.ts @@ -6,7 +6,7 @@ import { ObjectType, Prisma, EnrollmentStatus -} from '@nicestack/common'; +} from '@nice/common'; import { z } from 'zod'; import { EnrollSchema, UnenrollSchema } from './enroll.schema'; import EventBus, { CrudOperation } from '@server/utils/event-bus'; diff --git a/apps/server/src/models/lecture/lecture.router.ts b/apps/server/src/models/lecture/lecture.router.ts index affcdd4..39d609e 100644 --- a/apps/server/src/models/lecture/lecture.router.ts +++ b/apps/server/src/models/lecture/lecture.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { LectureService } from './lecture.service'; import { z, ZodType } from 'zod'; const LectureCreateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/lecture/lecture.service.ts b/apps/server/src/models/lecture/lecture.service.ts index 66bbc12..ea02d72 100644 --- a/apps/server/src/models/lecture/lecture.service.ts +++ b/apps/server/src/models/lecture/lecture.service.ts @@ -5,7 +5,7 @@ import { db, ObjectType, Prisma -} from '@nicestack/common'; +} from '@nice/common'; import EventBus, { CrudOperation } from '@server/utils/event-bus'; @Injectable() diff --git a/apps/server/src/models/lecture/utils.ts b/apps/server/src/models/lecture/utils.ts index a634d15..008bc87 100644 --- a/apps/server/src/models/lecture/utils.ts +++ b/apps/server/src/models/lecture/utils.ts @@ -1,4 +1,4 @@ -import { db, Lecture } from "@nicestack/common" +import { db, Lecture } from "@nice/common" export async function updateSectionLectureStats(sectionId: string) { const sectionStats = await db.lecture.aggregate({ diff --git a/apps/server/src/models/message/message.controller.ts b/apps/server/src/models/message/message.controller.ts index b6638dc..0738c08 100755 --- a/apps/server/src/models/message/message.controller.ts +++ b/apps/server/src/models/message/message.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { MessageService } from './message.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db, VisitType } from '@nicestack/common'; +import { db, VisitType } from '@nice/common'; @Controller('message') export class MessageController { diff --git a/apps/server/src/models/message/message.router.ts b/apps/server/src/models/message/message.router.ts index 188ef26..44d56b9 100755 --- a/apps/server/src/models/message/message.router.ts +++ b/apps/server/src/models/message/message.router.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { MessageService } from './message.service'; -import { Prisma } from '@nicestack/common'; +import { Prisma } from '@nice/common'; import { z, ZodType } from 'zod'; const MessageUncheckedCreateInputSchema: ZodType = z.any() const MessageWhereInputSchema: ZodType = z.any() diff --git a/apps/server/src/models/message/message.service.ts b/apps/server/src/models/message/message.service.ts index f6b9525..8b85635 100644 --- a/apps/server/src/models/message/message.service.ts +++ b/apps/server/src/models/message/message.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { UserProfile, db, Prisma, VisitType, ObjectType } from '@nicestack/common'; +import { UserProfile, db, Prisma, VisitType, ObjectType } from '@nice/common'; import { BaseService } from '../base/base.service'; import EventBus, { CrudOperation } from '@server/utils/event-bus'; import { setMessageRelation } from './utils'; diff --git a/apps/server/src/models/message/utils.ts b/apps/server/src/models/message/utils.ts index 40379ff..7c2bf35 100644 --- a/apps/server/src/models/message/utils.ts +++ b/apps/server/src/models/message/utils.ts @@ -1,4 +1,4 @@ -import { Message, UserProfile, VisitType, db } from "@nicestack/common" +import { Message, UserProfile, VisitType, db } from "@nice/common" export async function setMessageRelation( data: Message, staff?: UserProfile, diff --git a/apps/server/src/models/post/post.controller.ts b/apps/server/src/models/post/post.controller.ts index f9d5076..e5731af 100755 --- a/apps/server/src/models/post/post.controller.ts +++ b/apps/server/src/models/post/post.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { PostService } from './post.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; @Controller('post') export class PostController { diff --git a/apps/server/src/models/post/post.router.ts b/apps/server/src/models/post/post.router.ts index 7701a5e..b4ca364 100755 --- a/apps/server/src/models/post/post.router.ts +++ b/apps/server/src/models/post/post.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma } from '@nicestack/common'; +import { Prisma } from '@nice/common'; import { PostService } from './post.service'; import { z, ZodType } from 'zod'; const PostCreateArgsSchema: ZodType = z.any(); diff --git a/apps/server/src/models/post/post.service.ts b/apps/server/src/models/post/post.service.ts index f9f17ce..a391ba6 100755 --- a/apps/server/src/models/post/post.service.ts +++ b/apps/server/src/models/post/post.service.ts @@ -9,7 +9,7 @@ import { RolePerms, ResPerm, ObjectType, -} from '@nicestack/common'; +} from '@nice/common'; import { MessageService } from '../message/message.service'; import { BaseService } from '../base/base.service'; import { DepartmentService } from '../department/department.service'; diff --git a/apps/server/src/models/post/utils.ts b/apps/server/src/models/post/utils.ts index 63a5776..6029f81 100644 --- a/apps/server/src/models/post/utils.ts +++ b/apps/server/src/models/post/utils.ts @@ -1,4 +1,4 @@ -import { db, Post, PostType, UserProfile, VisitType } from "@nicestack/common"; +import { db, Post, PostType, UserProfile, VisitType } from "@nice/common"; export async function setPostRelation(params: { data: Post, staff?: UserProfile }) { const { data, staff } = params diff --git a/apps/server/src/models/rbac/role.router.ts b/apps/server/src/models/rbac/role.router.ts index b52308a..5bb9dd3 100755 --- a/apps/server/src/models/rbac/role.router.ts +++ b/apps/server/src/models/rbac/role.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { RoleService } from './role.service'; import { z, ZodType } from 'zod'; const RoleCreateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/rbac/role.row.service.ts b/apps/server/src/models/rbac/role.row.service.ts index 7a034eb..27e4574 100644 --- a/apps/server/src/models/rbac/role.row.service.ts +++ b/apps/server/src/models/rbac/role.row.service.ts @@ -1,4 +1,4 @@ -import { db, ObjectType, RowModelRequest, RowRequestSchema, UserProfile } from "@nicestack/common"; +import { db, ObjectType, RowModelRequest, RowRequestSchema, UserProfile } from "@nice/common"; import { RowCacheService } from "../base/row-cache.service"; import { isFieldCondition, LogicalCondition } from "../base/sql-builder"; import { z } from "zod"; diff --git a/apps/server/src/models/rbac/role.service.ts b/apps/server/src/models/rbac/role.service.ts index cd7d0ce..210b5df 100755 --- a/apps/server/src/models/rbac/role.service.ts +++ b/apps/server/src/models/rbac/role.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { db, RoleMethodSchema, ObjectType, Prisma } from "@nicestack/common"; +import { db, RoleMethodSchema, ObjectType, Prisma } from "@nice/common"; import { BaseService } from '../base/base.service'; @Injectable() export class RoleService extends BaseService { diff --git a/apps/server/src/models/rbac/rolemap.router.ts b/apps/server/src/models/rbac/rolemap.router.ts index ed862c9..72ae5a4 100755 --- a/apps/server/src/models/rbac/rolemap.router.ts +++ b/apps/server/src/models/rbac/rolemap.router.ts @@ -3,7 +3,7 @@ import { TrpcService } from '@server/trpc/trpc.service'; import { ObjectType, RoleMapMethodSchema, -} from '@nicestack/common'; +} from '@nice/common'; import { RoleMapService } from './rolemap.service'; @Injectable() diff --git a/apps/server/src/models/rbac/rolemap.service.ts b/apps/server/src/models/rbac/rolemap.service.ts index 932f851..d3c971a 100755 --- a/apps/server/src/models/rbac/rolemap.service.ts +++ b/apps/server/src/models/rbac/rolemap.service.ts @@ -6,7 +6,7 @@ import { Prisma, RowModelRequest, UserProfile, -} from '@nicestack/common'; +} from '@nice/common'; import { DepartmentService } from '@server/models/department/department.service'; import { TRPCError } from '@trpc/server'; import { RowModelService } from '../base/row-model.service'; diff --git a/apps/server/src/models/resource/pipe/resource.pipeline.ts b/apps/server/src/models/resource/pipe/resource.pipeline.ts index 9b0c962..4a1ce4c 100644 --- a/apps/server/src/models/resource/pipe/resource.pipeline.ts +++ b/apps/server/src/models/resource/pipe/resource.pipeline.ts @@ -1,6 +1,6 @@ import { PrismaClient, Resource } from '@prisma/client' import { ProcessResult, ResourceProcessor } from '../types' -import { db, ResourceProcessStatus } from '@nicestack/common' +import { db, ResourceProcessStatus } from '@nice/common' import { Logger } from '@nestjs/common'; diff --git a/apps/server/src/models/resource/processor/GeneralProcessor.ts b/apps/server/src/models/resource/processor/GeneralProcessor.ts index 652fddd..be2f731 100644 --- a/apps/server/src/models/resource/processor/GeneralProcessor.ts +++ b/apps/server/src/models/resource/processor/GeneralProcessor.ts @@ -1,5 +1,5 @@ import { BaseMetadata, FileMetadata, ResourceProcessor } from "../types"; -import { Resource, db, ResourceProcessStatus } from "@nicestack/common"; +import { Resource, db, ResourceProcessStatus } from "@nice/common"; import { extname } from "path"; import mime from "mime"; import { calculateFileHash, getUploadFilePath } from "@server/utils/file"; diff --git a/apps/server/src/models/resource/processor/ImageProcessor.ts b/apps/server/src/models/resource/processor/ImageProcessor.ts index cb42f50..4cd99c1 100644 --- a/apps/server/src/models/resource/processor/ImageProcessor.ts +++ b/apps/server/src/models/resource/processor/ImageProcessor.ts @@ -1,7 +1,7 @@ import path from "path"; import sharp from 'sharp'; import { FileMetadata, ImageMetadata, ResourceProcessor } from "../types"; -import { Resource, ResourceProcessStatus, db } from "@nicestack/common"; +import { Resource, ResourceProcessStatus, db } from "@nice/common"; import { getUploadFilePath } from "@server/utils/file"; import { Logger } from "@nestjs/common"; import { promises as fsPromises } from 'fs'; diff --git a/apps/server/src/models/resource/processor/VideoProcessor.ts b/apps/server/src/models/resource/processor/VideoProcessor.ts index 1d80eff..5ab678d 100644 --- a/apps/server/src/models/resource/processor/VideoProcessor.ts +++ b/apps/server/src/models/resource/processor/VideoProcessor.ts @@ -1,6 +1,6 @@ // import ffmpeg from 'fluent-ffmpeg'; // import { ResourceProcessor } from '../types'; -// import { Resource } from '@nicestack/common'; +// import { Resource } from '@nice/common'; // export class VideoProcessor implements ResourceProcessor { // async process(resource: Resource): Promise { diff --git a/apps/server/src/models/resource/resource.router.ts b/apps/server/src/models/resource/resource.router.ts index 0751de3..6d4290f 100644 --- a/apps/server/src/models/resource/resource.router.ts +++ b/apps/server/src/models/resource/resource.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { ResourceService } from './resource.service'; import { z, ZodType } from 'zod'; const ResourceCreateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/resource/resource.service.ts b/apps/server/src/models/resource/resource.service.ts index 4f71164..a64529f 100644 --- a/apps/server/src/models/resource/resource.service.ts +++ b/apps/server/src/models/resource/resource.service.ts @@ -6,8 +6,7 @@ import { ObjectType, Prisma, Resource, -} from '@nicestack/common'; -import { createHash } from 'crypto'; +} from '@nice/common'; @Injectable() export class ResourceService extends BaseService { @@ -31,8 +30,4 @@ export class ResourceService extends BaseService { }, }); } - async calculateFileHash(buffer: Buffer): Promise { - return createHash('sha256').update(buffer).digest('hex'); - } - } \ No newline at end of file diff --git a/apps/server/src/models/resource/types.ts b/apps/server/src/models/resource/types.ts index 842db41..12441a6 100644 --- a/apps/server/src/models/resource/types.ts +++ b/apps/server/src/models/resource/types.ts @@ -1,4 +1,4 @@ -import { Resource } from "@nicestack/common"; +import { Resource } from "@nice/common"; export interface ResourceProcessor { process(resource: Resource): Promise diff --git a/apps/server/src/models/section/section.router.ts b/apps/server/src/models/section/section.router.ts index ef9ff3e..fcdd6ba 100644 --- a/apps/server/src/models/section/section.router.ts +++ b/apps/server/src/models/section/section.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, UpdateOrderSchema } from '@nice/common'; import { SectionService } from './section.service'; import { z, ZodType } from 'zod'; const SectionCreateArgsSchema: ZodType = z.any() diff --git a/apps/server/src/models/section/section.service.ts b/apps/server/src/models/section/section.service.ts index e926eff..229e26b 100644 --- a/apps/server/src/models/section/section.service.ts +++ b/apps/server/src/models/section/section.service.ts @@ -6,7 +6,7 @@ import { ObjectType, Prisma, -} from '@nicestack/common'; +} from '@nice/common'; @Injectable() export class SectionService extends BaseService { constructor() { diff --git a/apps/server/src/models/staff/staff.controller.ts b/apps/server/src/models/staff/staff.controller.ts index 45e8349..7a590e9 100755 --- a/apps/server/src/models/staff/staff.controller.ts +++ b/apps/server/src/models/staff/staff.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { StaffService } from './staff.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; @Controller('staff') export class StaffController { diff --git a/apps/server/src/models/staff/staff.router.ts b/apps/server/src/models/staff/staff.router.ts index 5ea3015..37fe6fa 100755 --- a/apps/server/src/models/staff/staff.router.ts +++ b/apps/server/src/models/staff/staff.router.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { StaffService } from './staff.service'; // Adjust the import path as necessary -import { StaffMethodSchema, Prisma, UpdateOrderSchema } from '@nicestack/common'; +import { StaffMethodSchema, Prisma, UpdateOrderSchema } from '@nice/common'; import { z, ZodType } from 'zod'; import { StaffRowService } from './staff.row.service'; const StaffCreateArgsSchema: ZodType = z.any(); diff --git a/apps/server/src/models/staff/staff.row.service.ts b/apps/server/src/models/staff/staff.row.service.ts index 830f54c..c2e92c2 100644 --- a/apps/server/src/models/staff/staff.row.service.ts +++ b/apps/server/src/models/staff/staff.row.service.ts @@ -8,7 +8,7 @@ import { ResPerm, Staff, RowModelRequest, -} from '@nicestack/common'; +} from '@nice/common'; import { DepartmentService } from '../department/department.service'; import { RowCacheService } from '../base/row-cache.service'; import { z } from 'zod'; diff --git a/apps/server/src/models/staff/staff.service.ts b/apps/server/src/models/staff/staff.service.ts index c9ccd02..cf37549 100755 --- a/apps/server/src/models/staff/staff.service.ts +++ b/apps/server/src/models/staff/staff.service.ts @@ -5,7 +5,7 @@ import { ObjectType, UserProfile, Prisma, -} from '@nicestack/common'; +} from '@nice/common'; import { DepartmentService } from '../department/department.service'; import { z } from 'zod'; import { BaseService } from '../base/base.service'; diff --git a/apps/server/src/models/taxonomy/taxonomy.controller.ts b/apps/server/src/models/taxonomy/taxonomy.controller.ts index 00e0b6f..1d5f36c 100755 --- a/apps/server/src/models/taxonomy/taxonomy.controller.ts +++ b/apps/server/src/models/taxonomy/taxonomy.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { TaxonomyService } from './taxonomy.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; @Controller('tax') export class TaxonomyController { diff --git a/apps/server/src/models/taxonomy/taxonomy.router.ts b/apps/server/src/models/taxonomy/taxonomy.router.ts index 1f302a7..e827b36 100755 --- a/apps/server/src/models/taxonomy/taxonomy.router.ts +++ b/apps/server/src/models/taxonomy/taxonomy.router.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { TaxonomyService } from './taxonomy.service'; -import { TaxonomyMethodSchema } from '@nicestack/common'; +import { TaxonomyMethodSchema } from '@nice/common'; @Injectable() export class TaxonomyRouter { diff --git a/apps/server/src/models/taxonomy/taxonomy.service.ts b/apps/server/src/models/taxonomy/taxonomy.service.ts index c43914b..8ca0d00 100755 --- a/apps/server/src/models/taxonomy/taxonomy.service.ts +++ b/apps/server/src/models/taxonomy/taxonomy.service.ts @@ -1,5 +1,5 @@ import { Injectable } from '@nestjs/common'; -import { db, TaxonomyMethodSchema, Prisma } from '@nicestack/common'; +import { db, TaxonomyMethodSchema, Prisma } from '@nice/common'; import { redis } from '@server/utils/redis/redis.service'; import { deleteByPattern } from '@server/utils/redis/utils'; import { TRPCError } from '@trpc/server'; diff --git a/apps/server/src/models/term/term.controller.ts b/apps/server/src/models/term/term.controller.ts index 12a165d..787bed4 100755 --- a/apps/server/src/models/term/term.controller.ts +++ b/apps/server/src/models/term/term.controller.ts @@ -2,7 +2,7 @@ import { Controller, Get, Query, UseGuards } from '@nestjs/common'; import { TermService } from './term.service'; import { AuthGuard } from '@server/auth/auth.guard'; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; @Controller('term') export class TermController { diff --git a/apps/server/src/models/term/term.router.ts b/apps/server/src/models/term/term.router.ts index ba44e35..43afff5 100755 --- a/apps/server/src/models/term/term.router.ts +++ b/apps/server/src/models/term/term.router.ts @@ -1,7 +1,7 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; import { TermService } from './term.service'; // Adjust the import path as necessary -import { Prisma, TermMethodSchema, UpdateOrderSchema } from '@nicestack/common'; +import { Prisma, TermMethodSchema, UpdateOrderSchema } from '@nice/common'; import { z, ZodType } from 'zod'; import { TermRowService } from './term.row.service'; const TermCreateArgsSchema: ZodType = z.any(); diff --git a/apps/server/src/models/term/term.row.service.ts b/apps/server/src/models/term/term.row.service.ts index a518f9b..65ba56e 100644 --- a/apps/server/src/models/term/term.row.service.ts +++ b/apps/server/src/models/term/term.row.service.ts @@ -4,7 +4,7 @@ import { RowModelRequest, TermMethodSchema, UserProfile, -} from '@nicestack/common'; +} from '@nice/common'; import { date, z } from 'zod'; import { RowCacheService } from '../base/row-cache.service'; import { isFieldCondition } from '../base/sql-builder'; diff --git a/apps/server/src/models/term/term.service.ts b/apps/server/src/models/term/term.service.ts index c8ce22f..7f21041 100755 --- a/apps/server/src/models/term/term.service.ts +++ b/apps/server/src/models/term/term.service.ts @@ -13,7 +13,7 @@ import { TaxonomySlug, ObjectType, TermAncestry, -} from '@nicestack/common'; +} from '@nice/common'; import { z } from 'zod'; import { BaseTreeService } from '../base/base.tree.service'; import EventBus, { CrudOperation } from '@server/utils/event-bus'; diff --git a/apps/server/src/models/term/utils.ts b/apps/server/src/models/term/utils.ts index 8271d15..65bddcd 100644 --- a/apps/server/src/models/term/utils.ts +++ b/apps/server/src/models/term/utils.ts @@ -1,4 +1,4 @@ -import { TreeDataNode } from '@nicestack/common'; +import { TreeDataNode } from '@nice/common'; export function formatToTermTreeData(term: any): TreeDataNode { return { diff --git a/apps/server/src/models/transform/transform.router.ts b/apps/server/src/models/transform/transform.router.ts index 0c2c798..9a3d7ed 100755 --- a/apps/server/src/models/transform/transform.router.ts +++ b/apps/server/src/models/transform/transform.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TransformService } from './transform.service'; -import { TransformMethodSchema} from '@nicestack/common'; +import { TransformMethodSchema} from '@nice/common'; import { TrpcService } from '@server/trpc/trpc.service'; @Injectable() export class TransformRouter { diff --git a/apps/server/src/models/transform/transform.service.ts b/apps/server/src/models/transform/transform.service.ts index 9509bdc..22a835a 100755 --- a/apps/server/src/models/transform/transform.service.ts +++ b/apps/server/src/models/transform/transform.service.ts @@ -5,7 +5,7 @@ import { db, Prisma, Staff, -} from '@nicestack/common'; +} from '@nice/common'; import dayjs from 'dayjs'; import * as argon2 from 'argon2'; import { TaxonomyService } from '@server/models/taxonomy/taxonomy.service'; diff --git a/apps/server/src/models/visit/visit.router.ts b/apps/server/src/models/visit/visit.router.ts index f3dc2df..2bb9064 100644 --- a/apps/server/src/models/visit/visit.router.ts +++ b/apps/server/src/models/visit/visit.router.ts @@ -1,6 +1,6 @@ import { Injectable } from '@nestjs/common'; import { TrpcService } from '@server/trpc/trpc.service'; -import { Prisma } from '@nicestack/common'; +import { Prisma } from '@nice/common'; import { VisitService } from './visit.service'; import { z, ZodType } from 'zod'; diff --git a/apps/server/src/models/visit/visit.service.ts b/apps/server/src/models/visit/visit.service.ts index c3ed87e..69ac47a 100644 --- a/apps/server/src/models/visit/visit.service.ts +++ b/apps/server/src/models/visit/visit.service.ts @@ -6,7 +6,7 @@ import { ObjectType, Prisma, VisitType, -} from '@nicestack/common'; +} from '@nice/common'; import EventBus from '@server/utils/event-bus'; @Injectable() export class VisitService extends BaseService { diff --git a/apps/server/src/queue/stats/stats.service.ts b/apps/server/src/queue/stats/stats.service.ts index d3b91b5..a498704 100644 --- a/apps/server/src/queue/stats/stats.service.ts +++ b/apps/server/src/queue/stats/stats.service.ts @@ -2,7 +2,7 @@ import { InjectQueue } from "@nestjs/bullmq"; import { Injectable } from "@nestjs/common"; import EventBus from "@server/utils/event-bus"; import { Queue } from "bullmq"; -import { ObjectType } from "@nicestack/common"; +import { ObjectType } from "@nice/common"; import { QueueJobType } from "../types"; @Injectable() export class StatsService { diff --git a/apps/server/src/queue/worker/file.processor.ts b/apps/server/src/queue/worker/file.processor.ts index 404bf2e..b1d1374 100644 --- a/apps/server/src/queue/worker/file.processor.ts +++ b/apps/server/src/queue/worker/file.processor.ts @@ -5,7 +5,7 @@ import { ResourceProcessingPipeline } from '@server/models/resource/pipe/resourc import { GeneralProcessor } from '@server/models/resource/processor/GeneralProcessor'; import { ImageProcessor } from '@server/models/resource/processor/ImageProcessor'; import superjson from 'superjson-cjs'; -import { Resource } from '@nicestack/common'; +import { Resource } from '@nice/common'; const logger = new Logger('FileProcessorWorker'); const pipeline = new ResourceProcessingPipeline() .addProcessor(new GeneralProcessor()) diff --git a/apps/server/src/queue/worker/processor.ts b/apps/server/src/queue/worker/processor.ts index 30c27cf..6574912 100755 --- a/apps/server/src/queue/worker/processor.ts +++ b/apps/server/src/queue/worker/processor.ts @@ -4,7 +4,7 @@ import { updateCourseLectureStats, updateSectionLectureStats } from '@server/models/lecture/utils'; -import { ObjectType } from '@nicestack/common'; +import { ObjectType } from '@nice/common'; import { updateCourseEnrollmentStats, updateCourseReviewStats diff --git a/apps/server/src/socket/base/base-websocket-server.ts b/apps/server/src/socket/base/base-websocket-server.ts index babe3ac..f5716c1 100644 --- a/apps/server/src/socket/base/base-websocket-server.ts +++ b/apps/server/src/socket/base/base-websocket-server.ts @@ -2,7 +2,7 @@ import { WebSocketServer, WebSocket } from "ws"; import { Logger } from "@nestjs/common"; import { WebSocketServerConfig, WSClient, WebSocketType } from "../types"; -import { SocketMessage } from '@nicestack/common'; +import { SocketMessage } from '@nice/common'; const DEFAULT_CONFIG: WebSocketServerConfig = { pingInterval: 30000, diff --git a/apps/server/src/socket/collaboration/ws-shared-doc.ts b/apps/server/src/socket/collaboration/ws-shared-doc.ts index 9c2b3cf..ae1bd09 100644 --- a/apps/server/src/socket/collaboration/ws-shared-doc.ts +++ b/apps/server/src/socket/collaboration/ws-shared-doc.ts @@ -1,5 +1,5 @@ -import { readSyncMessage } from '@nicestack/common'; -import { applyAwarenessUpdate, Awareness, encodeAwarenessUpdate, removeAwarenessStates, writeSyncStep1, writeUpdate } from '@nicestack/common'; +import { readSyncMessage } from '@nice/common'; +import { applyAwarenessUpdate, Awareness, encodeAwarenessUpdate, removeAwarenessStates, writeSyncStep1, writeUpdate } from '@nice/common'; import * as encoding from 'lib0/encoding'; import * as decoding from 'lib0/decoding'; import * as Y from "yjs" @@ -7,7 +7,7 @@ import { debounce } from 'lodash'; import { getPersistence, setPersistence } from './persistence'; import { callbackHandler, isCallbackSet } from './callback'; import { WebSocket } from "ws"; -import { YMessageType } from '@nicestack/common'; +import { YMessageType } from '@nice/common'; import { WSClient } from '../types'; export const docs = new Map(); export const CALLBACK_DEBOUNCE_WAIT = parseInt(process.env.CALLBACK_DEBOUNCE_WAIT || '2000'); diff --git a/apps/server/src/socket/collaboration/yjs.server.ts b/apps/server/src/socket/collaboration/yjs.server.ts index 969d646..0b747bd 100644 --- a/apps/server/src/socket/collaboration/yjs.server.ts +++ b/apps/server/src/socket/collaboration/yjs.server.ts @@ -2,7 +2,7 @@ import { Injectable } from "@nestjs/common"; import { WebSocketType, WSClient } from "../types"; import { BaseWebSocketServer } from "../base/base-websocket-server"; import { encoding } from "lib0"; -import { YMessageType, writeSyncStep1, encodeAwarenessUpdate } from "@nicestack/common"; +import { YMessageType, writeSyncStep1, encodeAwarenessUpdate } from "@nice/common"; import { getYDoc, closeConn, WSSharedDoc, messageListener, send } from "./ws-shared-doc"; @Injectable() export class YjsServer extends BaseWebSocketServer { diff --git a/apps/server/src/socket/realtime/realtime.server.ts b/apps/server/src/socket/realtime/realtime.server.ts index 0c642c6..18d7d99 100644 --- a/apps/server/src/socket/realtime/realtime.server.ts +++ b/apps/server/src/socket/realtime/realtime.server.ts @@ -2,7 +2,7 @@ import { Injectable, OnModuleInit } from "@nestjs/common"; import { WebSocketType } from "../types"; import { BaseWebSocketServer } from "../base/base-websocket-server"; import EventBus, { CrudOperation } from "@server/utils/event-bus"; -import { ObjectType, SocketMsgType, MessageDto, PostDto, PostType } from "@nicestack/common"; +import { ObjectType, SocketMsgType, MessageDto, PostDto, PostType } from "@nice/common"; @Injectable() export class RealtimeServer extends BaseWebSocketServer implements OnModuleInit { onModuleInit() { diff --git a/apps/server/src/tasks/init/gendev.service.ts b/apps/server/src/tasks/init/gendev.service.ts index 209feee..d955a18 100644 --- a/apps/server/src/tasks/init/gendev.service.ts +++ b/apps/server/src/tasks/init/gendev.service.ts @@ -10,8 +10,7 @@ import { Staff, TaxonomySlug, Term, -} from '@nicestack/common'; -import * as argon2 from 'argon2'; +} from '@nice/common'; import EventBus from '@server/utils/event-bus'; import { @@ -19,9 +18,7 @@ import { DevDataCounts, getCounts, } from './utils'; - import { StaffService } from '@server/models/staff/staff.service'; -import { uuidv4 } from 'lib0/random'; @Injectable() export class GenDevService { private readonly logger = new Logger(GenDevService.name); diff --git a/apps/server/src/tasks/init/init.service.ts b/apps/server/src/tasks/init/init.service.ts index b44376e..1b2a420 100755 --- a/apps/server/src/tasks/init/init.service.ts +++ b/apps/server/src/tasks/init/init.service.ts @@ -1,5 +1,5 @@ import { Injectable, Logger } from '@nestjs/common'; -import { db, InitAppConfigs, InitRoles, InitTaxonomies, ObjectType } from "@nicestack/common"; +import { db, InitAppConfigs, InitRoles, InitTaxonomies, ObjectType } from "@nice/common"; import { AuthService } from '@server/auth/auth.service'; import { MinioService } from '@server/utils/minio/minio.service'; import { AppConfigService } from '@server/models/app-config/app-config.service'; diff --git a/apps/server/src/tasks/init/utils.ts b/apps/server/src/tasks/init/utils.ts index 3fb5f97..292eb9e 100644 --- a/apps/server/src/tasks/init/utils.ts +++ b/apps/server/src/tasks/init/utils.ts @@ -1,4 +1,4 @@ -import { db, getRandomElement, getRandomIntInRange, getRandomTimeInterval, } from '@nicestack/common'; +import { db, getRandomElement, getRandomIntInRange, getRandomTimeInterval, } from '@nice/common'; import dayjs from 'dayjs'; export interface DevDataCounts { deptCount: number; diff --git a/apps/server/src/trpc/trpc.service.ts b/apps/server/src/trpc/trpc.service.ts index b04bab1..cbcd25b 100755 --- a/apps/server/src/trpc/trpc.service.ts +++ b/apps/server/src/trpc/trpc.service.ts @@ -2,7 +2,7 @@ import { Injectable, Logger } from '@nestjs/common'; import { initTRPC, TRPCError } from '@trpc/server'; import superjson from 'superjson-cjs'; import * as trpcExpress from '@trpc/server/adapters/express'; -import { db, JwtPayload, UserProfile, RolePerms } from '@nicestack/common'; +import { db, JwtPayload, UserProfile, RolePerms } from '@nice/common'; import { CreateWSSContextFnOptions } from '@trpc/server/adapters/ws'; import { UserProfileService } from '@server/auth/utils'; type Context = Awaited>; diff --git a/apps/server/src/upload/chunk.manager.ts b/apps/server/src/upload/chunk.manager.ts new file mode 100644 index 0000000..ae41ec6 --- /dev/null +++ b/apps/server/src/upload/chunk.manager.ts @@ -0,0 +1,135 @@ +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { Injectable, Logger } from '@nestjs/common'; +import { ChunkDto } from '@nice/common'; + +@Injectable() +export class ChunkManager { + private readonly logger = new Logger(ChunkManager.name); + + private readonly CHUNK_PROCESSING_CONCURRENCY = 3; + private readonly uploadDir: string; + private readonly tempDir: string; + + constructor() { + this.uploadDir = process.env.UPLOAD_DIR || path.join(process.cwd(), 'uploads'); + this.tempDir = path.join(this.uploadDir, 'temp'); + this.logger.localInstance.setLogLevels(["log", "debug"]) + this.logger.log(`Initialized ChunkManager with uploadDir: ${this.uploadDir}, tempDir: ${this.tempDir}`); + } + + private validateChunk(chunk: ChunkDto, file: Express.Multer.File): void { + this.logger.debug(`Validating chunk: identifier=${chunk?.identifier}, chunkNumber=${chunk?.chunkNumber}, bufferLength=${file?.buffer?.length}`); + + if (!chunk?.identifier || !chunk.chunkNumber || !file?.buffer?.length) { + this.logger.warn('Invalid chunk metadata or buffer'); + throw new Error('Invalid chunk metadata or buffer'); + } + } + + async save(chunk: ChunkDto, file: Express.Multer.File): Promise { + this.logger.log(`Saving chunk: identifier=${chunk.identifier}, chunkNumber=${chunk.chunkNumber}`); + this.validateChunk(chunk, file); + const chunkDir = path.join(this.tempDir, chunk.identifier); + const chunkPath = path.join(chunkDir, `${chunk.chunkNumber}`); + try { + this.logger.debug(`Creating chunk directory: ${chunkDir}`); + await fs.mkdir(chunkDir, { recursive: true, mode: 0o755 }); + + this.logger.debug(`Writing chunk file: ${chunkPath}`); + await fs.writeFile(chunkPath, file.buffer, { mode: 0o644 }); + + this.logger.log(`Chunk saved successfully: ${chunkPath}`); + } catch (error) { + this.logger.error(`Chunk save failed: ${error instanceof Error ? error.message : error}`); + throw error; + } + } + + async getChunks(identifier: string): Promise { + this.logger.log(`Retrieving chunks for identifier: ${identifier}`); + if (!identifier) { + this.logger.warn('No identifier provided'); + return []; + } + try { + const chunkPath = path.join(this.tempDir, identifier); + this.logger.debug(`Reading directory: ${chunkPath}`); + + const files = await fs.readdir(chunkPath); + const chunks = files + .map(Number) + .filter(num => !isNaN(num) && num > 0) + .sort((a, b) => a - b); + + this.logger.log(`Found chunks: ${chunks.join(', ')}`); + return chunks; + } catch (error) { + this.logger.warn(`Chunk retrieval failed: ${error}`); + return []; + } + } + + async merge(chunkDir: string, finalPath: string, totalChunks: number): Promise { + this.logger.log(`Merging chunks: chunkDir=${chunkDir}, finalPath=${finalPath}, totalChunks=${totalChunks}`); + + if (!chunkDir || !finalPath || totalChunks <= 0) { + this.logger.warn('Invalid merge parameters'); + throw new Error('Invalid merge parameters'); + } + + try { + this.logger.debug(`Opening final file: ${finalPath}`); + const fileHandle = await fs.open(finalPath, 'w'); + + try { + for (let i = 0; i < totalChunks; i += this.CHUNK_PROCESSING_CONCURRENCY) { + const batch = Array.from( + { length: Math.min(this.CHUNK_PROCESSING_CONCURRENCY, totalChunks - i) }, + (_, j) => i + j + 1 + ); + this.logger.debug(`Processing batch: ${batch.join(', ')}`); + const chunkBuffers = await Promise.all( + batch.map(chunkNumber => { + const chunkPath = path.join(chunkDir, `${chunkNumber}`); + this.logger.debug(`Reading chunk: ${chunkPath}`); + return fs.readFile(chunkPath); + }) + ); + for (const chunkBuffer of chunkBuffers) { + await fileHandle.write(chunkBuffer, 0, chunkBuffer.length); + } + } + } finally { + this.logger.debug(`Closing file handle: ${finalPath}`); + await fileHandle.close(); + } + + this.logger.log(`Merge completed successfully: ${finalPath}`); + } catch (error) { + this.logger.error(`Merge failed: ${error}`); + throw error; + } + } + + async cleanup(identifier: string): Promise { + this.logger.log(`Cleaning up chunks for identifier: ${identifier}`); + + if (!identifier) { + this.logger.warn('No identifier provided for cleanup'); + return; + } + + try { + const cleanupPath = path.join(this.tempDir, identifier); + this.logger.debug(`Removing directory: ${cleanupPath}`); + + await fs.rm(cleanupPath, { recursive: true, force: true }); + this.logger.log(`Cleanup completed for: ${identifier}`); + } catch (err) { + if (err instanceof Error && err.name !== 'ENOENT') { + this.logger.error(`Cleanup failed: ${err.message}`); + } + } + } +} \ No newline at end of file diff --git a/apps/server/src/upload/clean.service.ts b/apps/server/src/upload/clean.service.ts new file mode 100644 index 0000000..29df686 --- /dev/null +++ b/apps/server/src/upload/clean.service.ts @@ -0,0 +1,52 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { UploadService } from './upload.service'; + +@Injectable() +export class CleanService { + private readonly logger = new Logger(CleanService.name); + private readonly tempDir: string; + private readonly TEMP_FILE_EXPIRATION_TIME = 24 * 60 * 60 * 1000; // 24 hours + + constructor(private uploadService: UploadService) { + this.tempDir = process.env.UPLOAD_TEMP_DIR || path.join(process.cwd(), 'uploads', 'temp'); + } + + @Cron(CronExpression.EVERY_6_HOURS) + async cleanExpiredTemporaryFiles(): Promise { + try { + const files = await fs.readdir(this.tempDir); + + for (const identifier of files) { + const chunkDir = path.join(this.tempDir, identifier); + + try { + const stat = await fs.stat(chunkDir); + + // 检查目录是否超过过期时间 + const isExpired = (Date.now() - stat.mtime.getTime()) > this.TEMP_FILE_EXPIRATION_TIME; + + // 检查上传是否不在进行中 + const status = this.uploadService.checkUploadStatusInfo(identifier); + const isNotInProgress = !status || + status.status === 'completed' || + status.status === 'error' || + status.status === 'paused'; + + if (isExpired && isNotInProgress) { + await fs.rm(chunkDir, { recursive: true, force: true }); + this.logger.log(`Cleaned up expired temporary files for identifier: ${identifier}`); + this.uploadService.deleteUploadStatusInfo(identifier); + } + } catch (statError) { + // 处理可能的文件系统错误 + this.logger.error(`Error processing directory ${identifier}: ${statError}`); + } + } + } catch (error) { + this.logger.error(`Error during temporary file cleanup: ${error instanceof Error ? error.message : error}`); + } + } +} \ No newline at end of file diff --git a/apps/server/src/upload/types.ts b/apps/server/src/upload/types.ts index 018c513..ef60aaa 100644 --- a/apps/server/src/upload/types.ts +++ b/apps/server/src/upload/types.ts @@ -1,32 +1,24 @@ -export interface HookRequest { - Type: 'post-finish' | 'pre-create'; - Event: { - Upload: { - ID?: string; - MetaData?: { - filename?: string - }; - Size?: number; - Storage?: { - Path: string - Type: string - }; - }; - }; +export interface UploadCompleteEvent { + identifier: string; + filename: string; + size: number; + hash: string; + integrityVerified: boolean; } -export interface HookResponse { - RejectUpload?: boolean; - HTTPResponse?: { - StatusCode?: number; - Body?: string; - Headers?: Record; - }; - ChangeFileInfo?: { - ID?: string; - MetaData?: Record; - }; + +export type UploadEvent = { + uploadStart: { identifier: string; filename: string; totalSize: number, resuming?: boolean }; + uploadComplete: UploadCompleteEvent + uploadError: { identifier: string; error: string, filename: string }; } -export interface FileHandle { - filename: string - path: string -} \ No newline at end of file +export interface UploadLock { + clientId: string; + timestamp: number; +} +// 添加重试机制,处理临时网络问题 +// 实现定期清理过期的临时文件 +// 添加文件完整性校验 +// 实现上传进度持久化,支持服务重启后恢复 +// 添加并发限制,防止系统资源耗尽 +// 实现文件去重功能,避免重复上传 +// 添加日志记录和监控机制 \ No newline at end of file diff --git a/apps/server/src/upload/upload-lock.service.ts b/apps/server/src/upload/upload-lock.service.ts new file mode 100644 index 0000000..4d5d136 --- /dev/null +++ b/apps/server/src/upload/upload-lock.service.ts @@ -0,0 +1,88 @@ +import { Injectable, Logger } from '@nestjs/common'; +import { Cron, CronExpression } from '@nestjs/schedule'; // 注意这里是 @Cron +import { UploadLock } from './types'; +import { UploadLockInfo } from '@nice/common'; + +@Injectable() +export class UploadLockService { + private readonly logger = new Logger(UploadLockService.name); + private readonly uploadLocks: Map = new Map(); + private readonly MAX_UPLOAD_DURATION = 2 * 60 * 60 * 1000; // 2 hours + + acquireLock(identifier: string, clientId: string): boolean { + if (!identifier || !clientId) { + this.logger.warn('Invalid lock parameters'); + return false; + } + const now = Date.now(); + const existingLock = this.uploadLocks.get(identifier); + // Check if lock exists and is not expired + if (existingLock) { + const lockDuration = now - existingLock.timestamp; + + // More robust lock conflict check + if ( + existingLock.clientId !== clientId && + lockDuration < this.MAX_UPLOAD_DURATION + ) { + this.logger.warn(`Upload conflict: File ${identifier} is locked by another client`); + return false; + } + } + + // Acquire or update the lock + this.uploadLocks.set(identifier, { clientId, timestamp: now }); + return true; + } + + releaseLock(identifier: string, clientId: string): boolean { + if (!identifier || !clientId) { + this.logger.warn('Invalid unlock parameters'); + return false; + } + + const currentLock = this.uploadLocks.get(identifier); + + // Only allow release by the current lock owner + if (currentLock?.clientId === clientId) { + this.uploadLocks.delete(identifier); + return true; + } + + this.logger.warn(`Unauthorized lock release attempt for ${identifier}`); + return false; + } + + checkLock(identifier: string): UploadLockInfo { + const now = Date.now(); + const lockInfo = this.uploadLocks.get(identifier); + // Check if lock exists and is not expired + if (lockInfo && now - lockInfo.timestamp < this.MAX_UPLOAD_DURATION) { + return { + isLocked: true, + lockedBy: lockInfo.clientId + }; + } + + return { isLocked: false }; + } + + @Cron(CronExpression.EVERY_HOUR) + cleanupExpiredLocks(): number { + const now = Date.now(); + let removedLocksCount = 0; + + for (const [identifier, lock] of this.uploadLocks.entries()) { + if (now - lock.timestamp > this.MAX_UPLOAD_DURATION) { + this.uploadLocks.delete(identifier); + removedLocksCount++; + } + } + + if (removedLocksCount > 0) { + this.logger.log(`Cleaned up ${removedLocksCount} expired locks`); + } + + return removedLocksCount; + } +} \ No newline at end of file diff --git a/apps/server/src/upload/upload.controller.ts b/apps/server/src/upload/upload.controller.ts index eb7028e..0fa3656 100644 --- a/apps/server/src/upload/upload.controller.ts +++ b/apps/server/src/upload/upload.controller.ts @@ -1,20 +1,54 @@ -import { Controller, Logger, Post, Body, Res } from "@nestjs/common"; -import { Response } from "express"; -import { HookRequest, HookResponse } from "./types"; -import { UploadService } from "./upload.service"; +import { + Controller, + Post, + UseInterceptors, + UploadedFile, + Body, + Param, + Get, +} from '@nestjs/common'; +import { FileInterceptor } from '@nestjs/platform-express'; +import { UploadService } from './upload.service'; +import { ChunkDto } from '@nice/common'; -@Controller("upload") +@Controller('upload') export class UploadController { - private readonly logger = new Logger(UploadController.name); constructor(private readonly uploadService: UploadService) { } - @Post("hook") - async handleTusHook(@Body() hookRequest: HookRequest, @Res() res: Response) { - try { - const hookResponse = await this.uploadService.handleTusHook(hookRequest); - return res.status(200).json(hookResponse); - } catch (error: any) { - this.logger.error(`Error handling hook: ${error.message}`); - return res.status(500).json({ error: 'Internal server error' }); + + @Post('chunk') + @UseInterceptors(FileInterceptor('file')) + async uploadChunk( + @Body('chunk') chunkString: string, // 改为接收字符串 + @UploadedFile() file: Express.Multer.File, + @Body('clientId') clientId: string + ) { + const chunk = JSON.parse(chunkString); // 解析字符串为对象 + await this.uploadService.uploadChunk(chunk, file, clientId); + return { message: 'Chunk uploaded successfully' }; + } + @Get('status/:identifier') + checkUploadStatusInfo(@Param('identifier') identifier: string) { + const status = this.uploadService.checkUploadStatusInfo(identifier); + return status || { message: 'No upload status found' }; + } + @Post('pause/:identifier') + pauseUpload( + @Param('identifier') identifier: string, + @Body('clientId') clientId: string + ) { + this.uploadService.pauseUpload(identifier, clientId); + return { message: 'Upload paused successfully' }; + } + + @Post('resume/:identifier') + async resumeUpload( + @Param('identifier') identifier: string, + @Body('clientId') clientId: string + ) { + const resumed = this.uploadService.resumeUpload(identifier, clientId); + if (!resumed) { + throw new Error('Unable to resume upload'); } + return { message: 'Upload resumed successfully' }; } } \ No newline at end of file diff --git a/apps/server/src/upload/upload.module.ts b/apps/server/src/upload/upload.module.ts index b1d37cb..863925c 100644 --- a/apps/server/src/upload/upload.module.ts +++ b/apps/server/src/upload/upload.module.ts @@ -2,6 +2,9 @@ import { Module } from '@nestjs/common'; import { UploadController } from './upload.controller'; import { UploadService } from './upload.service'; import { BullModule } from '@nestjs/bullmq'; +import { UploadLockService } from './upload-lock.service'; +import { ChunkManager } from './chunk.manager'; + @Module({ imports: [ BullModule.registerQueue({ @@ -9,6 +12,6 @@ import { BullModule } from '@nestjs/bullmq'; }), ], controllers: [UploadController], - providers: [UploadService], + providers: [UploadService, UploadLockService, ChunkManager], }) export class UploadModule { } \ No newline at end of file diff --git a/apps/server/src/upload/upload.service.ts b/apps/server/src/upload/upload.service.ts index 198a769..269b7e9 100644 --- a/apps/server/src/upload/upload.service.ts +++ b/apps/server/src/upload/upload.service.ts @@ -1,109 +1,285 @@ import { Injectable, Logger } from '@nestjs/common'; -import { Queue, Job } from 'bullmq'; // 添加 Job 导入 -import { InjectQueue } from '@nestjs/bullmq'; -import { db, Resource, ResourceType } from '@nicestack/common'; -import { HookRequest, HookResponse } from './types'; -import dayjs from 'dayjs'; -import { getFilenameWithoutExt } from '@server/utils/file'; -import { QueueJobType } from '@server/queue/types'; -import { nanoid } from 'nanoid'; -import { slugify } from 'transliteration'; -import path from 'path'; -import fs from 'node:fs'; +import { ChunkDto, UploadStatusInfo, UploadProgress, UploadStatusInfoDto } from '@nice/common'; +import * as fs from 'fs/promises'; +import * as path from 'path'; +import mitt from 'mitt'; +import { ChunkManager } from './chunk.manager'; +import { UploadLockService } from './upload-lock.service'; +import { calculateFileHash } from '@server/utils/file'; +import { UploadEvent } from './types'; @Injectable() export class UploadService { private readonly logger = new Logger(UploadService.name); + private readonly uploadDir: string; + private readonly tempDir: string; + private readonly fileStatuses: Map = new Map(); + private readonly emitter = mitt(); + // Performance Optimization: Configurable upload parameters + private MAX_CONCURRENT_UPLOADS = 5; // Configurable concurrent uploads + private MAX_CHUNK_SIZE = 10 * 1024 * 1024; // 10MB max chunk size + private UPLOAD_TIMEOUT = 30 * 60 * 1000; // 30 minutes timeout constructor( - @InjectQueue('file-queue') private fileQueue: Queue - ) { } - - async handlePreCreateHook(hookRequest: HookRequest): Promise { - const hookResponse: HookResponse = { - HTTPResponse: { - Headers: {} - } - }; - const metaData = hookRequest.Event.Upload.MetaData; - const isValid = metaData && 'filename' in metaData; - if (!isValid) { - hookResponse.RejectUpload = true; - hookResponse.HTTPResponse.StatusCode = 400; - hookResponse.HTTPResponse.Body = 'no filename provided'; - hookResponse.HTTPResponse.Headers['X-Some-Header'] = 'yes'; - } else { - const timestamp = dayjs().format('YYMMDDHHmmss'); - const originalName = metaData.filename; - const extension = path.extname(originalName); // 获取文件扩展名 - // 清理并转换文件名(不包含扩展名) - const cleanName = slugify(getFilenameWithoutExt(originalName), { - lowercase: true, - separator: '-', - trim: true - }) - .replace(/[^\w-]/g, '') - .replace(/-+/g, '-') - .substring(0, 32); - - const uniqueId = nanoid(6); - const fileId = `${timestamp}_${cleanName}_${uniqueId}`; - fs.mkdirSync(path.join(process.env.UPLOAD_DIR, fileId), { recursive: true, mode: 0o755 }) - // fs.chownSync(path.join(process.env.UPLOAD_DIR, fileId), 100999, 100999); - // await fs.promises.mkdir(path.join(process.env.UPLOAD_DIR, fileId), { recursive: true }); - hookResponse.ChangeFileInfo = { - ID: `${fileId}/${fileId}${extension}`, - MetaData: { - filename: originalName, - normalized_name: cleanName, - folder: fileId - } - }; + private chunkManager: ChunkManager, + private uploadLockService: UploadLockService + ) { + // Validate upload directory configuration + this.uploadDir = this.validateUploadDirectory(); + this.tempDir = path.join(this.uploadDir, 'temp'); + this.initDirectories().catch(error => { + this.logger.error(`Failed to initialize upload directories: ${error.message}`); + process.exit(1); + }); + this.configureUploadLimits(); + } + private validateUploadDirectory(): string { + const uploadDir = process.env.UPLOAD_DIR; + if (!uploadDir) { + throw new Error('UPLOAD_DIR environment variable is not set'); } - return hookResponse; + return uploadDir; } - async handlePostFinishHook(hookRequest: HookRequest) { - const { ID, Size, Storage, MetaData } = hookRequest.Event.Upload; - const filename = MetaData?.filename; - - const resource = await db.resource.create({ - data: { - filename, - fileId: ID, - title: getFilenameWithoutExt(filename), // 使用没有扩展名的标题 - metadata: MetaData || {} - } - }) - await this.addToProcessorPipe(resource) - this.logger.log(`Upload ${ID} (${Size} bytes) is finished.`); + private handleUploadError(identifier: string, error: unknown): void { + const status = this.fileStatuses.get(identifier); + const errorMessage = error instanceof Error ? error.message : String(error); + if (status) { + status.status = 'error'; + status.error = errorMessage; + } + this.logger.error(`Upload error for ${identifier}: ${errorMessage}`); + this.emitter.emit('uploadError', { + identifier, + error: errorMessage, + filename: status?.filename + }); + // Safe cleanup of temporary files + this.chunkManager.cleanup(identifier).catch(cleanupError => + this.logger.error(`Cleanup failed for ${identifier}: ${cleanupError}`) + ); } - async handleTusHook(hookRequest: HookRequest): Promise { - const hookResponse: HookResponse = { - HTTPResponse: { - Headers: {} - } - }; + // Improved directory initialization with better error handling + private async initDirectories(): Promise { try { - if (hookRequest.Type === 'pre-create') { - return this.handlePreCreateHook(hookRequest); - } - if (hookRequest.Type === 'post-finish') { - this.handlePostFinishHook(hookRequest); - } - return hookResponse; - } catch (error: any) { - this.logger.error(`Error handling hook: ${error.message}`); + await fs.mkdir(this.uploadDir, { recursive: true }); + await fs.mkdir(this.tempDir, { recursive: true }); + } catch (error) { + this.logger.error(`Directory initialization failed: ${error}`); throw error; } } - async addToProcessorPipe(resource: Resource): Promise { // 修改返回类型为 Job - const job = await this.fileQueue.add(QueueJobType.FILE_PROCESS, { - resource, - timestamp: Date.now() - }, { - attempts: 3, - removeOnComplete: true, - jobId: resource.id + private configureUploadLimits(): void { + const maxUploads = parseInt(process.env.MAX_CONCURRENT_UPLOADS || '5', 10); + const maxChunkSize = parseInt(process.env.MAX_CHUNK_SIZE || '10485760', 10); + this.MAX_CONCURRENT_UPLOADS = maxUploads; + this.MAX_CHUNK_SIZE = maxChunkSize; + } + // Enhanced input validation + async uploadChunk(chunk: ChunkDto, file: Express.Multer.File, clientId: string): Promise { + // Validate chunk size + if (chunk.currentChunkSize > this.MAX_CHUNK_SIZE) { + throw new Error(`Chunk size exceeds maximum limit of ${this.MAX_CHUNK_SIZE} bytes`); + } + + // Rate limiting and concurrent upload control + await this.controlConcurrentUploads(); + + const { identifier } = chunk; + + const lockAcquired = this.uploadLockService.acquireLock(identifier, clientId); + + if (!lockAcquired) { + throw new Error('Concurrent upload limit reached'); + } + + try { + // Add timeout mechanism + const uploadPromise = this.processChunkUpload(chunk, file); + const timeoutPromise = new Promise((_, reject) => + setTimeout(() => reject(new Error('Upload timeout')), this.UPLOAD_TIMEOUT) + ); + + await Promise.race([uploadPromise, timeoutPromise]); + } catch (error) { + this.handleUploadError(identifier, error); + } finally { + this.uploadLockService.releaseLock(identifier, clientId); + } + } + private async controlConcurrentUploads(): Promise { + const activeUploads = Array.from(this.fileStatuses.values()) + .filter(status => status.status === 'uploading').length; + + if (activeUploads >= this.MAX_CONCURRENT_UPLOADS) { + await new Promise(resolve => setTimeout(resolve, 1000)); // Wait and retry + await this.controlConcurrentUploads(); + } + } + + private async processChunkUpload(chunk: ChunkDto, file: Express.Multer.File): Promise { + const { identifier } = chunk; + if (!this.fileStatuses.has(identifier)) { + await this.initUploadStatusInfo(chunk); + } + const status = this.fileStatuses.get(identifier); + if (!status) { + throw new Error('File status initialization failed'); + } + if (status.chunks.has(chunk.chunkNumber)) { + return; + } + await this.chunkManager.save(chunk, file); + this.updateProgress(chunk); + + if (this.isUploadComplete(identifier)) { + await this.finalizeUpload(chunk); + } + } + + private async initUploadStatusInfo(chunk: ChunkDto): Promise { + const { identifier, filename, totalSize } = chunk; + + // 获取已经上传的chunks + const uploadedChunks = await this.chunkManager.getChunks(identifier); + const uploadedSize = uploadedChunks.length * chunk.currentChunkSize; + + this.emitter.emit('uploadStart', { + identifier, + filename, + totalSize, + resuming: uploadedChunks.length > 0 }); - return job; + + this.fileStatuses.set(identifier, { + identifier, + filename, + totalSize, + uploadedSize, + status: 'uploading', + chunks: new Set(uploadedChunks), // 初始化已上传的chunks + startTime: Date.now(), + lastUpdateTime: Date.now() + }); + } + private updateProgress(chunk: ChunkDto): void { + const status = this.fileStatuses.get(chunk.identifier); + if (!status) return; + // Use more efficient progress calculation + const newUploadedSize = chunk.chunkNumber * chunk.currentChunkSize; + const progressPercentage = Math.min( + Math.round((newUploadedSize / status.totalSize) * 100), + 100 + ); + status.chunks.add(chunk.chunkNumber); + status.uploadedSize = newUploadedSize; + status.lastUpdateTime = Date.now(); + const progress: UploadProgress = { + identifier: chunk.identifier, + percentage: progressPercentage, + uploadedSize: newUploadedSize, + totalSize: status.totalSize, + speed: this.calculateSpeed(status), + remainingTime: this.calculateRemainingTime(status) + }; + status.progress = progress + } + + private calculateRemainingTime(status: UploadStatusInfo): number { + const speed = this.calculateSpeed(status); + if (speed === 0) return 0; + const remainingBytes = status.totalSize - status.uploadedSize; + return Math.ceil(remainingBytes / speed); // Returns seconds remaining + } + private calculateSpeed(status: UploadStatusInfo): number { + const duration = (status.lastUpdateTime - status.startTime) / 1000; // in seconds + return duration > 0 ? Math.round(status.uploadedSize / duration) : 0; // bytes per second + } + private async finalizeUpload(chunk: ChunkDto): Promise { + const { identifier, filename, totalChunks, checksum } = chunk; + const chunkDir = path.join(this.tempDir, identifier); + const finalPath = path.join(this.uploadDir, filename); + try { + await this.chunkManager.merge(chunkDir, finalPath, totalChunks); + // Calculate file hash + const calculatedHash = await calculateFileHash(finalPath); + // Verify file integrity + if (checksum && calculatedHash !== checksum) { + throw new Error('File integrity check failed: Checksums do not match'); + } + await this.chunkManager.cleanup(identifier); + const status = this.fileStatuses.get(identifier); + if (status) { + status.status = 'completed'; + status.hash = calculatedHash; + } + this.emitter.emit('uploadComplete', { + identifier, + filename, + size: status.totalSize, + hash: calculatedHash, + integrityVerified: !checksum || calculatedHash === checksum + }); + } catch (error) { + this.handleUploadError(identifier, error); + } + } + private isUploadComplete(identifier: string): boolean { + const status = this.fileStatuses.get(identifier); + if (!status) return false; + + return status.uploadedSize === status.totalSize; + } + + deleteUploadStatusInfo(identifier: string): boolean { + // Check if the file status exists + const status = this.fileStatuses.get(identifier); + if (!status) { + // If the status doesn't exist, return false + return false; + } + // Check if the upload is still in progress + if (status.status === 'uploading') { + this.logger.warn(`Attempting to delete file status for ongoing upload: ${identifier}`); + return false; + } + // Remove the file status from the map + const deleted = this.fileStatuses.delete(identifier); + if (deleted) { + this.logger.log(`File status deleted for identifier: ${identifier}`); + } + return deleted; + } + checkUploadStatusInfo(identifier: string): UploadStatusInfoDto { + const lockInfo = this.uploadLockService.checkLock(identifier); + const statusInfo = { + ...lockInfo, + ...this.fileStatuses.get(identifier) + }; + return statusInfo || null + } + pauseUpload(identifier: string, clientId: string): void { + const status = this.fileStatuses.get(identifier); + if (status) { + status.status = 'paused'; + this.uploadLockService.releaseLock(identifier, clientId); + } + } + resumeUpload(identifier: string, clientId: string): boolean { + const status = this.fileStatuses.get(identifier); + if (status) { + // Try to reacquire the lock + const lockAcquired = this.uploadLockService.acquireLock(identifier, clientId); + if (lockAcquired) { + status.status = 'uploading'; + return true; + } + } + return false; + } + onUploadEvent( + event: K, + handler: (data: UploadEvent[K]) => void + ): () => void { + this.emitter.on(event, handler); + return () => this.emitter.off(event, handler); } } \ No newline at end of file diff --git a/apps/server/src/utils/event-bus.ts b/apps/server/src/utils/event-bus.ts index f4bc904..8cc9c2e 100644 --- a/apps/server/src/utils/event-bus.ts +++ b/apps/server/src/utils/event-bus.ts @@ -1,5 +1,5 @@ import mitt from 'mitt'; -import { ObjectType, UserProfile, MessageDto } from '@nicestack/common'; +import { ObjectType, UserProfile, MessageDto } from '@nice/common'; export enum CrudOperation { CREATED, UPDATED, diff --git a/apps/server/src/utils/tool.ts b/apps/server/src/utils/tool.ts index 8577f4d..20b6554 100755 --- a/apps/server/src/utils/tool.ts +++ b/apps/server/src/utils/tool.ts @@ -1,7 +1,7 @@ import { createReadStream } from "fs"; import { createInterface } from "readline"; -import { db } from '@nicestack/common'; +import { db } from '@nice/common'; import * as tus from "tus-js-client"; import ExcelJS from 'exceljs'; diff --git a/apps/server/src/validation/index.ts b/apps/server/src/validation/index.ts new file mode 100644 index 0000000..8f49df8 --- /dev/null +++ b/apps/server/src/validation/index.ts @@ -0,0 +1,18 @@ +import { PipeTransform, BadRequestException } from '@nestjs/common'; +import { ZodSchema } from 'zod'; + +export class ZodValidationPipe implements PipeTransform { + constructor(private schema: ZodSchema) { } + + transform(value: unknown) { + try { + const result = this.schema.parse(value); + return result; + } catch (error: any) { + throw new BadRequestException('Validation failed', { + cause: error, + description: error.errors + }); + } + } +} \ No newline at end of file diff --git a/apps/web/package.json b/apps/web/package.json index 06df590..53aaa76 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -27,9 +27,9 @@ "@floating-ui/react": "^0.26.25", "@heroicons/react": "^2.2.0", "@hookform/resolvers": "^3.9.1", - "@nicestack/client": "workspace:^", - "@nicestack/common": "workspace:^", - "@nicestack/iconer": "workspace:^", + "@nice/client": "workspace:^", + "@nice/common": "workspace:^", + "@nice/iconer": "workspace:^", "@tanstack/query-async-storage-persister": "^5.51.9", "@tanstack/react-query": "^5.51.21", "@tanstack/react-query-persist-client": "^5.51.9", @@ -58,7 +58,8 @@ "superjson": "^2.2.1", "tailwind-merge": "^2.6.0", "yjs": "^13.6.20", - "zod": "^3.23.8" + "zod": "^3.23.8", + "uuid": "^10.0.0" }, "devDependencies": { "@eslint/js": "^9.9.0", diff --git a/apps/web/src/app/admin/base-setting/page.tsx b/apps/web/src/app/admin/base-setting/page.tsx index 0387e63..ae1060d 100644 --- a/apps/web/src/app/admin/base-setting/page.tsx +++ b/apps/web/src/app/admin/base-setting/page.tsx @@ -2,7 +2,7 @@ import { AppConfigSlug, BaseSetting, RolePerms, -} from "@nicestack/common"; +} from "@nice/common"; import { useContext, useEffect, useState } from "react"; import { Button, @@ -11,12 +11,12 @@ import { message, theme, } from "antd"; -import { useAppConfig } from "@nicestack/client"; +import { useAppConfig } from "@nice/client"; import { useAuth } from "@web/src/providers/auth-provider"; import FixedHeader from "@web/src/components/layout/fix-header"; import { useForm } from "antd/es/form/Form"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" import { MainLayoutContext } from "../layout"; export default function BaseSettingPage() { diff --git a/apps/web/src/app/admin/layout.tsx b/apps/web/src/app/admin/layout.tsx index 9aad05e..c87aab6 100644 --- a/apps/web/src/app/admin/layout.tsx +++ b/apps/web/src/app/admin/layout.tsx @@ -10,10 +10,10 @@ import { theme } from "antd"; import ResizableSidebar from "@web/src/components/layout/resizable-sidebar"; import SidebarContent from "@web/src/components/layout/sidebar-content"; import UserHeader from "@web/src/components/layout/user-header"; -import { Icon } from "@nicestack/iconer"; +import { Icon } from "@nice/iconer"; import { env } from "@web/src/env"; import RoundedClip from "@web/src/components/svg/rounded-clip"; -import {useTerm} from "@nicestack/client" +import {useTerm} from "@nice/client" export const MainLayoutContext = createContext<{ pageWidth?: number; diff --git a/apps/web/src/app/main/courses/instructor/page.tsx b/apps/web/src/app/main/courses/instructor/page.tsx index be9ef14..bc0292e 100644 --- a/apps/web/src/app/main/courses/instructor/page.tsx +++ b/apps/web/src/app/main/courses/instructor/page.tsx @@ -1,7 +1,7 @@ import { PlusIcon } from "@heroicons/react/24/outline"; import { CourseList } from "@web/src/components/models/course/list/course-list"; import { Button } from "@web/src/components/presentation/element/Button"; -import { api } from "@nicestack/client"; +import { api } from "@nice/client"; import { useState } from "react"; import { CourseCard } from "@web/src/components/models/course/card/CourseCard"; import { useNavigate } from "react-router-dom"; diff --git a/apps/web/src/app/main/courses/student/page.tsx b/apps/web/src/app/main/courses/student/page.tsx index a93ec87..3704128 100644 --- a/apps/web/src/app/main/courses/student/page.tsx +++ b/apps/web/src/app/main/courses/student/page.tsx @@ -1,5 +1,5 @@ import { CourseList } from "@web/src/components/models/course/list/course-list"; -import { api } from "@nicestack/client"; +import { api } from "@nice/client"; import { useState } from "react"; import { CourseCard } from "@web/src/components/models/course/card/CourseCard"; import { useAuth } from "@web/src/providers/auth-provider"; diff --git a/apps/web/src/app/main/home/page.tsx b/apps/web/src/app/main/home/page.tsx index a895b50..577da43 100644 --- a/apps/web/src/app/main/home/page.tsx +++ b/apps/web/src/app/main/home/page.tsx @@ -1,79 +1,82 @@ -import React, { useCallback, useState } from 'react'; -import * as tus from 'tus-js-client'; +import React, { useState } from 'react'; +import { useUpload } from '@nice/client'; // Assuming the previous hook is in this file -const UploadTest: React.FC = () => { - const [progress, setProgress] = useState(0); - const [uploadStatus, setUploadStatus] = useState(''); +const FileUploadComponent: React.FC = () => { + const [selectedFiles, setSelectedFiles] = useState([]); - const handleFileSelect = useCallback((event: React.ChangeEvent) => { - const file = event.target.files?.[0]; - if (!file) return; + const { + upload, + pauseUpload, + resumeUpload, + progress, + errors + } = useUpload({ + // Optional configuration + baseUrl: "http://localhost:3000/upload", + onProgress: (progressInfo) => { + console.log('Upload progress:', progressInfo); + }, + onError: (error) => { + console.error('Upload error:', error); + } + }); - // 创建 tus upload 实例 - const upload = new tus.Upload(file, { - endpoint: 'http://localhost:8080/files', // 替换成你的 NestJS 服务器地址 - retryDelays: [0, 3000, 5000], - metadata: { - filename: file.name, - filetype: file.type - }, - onError: (error) => { - console.error('上传失败:', error); - setUploadStatus('上传失败'); - }, - onProgress: (bytesUploaded, bytesTotal) => { - const percentage = ((bytesUploaded / bytesTotal) * 100).toFixed(2); - setProgress(Number(percentage)); - setUploadStatus('上传中...'); - }, - onSuccess: () => { - setUploadStatus('上传成功!'); - console.log('上传完成:', upload.url); - } - }); + const handleFileSelect = (event: React.ChangeEvent) => { + if (event.target.files) { + setSelectedFiles(Array.from(event.target.files)); + } + }; - // 开始上传 - upload.start(); - }, []); + const handleUpload = async () => { + try { + await upload(selectedFiles); + alert('Upload completed successfully!'); + } catch (error) { + console.error('Upload failed:', error); + } + }; + + const renderProgressBar = (fileName: string) => { + const fileProgress = progress[fileName]; + if (!fileProgress) return null; + + return ( +
+

{fileName}

+ + {fileProgress.percentage.toFixed(2)}% + {errors[fileName] && ( +

+ Error: {errors[fileName].message} +

+ )} +
+ ); + }; return ( -
-

文件上传测试

+
+ - {progress > 0 && ( -
-
上传进度: {progress}%
-
-
-
-
- )} - - {uploadStatus && ( -
- 状态: {uploadStatus} -
- )} +
+

Upload Progress

+ {selectedFiles.map(file => renderProgressBar(file.name))} +
); }; - -export default function HomePage() { - return -} \ No newline at end of file +export default FileUploadComponent; \ No newline at end of file diff --git a/apps/web/src/components/layout/fix-header.tsx b/apps/web/src/components/layout/fix-header.tsx index af578e8..d5aa1b1 100644 --- a/apps/web/src/components/layout/fix-header.tsx +++ b/apps/web/src/components/layout/fix-header.tsx @@ -4,8 +4,8 @@ import React, { ReactNode, useEffect, useState, useRef, CSSProperties } from "re import { SyncOutlined } from "@ant-design/icons"; import Breadcrumb from "../layout/breadcrumb"; import * as Y from "yjs"; -import { stringToColor, YWsProvider } from "@nicestack/common"; -import { lightenColor } from "@nicestack/client" +import { stringToColor, YWsProvider } from "@nice/common"; +import { lightenColor } from "@nice/client" import { useLocalSettings } from "@web/src/hooks/useLocalSetting"; interface FixedHeaderProps { children?: ReactNode; diff --git a/apps/web/src/components/layout/main/nav-data.tsx b/apps/web/src/components/layout/main/nav-data.tsx index d4192b9..1f2bea1 100644 --- a/apps/web/src/components/layout/main/nav-data.tsx +++ b/apps/web/src/components/layout/main/nav-data.tsx @@ -1,4 +1,4 @@ -import { NavItem } from '@nicestack/client'; +import { NavItem } from '@nice/client'; import { HomeIcon, BookOpenIcon, diff --git a/apps/web/src/components/layout/main/side-bar.tsx b/apps/web/src/components/layout/main/side-bar.tsx index a9f6e40..e85cfcf 100644 --- a/apps/web/src/components/layout/main/side-bar.tsx +++ b/apps/web/src/components/layout/main/side-bar.tsx @@ -1,6 +1,6 @@ import { motion } from 'framer-motion'; import { useNavigate, useLocation } from 'react-router-dom'; -import { NavItem } from '@nicestack/client'; +import { NavItem } from '@nice/client'; interface SidebarProps { navItems: Array; diff --git a/apps/web/src/components/layout/sidebar-content.tsx b/apps/web/src/components/layout/sidebar-content.tsx index b85c341..95ab558 100644 --- a/apps/web/src/components/layout/sidebar-content.tsx +++ b/apps/web/src/components/layout/sidebar-content.tsx @@ -1,9 +1,9 @@ import { Avatar, Divider, Dropdown, theme } from "antd"; -import { Icon } from "@nicestack/iconer"; +import { Icon } from "@nice/iconer"; import CollapsibleSection from "../presentation/collapse-section"; import { useAuth } from "@web/src/providers/auth-provider"; -import { RolePerms } from "@nicestack/common"; +import { RolePerms } from "@nice/common"; export default function SidebarContent() { const { logout, user, isAuthenticated, hasSomePermissions } = useAuth(); diff --git a/apps/web/src/components/layout/user-header.tsx b/apps/web/src/components/layout/user-header.tsx index eaca058..a62ff2a 100644 --- a/apps/web/src/components/layout/user-header.tsx +++ b/apps/web/src/components/layout/user-header.tsx @@ -1,6 +1,6 @@ import { Avatar, Button, Dropdown, theme } from "antd"; import { useAuth } from "@web/src/providers/auth-provider"; -import { Icon } from "@nicestack/iconer"; +import { Icon } from "@nice/iconer"; import { useNavigate } from "react-router-dom"; export default function UserHeader() { diff --git a/apps/web/src/components/models/course/card/CourseCard.tsx b/apps/web/src/components/models/course/card/CourseCard.tsx index e66a268..560aabb 100644 --- a/apps/web/src/components/models/course/card/CourseCard.tsx +++ b/apps/web/src/components/models/course/card/CourseCard.tsx @@ -1,4 +1,4 @@ -import { CourseDto } from '@nicestack/common'; +import { CourseDto } from '@nice/common'; import { Card } from '@web/src/components/presentation/container/Card'; import { CourseHeader } from './CourseHeader'; import { CourseStats } from './CourseStats'; diff --git a/apps/web/src/components/models/course/detail/course-detail.tsx b/apps/web/src/components/models/course/detail/course-detail.tsx index e4fa121..f949573 100644 --- a/apps/web/src/components/models/course/detail/course-detail.tsx +++ b/apps/web/src/components/models/course/detail/course-detail.tsx @@ -1,5 +1,5 @@ import { CheckCircleIcon } from '@heroicons/react/24/outline' -import { Course } from "@nicestack/common" +import { Course } from "@nice/common" interface CourseDetailProps { course: Course } diff --git a/apps/web/src/components/models/course/detail/course-syllabus.tsx b/apps/web/src/components/models/course/detail/course-syllabus.tsx index 36af3d1..18ab849 100644 --- a/apps/web/src/components/models/course/detail/course-syllabus.tsx +++ b/apps/web/src/components/models/course/detail/course-syllabus.tsx @@ -1,7 +1,7 @@ import { ChevronDownIcon, ClockIcon, PlayCircleIcon } from '@heroicons/react/24/outline' import { useState } from 'react' -import { Section, SectionDto } from "@nicestack/common" +import { Section, SectionDto } from "@nice/common" interface CourseSyllabusProps { sections: SectionDto[] onLectureClick?: (lectureId: string) => void diff --git a/apps/web/src/components/models/course/list/course-list.tsx b/apps/web/src/components/models/course/list/course-list.tsx index 912954c..2da2963 100644 --- a/apps/web/src/components/models/course/list/course-list.tsx +++ b/apps/web/src/components/models/course/list/course-list.tsx @@ -1,6 +1,6 @@ // CourseList.tsx import { motion } from "framer-motion"; -import { Course, CourseDto } from "@nicestack/common"; +import { Course, CourseDto } from "@nice/common"; import { EmptyState } from "@web/src/components/presentation/space/Empty"; import { Pagination } from "@web/src/components/presentation/element/Pagination"; diff --git a/apps/web/src/components/models/course/manage/CourseBasicForm.tsx b/apps/web/src/components/models/course/manage/CourseBasicForm.tsx index 23fd165..2fa06ef 100644 --- a/apps/web/src/components/models/course/manage/CourseBasicForm.tsx +++ b/apps/web/src/components/models/course/manage/CourseBasicForm.tsx @@ -1,11 +1,11 @@ import { SubmitHandler, useFormContext } from 'react-hook-form'; import { CourseFormData, useCourseEditor } from './CourseEditorContext'; -import { CourseLevel, CourseLevelLabel } from '@nicestack/common'; +import { CourseLevel, CourseLevelLabel } from '@nice/common'; import { FormInput } from '@web/src/components/presentation/form/FormInput'; import { FormSelect } from '@web/src/components/presentation/form/FormSelect'; import { FormArrayField } from '@web/src/components/presentation/form/FormArrayField'; -import { convertToOptions } from '@nicestack/client'; +import { convertToOptions } from '@nice/client'; export function CourseBasicForm() { const { register, formState: { errors }, watch, handleSubmit } = useFormContext(); diff --git a/apps/web/src/components/models/course/manage/CourseEditorContext.tsx b/apps/web/src/components/models/course/manage/CourseEditorContext.tsx index 961c2ab..5e24160 100644 --- a/apps/web/src/components/models/course/manage/CourseEditorContext.tsx +++ b/apps/web/src/components/models/course/manage/CourseEditorContext.tsx @@ -2,8 +2,8 @@ import { createContext, useContext, ReactNode, useEffect } from 'react'; import { useForm, FormProvider, SubmitHandler } from 'react-hook-form'; import { zodResolver } from '@hookform/resolvers/zod'; import { z } from 'zod'; -import { CourseDto, CourseLevel, CourseStatus } from '@nicestack/common'; -import { api, useCourse } from '@nicestack/client'; +import { CourseDto, CourseLevel, CourseStatus } from '@nice/common'; +import { api, useCourse } from '@nice/client'; import toast from 'react-hot-toast'; import { useNavigate } from 'react-router-dom'; // 定义课程表单验证 Schema diff --git a/apps/web/src/components/models/course/manage/CourseEditorHeader.tsx b/apps/web/src/components/models/course/manage/CourseEditorHeader.tsx index 9f93f91..d3e93b9 100644 --- a/apps/web/src/components/models/course/manage/CourseEditorHeader.tsx +++ b/apps/web/src/components/models/course/manage/CourseEditorHeader.tsx @@ -3,7 +3,7 @@ import { SubmitHandler, useFormContext } from 'react-hook-form'; import { useNavigate } from 'react-router-dom'; import { CourseFormData, useCourseEditor } from './CourseEditorContext'; import { Button } from '@web/src/components/presentation/element/Button'; -import { CourseStatus, CourseStatusLabel } from '@nicestack/common'; +import { CourseStatus, CourseStatusLabel } from '@nice/common'; import Tag from '@web/src/components/presentation/element/Tag'; const courseStatusVariant: Record = { [CourseStatus.DRAFT]: 'default', diff --git a/apps/web/src/components/models/course/manage/CourseEditorLayout.tsx b/apps/web/src/components/models/course/manage/CourseEditorLayout.tsx index 63948a5..221a435 100644 --- a/apps/web/src/components/models/course/manage/CourseEditorLayout.tsx +++ b/apps/web/src/components/models/course/manage/CourseEditorLayout.tsx @@ -3,7 +3,7 @@ import { useNavigate } from "react-router-dom"; import { DEFAULT_NAV_ITEMS } from "./navItems"; import CourseEditorHeader from "./CourseEditorHeader"; import { motion } from "framer-motion"; -import { NavItem } from "@nicestack/client" +import { NavItem } from "@nice/client" import CourseEditorSidebar from "./CourseEditorSidebar"; interface CourseEditorLayoutProps { children: ReactNode; diff --git a/apps/web/src/components/models/course/manage/CourseEditorSidebar.tsx b/apps/web/src/components/models/course/manage/CourseEditorSidebar.tsx index 071f63e..143ab15 100644 --- a/apps/web/src/components/models/course/manage/CourseEditorSidebar.tsx +++ b/apps/web/src/components/models/course/manage/CourseEditorSidebar.tsx @@ -1,5 +1,5 @@ import { motion } from 'framer-motion'; -import { NavItem } from "@nicestack/client" +import { NavItem } from "@nice/client" interface CourseSidebarProps { isHovered: boolean; setIsHovered: (value: boolean) => void; diff --git a/apps/web/src/components/models/course/manage/navItems.tsx b/apps/web/src/components/models/course/manage/navItems.tsx index ca88bef..57ac873 100644 --- a/apps/web/src/components/models/course/manage/navItems.tsx +++ b/apps/web/src/components/models/course/manage/navItems.tsx @@ -1,5 +1,5 @@ import { AcademicCapIcon, BookOpenIcon, Cog6ToothIcon, VideoCameraIcon } from '@heroicons/react/24/outline'; -import { NavItem } from '@nicestack/client'; +import { NavItem } from '@nice/client'; export const DEFAULT_NAV_ITEMS: (NavItem & { isCompleted?: boolean })[] = [ { diff --git a/apps/web/src/components/models/department/department-form.tsx b/apps/web/src/components/models/department/department-form.tsx index 328826f..37d8bc3 100644 --- a/apps/web/src/components/models/department/department-form.tsx +++ b/apps/web/src/components/models/department/department-form.tsx @@ -1,6 +1,6 @@ import { Spin, message, Input, InputNumber, Checkbox, Form } from "antd"; -import { useDepartment, api } from "@nicestack/client"; -import { Department, DepartmentDto, ObjectType, mapArrayToObjectArray } from "@nicestack/common"; +import { useDepartment, api } from "@nice/client"; +import { Department, DepartmentDto, ObjectType, mapArrayToObjectArray } from "@nice/common"; import { useContext, useState, useEffect } from "react"; import TermSelect from "../term/term-select"; import DepartmentSelect from "./department-select"; diff --git a/apps/web/src/components/models/department/department-import-drawer.tsx b/apps/web/src/components/models/department/department-import-drawer.tsx index b33faaf..b77383c 100644 --- a/apps/web/src/components/models/department/department-import-drawer.tsx +++ b/apps/web/src/components/models/department/department-import-drawer.tsx @@ -1,7 +1,7 @@ import { Button, Drawer, Form } from "antd"; import React, { useRef, useState } from "react"; import type { ButtonProps, FormInstance } from "antd"; -import { Department } from "@nicestack/common"; +import { Department } from "@nice/common"; import { ExcelImporter } from "../../utils/excel-importer"; import DepartmentSelect from "./department-select"; diff --git a/apps/web/src/components/models/department/department-list.tsx b/apps/web/src/components/models/department/department-list.tsx index f61260c..ee4f24c 100644 --- a/apps/web/src/components/models/department/department-list.tsx +++ b/apps/web/src/components/models/department/department-list.tsx @@ -1,5 +1,5 @@ import React, { useMemo, useContext, useCallback, useState } from "react"; -import { ObjectType } from "@nicestack/common" +import { ObjectType } from "@nice/common" import { ICellRendererParams, SortDirection } from "ag-grid-community"; import { ColDef } from "@ag-grid-community/core"; import AgServerTable from "../../presentation/ag-server-table"; @@ -8,7 +8,7 @@ import { Menu, MenuItem } from "../../presentation/dropdown-menu"; import { DeptEditorContext } from "./dept-editor"; import { CustomCellRendererProps } from "ag-grid-react"; import { message, Tag } from "antd"; -import { CrudOperation, emitDataChange, useDepartment } from "@nicestack/client"; +import { CrudOperation, emitDataChange, useDepartment } from "@nice/client"; export default function DepartmentList() { const { setEditId, setModalOpen, setParentId } = useContext(DeptEditorContext); diff --git a/apps/web/src/components/models/department/department-select.tsx b/apps/web/src/components/models/department/department-select.tsx index 848367b..79cb7f4 100644 --- a/apps/web/src/components/models/department/department-select.tsx +++ b/apps/web/src/components/models/department/department-select.tsx @@ -1,7 +1,7 @@ import { TreeSelect, TreeSelectProps } from "antd"; import React, { useEffect, useState, useCallback } from "react"; -import { getUniqueItems } from "@nicestack/common"; -import { api } from "@nicestack/client" +import { getUniqueItems } from "@nice/common"; +import { api } from "@nice/client" import { DefaultOptionType } from "antd/es/select"; interface DepartmentSelectProps { diff --git a/apps/web/src/components/models/department/dept-editor.tsx b/apps/web/src/components/models/department/dept-editor.tsx index 38a1bf8..1d43a2d 100644 --- a/apps/web/src/components/models/department/dept-editor.tsx +++ b/apps/web/src/components/models/department/dept-editor.tsx @@ -1,6 +1,6 @@ import { createContext, useMemo, useState } from "react"; import { useAuth } from "@web/src/providers/auth-provider"; -import { RolePerms } from "@nicestack/common"; +import { RolePerms } from "@nice/common"; import { Button, FormInstance } from "antd"; import { useForm } from "antd/es/form/Form"; diff --git a/apps/web/src/components/models/department/dept-import-form.tsx b/apps/web/src/components/models/department/dept-import-form.tsx index 3a9a6c7..dc70a23 100644 --- a/apps/web/src/components/models/department/dept-import-form.tsx +++ b/apps/web/src/components/models/department/dept-import-form.tsx @@ -14,7 +14,7 @@ import DepartmentSelect from "../department/department-select"; import { useAuth } from "@web/src/providers/auth-provider"; -import { useTransform } from "@nicestack/client"; +import { useTransform } from "@nice/client"; import ExcelToBase64Uploader from "../../presentation/excel-to-base64-uploader"; import { DeptEditorContext } from "./dept-editor"; diff --git a/apps/web/src/components/models/role/role-editor/assign-list.tsx b/apps/web/src/components/models/role/role-editor/assign-list.tsx index 8e16542..4aadd01 100644 --- a/apps/web/src/components/models/role/role-editor/assign-list.tsx +++ b/apps/web/src/components/models/role/role-editor/assign-list.tsx @@ -1,5 +1,5 @@ import AgServerTable from "@web/src/components/presentation/ag-server-table"; -import { ObjectType } from "@nicestack/common" +import { ObjectType } from "@nice/common" import { ICellRendererParams } from "@ag-grid-community/core"; import { Menu, MenuItem } from "@web/src/components/presentation/dropdown-menu"; import { DeleteOutlined, EllipsisOutlined, PlusOutlined } from "@ant-design/icons"; @@ -9,7 +9,7 @@ import DepartmentSelect from "../../department/department-select"; import { useContext, useEffect } from "react"; import { RoleEditorContext } from "./role-editor"; import { useAuth } from "@web/src/providers/auth-provider"; -import { useRoleMap } from "@nicestack/client" +import { useRoleMap } from "@nice/client" const OpreationRenderer = ({ props }: { props: ICellRendererParams }) => { const { deleteMany } = useRoleMap() return ( diff --git a/apps/web/src/components/models/role/role-editor/role-editor.tsx b/apps/web/src/components/models/role/role-editor/role-editor.tsx index 7ebce68..1d83609 100644 --- a/apps/web/src/components/models/role/role-editor/role-editor.tsx +++ b/apps/web/src/components/models/role/role-editor/role-editor.tsx @@ -4,7 +4,7 @@ import RoleList from "./role-list"; import RoleStaffModal from "./role-staff-modal"; import { useAuth } from "@web/src/providers/auth-provider"; -import { Role, RolePerms } from "@nicestack/common" +import { Role, RolePerms } from "@nice/common" import RoleModal from "./role-modal"; import { FormInstance, useForm } from "antd/es/form/Form"; // 扩展上下文类型以包括 mapStaffIds 和 setMapStaffIds diff --git a/apps/web/src/components/models/role/role-editor/role-form.tsx b/apps/web/src/components/models/role/role-editor/role-form.tsx index 1896caf..db58daf 100644 --- a/apps/web/src/components/models/role/role-editor/role-form.tsx +++ b/apps/web/src/components/models/role/role-editor/role-form.tsx @@ -1,8 +1,8 @@ import { Form, Input, message, Select, Spin } from "antd"; import { useContext, useEffect } from "react"; -import { Role, RolePerms } from "@nicestack/common"; -import { useRole } from "@nicestack/client"; -import { api } from "@nicestack/client"; +import { Role, RolePerms } from "@nice/common"; +import { useRole } from "@nice/client"; +import { api } from "@nice/client"; import { RoleEditorContext } from "./role-editor"; const options: { value: string; label: string }[] = Object.values(RolePerms).map((permission) => ({ value: permission, diff --git a/apps/web/src/components/models/role/role-editor/role-list.tsx b/apps/web/src/components/models/role/role-editor/role-list.tsx index 98a6166..ad3b8cb 100644 --- a/apps/web/src/components/models/role/role-editor/role-list.tsx +++ b/apps/web/src/components/models/role/role-editor/role-list.tsx @@ -1,10 +1,10 @@ -import { Role, RolePerms } from "@nicestack/common" +import { Role, RolePerms } from "@nice/common" import { DeleteOutlined, EditOutlined, EllipsisOutlined, PlusOutlined, UserOutlined } from "@ant-design/icons"; -import { useRole } from "@nicestack/client"; +import { useRole } from "@nice/client"; import { Button, theme } from "antd"; import { useContext, useEffect, useMemo } from "react"; import { RoleEditorContext } from "./role-editor"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" import { useAuth } from "@web/src/providers/auth-provider"; import { Menu, MenuItem } from "@web/src/components/presentation/dropdown-menu"; const OpreationRenderer = ({ data }: { data: Role }) => { diff --git a/apps/web/src/components/models/role/role-editor/role-staff-modal.tsx b/apps/web/src/components/models/role/role-editor/role-staff-modal.tsx index f48a5ce..388ab61 100644 --- a/apps/web/src/components/models/role/role-editor/role-staff-modal.tsx +++ b/apps/web/src/components/models/role/role-editor/role-staff-modal.tsx @@ -1,10 +1,10 @@ -import { useRoleMap } from "@nicestack/client"; +import { useRoleMap } from "@nice/client"; import { Button, message, Modal } from "antd"; import { useContext, useRef, useState } from "react"; import { RoleEditorContext } from "./role-editor"; import StaffTransfer, { StaffTransferRef } from "../../staff/staff-transfer"; -import { ObjectType } from "@nicestack/common"; -import { api } from "@nicestack/client" +import { ObjectType } from "@nice/common"; +import { api } from "@nice/client" export default function RoleStaffModal() { const { domainId, diff --git a/apps/web/src/components/models/role/role-select.tsx b/apps/web/src/components/models/role/role-select.tsx index b74f0b4..8128884 100644 --- a/apps/web/src/components/models/role/role-select.tsx +++ b/apps/web/src/components/models/role/role-select.tsx @@ -1,7 +1,7 @@ import { useState } from 'react'; import { Select, Spin } from 'antd'; import type { SelectProps } from 'antd'; -import { api } from "@nicestack/client" +import { api } from "@nice/client" interface RoleSelectProps { value?: string | string[]; onChange?: (value: string | string[]) => void; diff --git a/apps/web/src/components/models/staff/staff-editor.tsx b/apps/web/src/components/models/staff/staff-editor.tsx index 1b76fa1..285e149 100644 --- a/apps/web/src/components/models/staff/staff-editor.tsx +++ b/apps/web/src/components/models/staff/staff-editor.tsx @@ -1,6 +1,6 @@ import StaffList from "./staff-list"; -import { ObjectType, RolePerms } from "@nicestack/common" -import { Icon } from "@nicestack/iconer" +import { ObjectType, RolePerms } from "@nice/common" +import { Icon } from "@nice/iconer" import StaffModal from "./staff-modal"; import { createContext, useEffect, useMemo, useState } from "react"; import { useAuth } from "@web/src/providers/auth-provider"; diff --git a/apps/web/src/components/models/staff/staff-form.tsx b/apps/web/src/components/models/staff/staff-form.tsx index b7d3c52..1dc4920 100644 --- a/apps/web/src/components/models/staff/staff-form.tsx +++ b/apps/web/src/components/models/staff/staff-form.tsx @@ -1,8 +1,8 @@ import { Button, Form, Input, Spin, Switch, message } from "antd"; import { useContext, useEffect} from "react"; -import { useStaff } from "@nicestack/client"; +import { useStaff } from "@nice/client"; import DepartmentSelect from "../department/department-select"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" import { StaffEditorContext } from "./staff-editor"; import { useAuth } from "@web/src/providers/auth-provider"; export default function StaffForm() { diff --git a/apps/web/src/components/models/staff/staff-list.tsx b/apps/web/src/components/models/staff/staff-list.tsx index 2f295a5..f10018f 100644 --- a/apps/web/src/components/models/staff/staff-list.tsx +++ b/apps/web/src/components/models/staff/staff-list.tsx @@ -1,4 +1,4 @@ -import { Icon } from "@nicestack/iconer"; import { +import { Icon } from "@nice/iconer"; import { DeleteOutlined, EditFilled, EllipsisOutlined, @@ -8,7 +8,7 @@ import { ColDef, ValueGetterParams, } from "@ag-grid-community/core"; -import { ObjectType, StaffRowModel } from "@nicestack/common"; +import { ObjectType, StaffRowModel } from "@nice/common"; import { Menu, MenuItem } from "../../presentation/dropdown-menu"; import AgServerTable from "../../presentation/ag-server-table"; import IdCard from "../../presentation/id-card"; @@ -16,7 +16,7 @@ import { useContext, useEffect, useState } from "react"; import { StaffEditorContext } from "./staff-editor"; import PhoneBook from "../../presentation/phone-book"; import { SortDirection } from "ag-grid-community"; -import { CrudOperation, emitDataChange, useStaff } from "@nicestack/client"; +import { CrudOperation, emitDataChange, useStaff } from "@nice/client"; import { message, Tag } from "antd"; import { CustomCellRendererProps } from "@ag-grid-community/react"; const OpreationRenderer = ({ props }: { props: ICellRendererParams }) => { diff --git a/apps/web/src/components/models/staff/staff-select.tsx b/apps/web/src/components/models/staff/staff-select.tsx index 24e8b16..5881f2a 100644 --- a/apps/web/src/components/models/staff/staff-select.tsx +++ b/apps/web/src/components/models/staff/staff-select.tsx @@ -1,7 +1,7 @@ import { useMemo, useState } from "react"; import { Button, Select, Spin } from "antd"; import type { SelectProps } from "antd"; -import { api } from "@nicestack/client"; +import { api } from "@nice/client"; interface StaffSelectProps { value?: string | string[]; onChange?: (value: string | string[]) => void; diff --git a/apps/web/src/components/models/staff/staff-transfer.tsx b/apps/web/src/components/models/staff/staff-transfer.tsx index a99b259..3708839 100644 --- a/apps/web/src/components/models/staff/staff-transfer.tsx +++ b/apps/web/src/components/models/staff/staff-transfer.tsx @@ -1,5 +1,5 @@ import { Avatar, theme, Transfer, TransferProps } from "antd"; -import { StaffDto } from "@nicestack/common"; +import { StaffDto } from "@nice/common"; import React, { forwardRef, useImperativeHandle, useMemo, useState } from "react"; // Define the ref type diff --git a/apps/web/src/components/models/taxonomy/taxonomy-select.tsx b/apps/web/src/components/models/taxonomy/taxonomy-select.tsx index a6f32bd..b63ad42 100644 --- a/apps/web/src/components/models/taxonomy/taxonomy-select.tsx +++ b/apps/web/src/components/models/taxonomy/taxonomy-select.tsx @@ -1,5 +1,5 @@ import { Button, Select } from "antd"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" import { useEffect, useState } from "react"; // 定义组件的 props 类型 diff --git a/apps/web/src/components/models/term/taxonomy-form.tsx b/apps/web/src/components/models/term/taxonomy-form.tsx index 8332675..3bdeee2 100644 --- a/apps/web/src/components/models/term/taxonomy-form.tsx +++ b/apps/web/src/components/models/term/taxonomy-form.tsx @@ -1,9 +1,9 @@ import { Form, Input, Select } from "antd"; import { useContext, useState } from "react"; -import { ObjectType } from "@nicestack/common"; -import { useTaxonomy } from "@nicestack/client"; +import { ObjectType } from "@nice/common"; +import { useTaxonomy } from "@nice/client"; import { TermEditorContext } from "./term-editor"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" export default function TaxonomyForm() { const { create, update } = useTaxonomy(); const [loading, setLoading] = useState(false); diff --git a/apps/web/src/components/models/term/taxonomy-list.tsx b/apps/web/src/components/models/term/taxonomy-list.tsx index 52822d6..2689cce 100644 --- a/apps/web/src/components/models/term/taxonomy-list.tsx +++ b/apps/web/src/components/models/term/taxonomy-list.tsx @@ -1,5 +1,5 @@ import React, { useContext, useMemo, useEffect, useState } from 'react'; -import { api } from "@nicestack/client"; +import { api } from "@nice/client"; import { TermEditorContext } from './term-editor'; import { Button, theme } from 'antd'; diff --git a/apps/web/src/components/models/term/term-editor.tsx b/apps/web/src/components/models/term/term-editor.tsx index 028396f..bec26ba 100644 --- a/apps/web/src/components/models/term/term-editor.tsx +++ b/apps/web/src/components/models/term/term-editor.tsx @@ -1,6 +1,6 @@ import { createContext, useMemo, useState } from "react"; import { useAuth } from "@web/src/providers/auth-provider"; -import { RolePerms } from "@nicestack/common"; +import { RolePerms } from "@nice/common"; import TaxonomyModal from "./taxonomy-modal"; import TaxonomyList from "./taxonomy-list"; import TermList from "./term-list"; diff --git a/apps/web/src/components/models/term/term-form.tsx b/apps/web/src/components/models/term/term-form.tsx index a114037..59b8594 100644 --- a/apps/web/src/components/models/term/term-form.tsx +++ b/apps/web/src/components/models/term/term-form.tsx @@ -1,9 +1,9 @@ import { Button, Form, Input, message, Checkbox, Spin } from "antd"; import { useContext, useEffect, useRef, useState } from "react"; -import { useTerm } from "@nicestack/client"; +import { useTerm } from "@nice/client"; import TermSelect from "./term-select"; import { TermEditorContext } from "./term-editor"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" export default function TermForm() { const { termForm, setTermModalOpen, taxonomyId, domainId, editId, parentId, setEditId, setParentId } = useContext(TermEditorContext); const { create, update } = useTerm(); // Ensure you have these methods in your hooks diff --git a/apps/web/src/components/models/term/term-import-form.tsx b/apps/web/src/components/models/term/term-import-form.tsx index d4e4ab5..0ff4278 100644 --- a/apps/web/src/components/models/term/term-import-form.tsx +++ b/apps/web/src/components/models/term/term-import-form.tsx @@ -9,7 +9,7 @@ import { useContext, useState, useEffect, useCallback } from "react"; import DepartmentSelect from "../department/department-select"; import TermSelect from "../term/term-select"; import { useAuth } from "@web/src/providers/auth-provider"; -import { useTransform } from "@nicestack/client"; +import { useTransform } from "@nice/client"; import ExcelToBase64Uploader from "../../presentation/excel-to-base64-uploader"; import { TermEditorContext } from "./term-editor"; diff --git a/apps/web/src/components/models/term/term-list.tsx b/apps/web/src/components/models/term/term-list.tsx index 082a781..433e7f8 100644 --- a/apps/web/src/components/models/term/term-list.tsx +++ b/apps/web/src/components/models/term/term-list.tsx @@ -8,12 +8,12 @@ import { ImportOutlined, PlusOutlined, } from "@ant-design/icons"; -import { CrudOperation, emitDataChange, useTerm } from "@nicestack/client"; -import { ObjectType, Term, TreeDataNode } from "@nicestack/common"; +import { CrudOperation, emitDataChange, useTerm } from "@nice/client"; +import { ObjectType, Term, TreeDataNode } from "@nice/common"; import DepartmentSelect from "../department/department-select"; import { TermEditorContext } from "./term-editor"; import { useAuth } from "@web/src/providers/auth-provider"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" import { Menu, MenuItem } from "../../presentation/dropdown-menu"; import AgServerTable from "../../presentation/ag-server-table"; diff --git a/apps/web/src/components/models/term/term-select.tsx b/apps/web/src/components/models/term/term-select.tsx index b5d28a9..045e0e1 100644 --- a/apps/web/src/components/models/term/term-select.tsx +++ b/apps/web/src/components/models/term/term-select.tsx @@ -1,7 +1,7 @@ import { TreeSelect, TreeSelectProps } from "antd"; import React, { useEffect, useState, useCallback, useRef } from "react"; -import { getUniqueItems } from "@nicestack/common"; -import { api } from "@nicestack/client"; +import { getUniqueItems } from "@nice/common"; +import { api } from "@nice/client"; import { DefaultOptionType } from "antd/es/select"; interface TermSelectProps { diff --git a/apps/web/src/components/models/term/term-select_BACKUP.tsx b/apps/web/src/components/models/term/term-select_BACKUP.tsx index efac917..1dc7435 100644 --- a/apps/web/src/components/models/term/term-select_BACKUP.tsx +++ b/apps/web/src/components/models/term/term-select_BACKUP.tsx @@ -1,6 +1,6 @@ import { TreeSelect } from "antd"; import { useEffect, useState, useCallback, useMemo } from "react"; -import { api } from "@nicestack/client" +import { api } from "@nice/client" interface TermSelectProps { defaultValue?: string | string[]; value?: string | string[]; diff --git a/apps/web/src/components/models/term/util.ts b/apps/web/src/components/models/term/util.ts index e9bf8bf..c54910a 100644 --- a/apps/web/src/components/models/term/util.ts +++ b/apps/web/src/components/models/term/util.ts @@ -1,4 +1,4 @@ -import { TreeDataNode } from "@nicestack/common" +import { TreeDataNode } from "@nice/common" export const treeVisitor = ( data: TreeDataNode[], key: React.Key, diff --git a/apps/web/src/components/presentation/ag-server-table.tsx b/apps/web/src/components/presentation/ag-server-table.tsx index 42ea345..d7f13af 100644 --- a/apps/web/src/components/presentation/ag-server-table.tsx +++ b/apps/web/src/components/presentation/ag-server-table.tsx @@ -29,12 +29,12 @@ import { ClipboardModule } from "@ag-grid-enterprise/clipboard"; import { MenuModule } from "@ag-grid-enterprise/menu"; import { ServerSideRowModelModule } from "@ag-grid-enterprise/server-side-row-model"; import { AG_GRID_LOCALE_CH } from "@web/src/locale/ag-grid-locale"; -import { api, CrudOperation, emitDataChange } from "@nicestack/client" +import { api, CrudOperation, emitDataChange } from "@nice/client" import { message } from "antd"; import { useLocation } from "react-router-dom"; import { useAuth } from "@web/src/providers/auth-provider"; -import { EventBus } from "@nicestack/client"; -import { ObjectType } from "@nicestack/common"; +import { EventBus } from "@nice/client"; +import { ObjectType } from "@nice/common"; ModuleRegistry.registerModules([ MasterDetailModule, diff --git a/apps/web/src/components/presentation/collapse-section.tsx b/apps/web/src/components/presentation/collapse-section.tsx index 0dc241d..072e596 100644 --- a/apps/web/src/components/presentation/collapse-section.tsx +++ b/apps/web/src/components/presentation/collapse-section.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import { Icon } from "@nicestack/iconer"; +import { Icon } from "@nice/iconer"; import { theme } from "antd"; import { motion } from "framer-motion"; // Import Framer Motion diff --git a/apps/web/src/components/presentation/dropdown-menu.tsx b/apps/web/src/components/presentation/dropdown-menu.tsx index 09716bb..3442df2 100644 --- a/apps/web/src/components/presentation/dropdown-menu.tsx +++ b/apps/web/src/components/presentation/dropdown-menu.tsx @@ -24,7 +24,7 @@ import { useTypeahead, } from "@floating-ui/react"; import * as React from "react"; -import { Icon } from "@nicestack/iconer"; +import { Icon } from "@nice/iconer"; const MenuContext = React.createContext<{ getItemProps: ( diff --git a/apps/web/src/components/presentation/form/FormArrayField.tsx b/apps/web/src/components/presentation/form/FormArrayField.tsx index 07245bf..93c5b27 100644 --- a/apps/web/src/components/presentation/form/FormArrayField.tsx +++ b/apps/web/src/components/presentation/form/FormArrayField.tsx @@ -1,9 +1,9 @@ import { useFormContext } from 'react-hook-form'; import { PlusIcon, XMarkIcon, Bars3Icon } from '@heroicons/react/24/outline'; import { Reorder } from 'framer-motion'; -import { v4 as uuidv4 } from 'uuid'; import { useState } from 'react'; import FormError from './FormError'; +import { UUIDGenerator } from '@nice/common'; interface ArrayFieldProps { name: string; label: string; @@ -21,7 +21,7 @@ export function FormArrayField({ name, inputProps = {} }: ArrayFieldProps) { const { register, watch, setValue, formState: { errors }, trigger } = useFormContext(); const [items, setItems] = useState(() => - (watch(name) as string[])?.map(value => ({ id: uuidv4(), value })) || [] + (watch(name) as string[])?.map(value => ({ id: UUIDGenerator.generate(), value })) || [] ); const error = errors[name]?.message as string; @@ -76,7 +76,7 @@ export function FormArrayField({ name,