diff --git a/.vscode/settings.json b/.vscode/settings.json index a2c76e4..d955e5f 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,6 @@ { - "marscode.chatLanguage": "cn" + "marscode.chatLanguage": "cn", + "marscode.codeCompletionPro": { + "enableCodeCompletionPro": true + } } \ No newline at end of file diff --git a/apps/server/src/models/share-code/share-code.router.ts b/apps/server/src/models/share-code/share-code.router.ts index a813ef3..946e01f 100644 --- a/apps/server/src/models/share-code/share-code.router.ts +++ b/apps/server/src/models/share-code/share-code.router.ts @@ -11,9 +11,9 @@ export class ShareCodeRouter { router = this.trpc.router({ generateShareCode: this.trpc.procedure - .input(z.object({ fileId: z.string() })) + .input(z.object({ fileId: z.string(), expiresAt: z.date(), canUseTimes: z.number() })) .mutation(async ({ input }) => { - return this.shareCodeService.generateShareCode(input.fileId); + return this.shareCodeService.generateShareCode(input.fileId, input.expiresAt, input.canUseTimes); }), validateAndUseCode: this.trpc.procedure .input(z.object({ code: z.string() })) @@ -41,9 +41,9 @@ export class ShareCodeRouter { return this.shareCodeService.getFileByShareCode(input.code); }), generateShareCodeByFileId: this.trpc.procedure - .input(z.object({ fileId: z.string() })) + .input(z.object({ fileId: z.string(), expiresAt: z.date(), canUseTimes: z.number() })) .mutation(async ({ input }) => { - return this.shareCodeService.generateShareCodeByFileId(input.fileId); + return this.shareCodeService.generateShareCodeByFileId(input.fileId, input.expiresAt, input.canUseTimes); }), }); } \ No newline at end of file diff --git a/apps/server/src/models/share-code/share-code.service.ts b/apps/server/src/models/share-code/share-code.service.ts index 3adf097..346f1ab 100755 --- a/apps/server/src/models/share-code/share-code.service.ts +++ b/apps/server/src/models/share-code/share-code.service.ts @@ -5,8 +5,11 @@ import { Cron, CronExpression } from '@nestjs/schedule'; import { ResourceService } from '@server/models/resource/resource.service'; import * as fs from 'fs' import * as path from 'path' - - +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import timezone from 'dayjs/plugin/timezone'; +dayjs.extend(utc); +dayjs.extend(timezone); export interface ShareCode { id: string; code: string; @@ -15,10 +18,12 @@ export interface ShareCode { expiresAt: Date; isUsed: boolean; fileName?: string | null; + canUseTimes: number | null; } export interface GenerateShareCodeResponse { code: string; expiresAt: Date; + canUseTimes: number; } interface ResourceMeta { @@ -40,6 +45,8 @@ export class ShareCodeService { async generateShareCode( fileId: string, + expiresAt: Date, + canUseTimes: number, fileName?: string, ): Promise { try { @@ -55,8 +62,6 @@ export class ShareCodeService { // 生成分享码 const code = this.generateCode(); - const expiresAt = new Date(Date.now() + 24 * 60 * 60 * 1000); // 24小时后过期 - //const expiresAt = new Date(Date.now() + 10 * 1000); // 24小时后过期 // 查找是否已有分享码记录 const existingShareCode = await db.shareCode.findUnique({ where: { fileId }, @@ -69,6 +74,7 @@ export class ShareCodeService { data: { code, expiresAt, + canUseTimes, isUsed: false, // 只在没有现有文件名且提供了新文件名时才更新文件名 ...(fileName && !existingShareCode.fileName ? { fileName } : {}), @@ -81,15 +87,17 @@ export class ShareCodeService { code, fileId, expiresAt, + canUseTimes, isUsed: false, fileName: fileName || null, }, }); } - this.logger.log(`Generated share code ${code} for file ${fileId}`); + this.logger.log(`Generated share code ${code} for file ${fileId} canUseTimes: ${canUseTimes}`); return { code, expiresAt, + canUseTimes, }; } catch (error) { this.logger.error('Failed to generate share code', error); @@ -106,26 +114,26 @@ export class ShareCodeService { where: { code, isUsed: false, - expiresAt: { gt: new Date() }, + expiresAt: { gt: dayjs().tz('Asia/Shanghai').toDate() }, }, }); - + if (shareCode.canUseTimes <= 0) { + this.logger.log('分享码已使用次数超过限制'); + return null; + } + //更新已使用次数 + await db.shareCode.update({ + where: { id: shareCode.id }, + data: { canUseTimes: shareCode.canUseTimes - 1 }, + }); this.logger.log('查询结果:', shareCode); if (!shareCode) { this.logger.log('分享码无效或已过期'); return null; } - - // 标记分享码为已使用 - // await db.shareCode.update({ - // where: { id: shareCode.id }, - // data: { isUsed: true }, - // }); - // 记录使用日志 this.logger.log(`Share code ${code} used for file ${shareCode.fileId}`); - // 返回完整的分享码信息,包括文件名 return shareCode; } catch (error) { @@ -141,7 +149,11 @@ export class ShareCodeService { try { const shareCodes = await db.shareCode.findMany({ where: { - OR: [{ expiresAt: { lt: new Date() } }, { isUsed: true }], + OR: [ + { expiresAt: { lt: dayjs().tz('Asia/Shanghai').toDate() } }, + { isUsed: true }, + { canUseTimes: { lte: 0 } } + ], } }) this.logger.log('需要清理的分享码:', shareCodes); @@ -222,7 +234,7 @@ export class ShareCodeService { where: { fileId, isUsed: false, - expiresAt: { gt: new Date() }, + expiresAt: { gt: dayjs().tz('Asia/Shanghai').toDate() }, }, }); @@ -249,7 +261,6 @@ export class ShareCodeService { // 根据分享码获取文件 async getFileByShareCode(code: string) { this.logger.log('收到验证分享码请求,code:', code); - const shareCode = await this.validateAndUseCode(code); this.logger.log('验证分享码结果:', shareCode); @@ -275,6 +286,7 @@ export class ShareCodeService { code: shareCode.code, expiresAt: shareCode.expiresAt, url: fileUrl, + canUseTimes: shareCode.canUseTimes - 1, }; this.logger.log('返回给前端的数据:', response); // 添加日志 @@ -282,10 +294,10 @@ export class ShareCodeService { } // 根据文件ID生成分享码 - async generateShareCodeByFileId(fileId: string) { + async generateShareCodeByFileId(fileId: string, expiresAt: Date, canUseTimes: number) { try { this.logger.log('收到生成分享码请求,fileId:', fileId); - const result = await this.generateShareCode(fileId); + const result = await this.generateShareCode(fileId, expiresAt, canUseTimes); this.logger.log('生成分享码结果:', result); return result; } catch (error) { diff --git a/apps/web/package.json b/apps/web/package.json index af89247..17fae24 100755 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -62,6 +62,7 @@ "quill": "2.0.3", "react": "18.2.0", "react-dom": "18.2.0", + "react-fast-marquee": "^1.6.5", "react-hook-form": "^7.54.2", "react-hot-toast": "^2.4.1", "react-player": "^2.16.0", diff --git a/apps/web/public/logo.svg b/apps/web/public/logo.svg index 3b6b9f3..101f9b4 100644 --- a/apps/web/public/logo.svg +++ b/apps/web/public/logo.svg @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + + + + + diff --git a/apps/web/public/vite.svg b/apps/web/public/vite.svg index 3b6b9f3..101f9b4 100644 --- a/apps/web/public/vite.svg +++ b/apps/web/public/vite.svg @@ -1 +1,10 @@ - \ No newline at end of file + + + + + + + + + + diff --git a/apps/web/src/app/admin/deptsettingpage/page.tsx b/apps/web/src/app/admin/deptsettingpage/page.tsx index aa56901..7715eb8 100755 --- a/apps/web/src/app/admin/deptsettingpage/page.tsx +++ b/apps/web/src/app/admin/deptsettingpage/page.tsx @@ -1,7 +1,7 @@ import { ShareCodeGenerator } from "../sharecode/sharecodegenerator"; import { ShareCodeValidator } from "../sharecode/sharecodevalidator"; -import { message, Tabs, Form, Spin } from "antd"; +import { message, Tabs, Form, Spin, Alert } from "antd"; import { env } from '../../../env' import { TusUploader } from "@web/src/components/common/uploader/TusUploader"; import { useState } from "react"; @@ -10,30 +10,20 @@ export default function DeptSettingPage() { const [form] = Form.useForm(); const uploadFileId = Form.useWatch(["file"], form)?.[0] const [isGetingFileId, setIsGetingFileId] = useState(false); - // 处理分享码生成成功 const handleShareSuccess = (code: string) => { message.success('分享码生成成功:' + code); - // 可以在这里添加其他逻辑,比如保存到历史记录 } - // 处理分享码验证成功 const handleValidSuccess = async (fileUrl: string, fileName: string) => { setIsGetingFileId(true); try { - // 构建下载URL(包含文件名参数) - console.log('文件url:', fileUrl); const downloadUrl = `http://${env.SERVER_IP}:${env.FILE_PORT}/uploads/${fileUrl}`; - console.log('下载URL:', downloadUrl); const link = document.createElement('a'); link.href = downloadUrl; - - // 直接使用传入的 fileName link.download = fileName; - link.target = '_blank'; // 在新标签页中打开 - // 触发下载 + link.target = '_blank'; document.body.appendChild(link); link.click(); document.body.removeChild(link); - message.success('文件下载开始'); } catch (error) { console.error('下载失败:', error); @@ -49,35 +39,30 @@ export default function DeptSettingPage() { { isGetingFileId ? () : - (
- 文件分享中心 + (
+
+ + 烽火快传 +
- {/* 文件上传区域 */} -
- 第一步:上传文件 - {/* 如果没有已上传文件,显示上传区域 */} +
- - {/* 生成分享码区域 */}
- 第二步:生成分享码
- - {/* 使用分享码区域 */}
使用分享码下载文件 diff --git a/apps/web/src/app/admin/sharecode/sharecodegenerator.tsx b/apps/web/src/app/admin/sharecode/sharecodegenerator.tsx index efc4b87..e20a02e 100755 --- a/apps/web/src/app/admin/sharecode/sharecodegenerator.tsx +++ b/apps/web/src/app/admin/sharecode/sharecodegenerator.tsx @@ -1,11 +1,14 @@ - import React, { useEffect, useState } from 'react'; -import { Button, message } from 'antd'; +import { Button, DatePicker, Form, message, Select } from 'antd'; import { CopyOutlined } from '@ant-design/icons'; -import { env } from '../../../env' import { useQueryClient } from '@tanstack/react-query'; import { getQueryKey } from '@trpc/react-query'; import { api } from '@nice/client'; +import dayjs from 'dayjs'; +import utc from 'dayjs/plugin/utc'; +import timezone from 'dayjs/plugin/timezone'; +dayjs.extend(utc); +dayjs.extend(timezone); interface ShareCodeGeneratorProps { fileId: string; onSuccess?: (code: string) => void; @@ -14,6 +17,7 @@ interface ShareCodeGeneratorProps { interface ShareCodeResponse { code?: string; expiresAt?: Date; + canUseTimes?: number; } export const ShareCodeGenerator: React.FC = ({ fileId, @@ -23,10 +27,12 @@ export const ShareCodeGenerator: React.FC = ({ const [loading, setLoading] = useState(false); const [shareCode, setShareCode] = useState(''); const [expiresAt, setExpiresAt] = useState(null); + const [canUseTimes, setCanUseTimes] = useState(null); const queryClient = useQueryClient(); const queryKey = getQueryKey(api.term); const [isGenerate, setIsGenerate] = useState(false); const [currentFileId, setCurrentFileId] = useState(''); + const [form] = Form.useForm(); const generateShareCode = api.shareCode.generateShareCodeByFileId.useMutation({ onSuccess: (data) => { queryClient.invalidateQueries({ queryKey }); @@ -46,12 +52,17 @@ export const ShareCodeGenerator: React.FC = ({ setLoading(true); console.log('开始生成分享码,fileId:', fileId, 'fileName:', fileName); try { - const data: ShareCodeResponse = await generateShareCode.mutateAsync({ fileId }); - console.log('生成分享码结果:', data); + const data: ShareCodeResponse = await generateShareCode.mutateAsync({ + fileId, + expiresAt: form.getFieldsValue()?.expiresAt ? form.getFieldsValue().expiresAt.toDate() : dayjs().add(1, 'day').tz('Asia/Shanghai').toDate(), + canUseTimes: form.getFieldsValue()?.canUseTimes ? form.getFieldsValue().canUseTimes : 10, + }); + console.log('data', data) setShareCode(data.code); setIsGenerate(true); - setExpiresAt(data.expiresAt ? new Date(data.expiresAt) : null); onSuccess?.(data.code); + setExpiresAt(dayjs(data.expiresAt).tz('Asia/Shanghai').toDate()); + setCanUseTimes(data.canUseTimes); //message.success('分享码生成成功'); } catch (error) { console.error('生成分享码错误:', error); @@ -73,30 +84,61 @@ export const ShareCodeGenerator: React.FC = ({ return Promise.resolve(); } } - - // 组件使用 const handleCopy = (code) => { copyToClipboard(code) .then(() => console.log('复制成功')) .catch(() => console.error('复制失败')); }; + useEffect(() => { + const date = dayjs().add(1, 'day').tz('Asia/Shanghai'); + form.setFieldsValue({ + expiresAt: date, + canUseTimes: 10 + }); + }, [form]); + useEffect(()=>{ + if (fileId) { + generateCode() + } + }, [fileId]) return (
-
- {/* 添加调试信息 */} - 文件ID: {fileId ? fileId : '未选择文件'} +
+ 文件ID: {fileId ? fileId : '暂未上传文件'}
- {!isGenerate ? ( - + <> +
+
+ + {"分享码的有效期"} + + + + + + {"分享码的使用次数"} + + + setCode(e.target.value.toUpperCase())} - placeholder="请输入分享码" - maxLength={8} - onPressEnter={validateCode} - /> - -
+ <> +
+ setCode(e.target.value.toUpperCase())} + placeholder="请输入分享码" + maxLength={8} + onPressEnter={validateCode} + /> + +
+ { + !loading && result && ( +
+ {`分享码:${result?.code ? result.code : ''}`} + {`文件名:${result?.fileName ? result.fileName : ''}`} + {`过期时间:${result?.expiresAt ? dayjs(result.expiresAt).tz('Asia/Shanghai').toDate().toLocaleString() : ''}`} + {`剩余使用次数:${result?.canUseTimes ? result.canUseTimes : ''}`} +
+ ) + } + ); }; \ No newline at end of file diff --git a/apps/web/src/components/common/uploader/TusUploader.tsx b/apps/web/src/components/common/uploader/TusUploader.tsx index 876c151..8a5a160 100755 --- a/apps/web/src/components/common/uploader/TusUploader.tsx +++ b/apps/web/src/components/common/uploader/TusUploader.tsx @@ -4,7 +4,7 @@ import { CheckCircleOutlined, DeleteOutlined, } from "@ant-design/icons"; -import { Upload, Progress, Button } from "antd"; +import { Upload, Progress, Button, message } from "antd"; import { useTusUpload } from "@web/src/hooks/useTusUpload"; import toast from "react-hot-toast"; export interface TusUploaderProps { @@ -12,9 +12,9 @@ export interface TusUploaderProps { onChange?: (value: string[]) => void; multiple?: boolean; allowTypes?: string[]; - style?:string - icon?:ReactNode, - description?:string + style?: string + icon?: ReactNode, + description?: string } interface UploadingFile { @@ -30,7 +30,7 @@ export const TusUploader = ({ onChange, multiple = true, allowTypes = undefined, - style="", + style = "", icon = , description = "点击或拖拽文件到此区域进行上传", }: TusUploaderProps) => { @@ -67,8 +67,14 @@ export const TusUploader = ({ const handleBeforeUpload = useCallback( (file: File) => { + console.log('File object:',file) + // 判断是否为文件 + if (!file.type) { + message.error('请选择正确的文件'); + return Upload.LIST_IGNORE; + } if (allowTypes && !allowTypes.includes(file.type)) { - toast.error(`文件类型 ${file.type} 不在允许范围内`); + message.error(`文件类型 ${file.type} 不在允许范围内`); return Upload.LIST_IGNORE; // 使用 antd 的官方阻止方式 } const fileKey = `${file.name}-${Date.now()}`; @@ -150,7 +156,9 @@ export const TusUploader = ({ name="files" multiple={multiple} showUploadList={false} - beforeUpload={handleBeforeUpload}> + beforeUpload={handleBeforeUpload} + directory={false} + >

{icon}

@@ -180,10 +188,10 @@ export const TusUploader = ({ file.status === "done" ? 100 : Math.round( - uploadProgress?.[ - file.fileKey! - ] || 0 - ) + uploadProgress?.[ + file.fileKey! + ] || 0 + ) } status={ file.status === "error" diff --git a/config/nginx/conf.d/web.conf b/config/nginx/conf.d/web.conf index 8f9683a..8f5da09 100755 --- a/config/nginx/conf.d/web.conf +++ b/config/nginx/conf.d/web.conf @@ -100,7 +100,7 @@ server { # 仅供内部使用 internal; # 代理到认证服务 - proxy_pass http://192.168.43.206:3006/auth/file; + proxy_pass http://192.168.43.206:3001/auth/file; # 请求优化:不传递请求体 proxy_pass_request_body off; diff --git a/packages/common/prisma/schema.prisma b/packages/common/prisma/schema.prisma index 5a8f27f..4e0b275 100755 --- a/packages/common/prisma/schema.prisma +++ b/packages/common/prisma/schema.prisma @@ -89,11 +89,10 @@ model Staff { officerId String? @map("officer_id") // watchedPost Post[] @relation("post_watch_staff") - visits Visit[] - posts Post[] + visits Visit[] + posts Post[] - - learningPosts Post[] @relation("post_student") + learningPosts Post[] @relation("post_student") sentMsgs Message[] @relation("message_sender") receivedMsgs Message[] @relation("message_receiver") registerToken String? @@ -113,7 +112,7 @@ model Department { id String @id @default(cuid()) name String order Float? - posts Post[] @relation("post_dept") + posts Post[] @relation("post_dept") ancestors DeptAncestry[] @relation("DescendantToAncestor") descendants DeptAncestry[] @relation("AncestorToDescendant") parentId String? @map("parent_id") @@ -191,45 +190,45 @@ model AppConfig { model Post { // 字符串类型字段 - id String @id @default(cuid()) // 帖子唯一标识,使用 cuid() 生成默认值 - type String? // Post类型,课程、章节、小节、讨论都用Post实现 - level String? - state String? - title String? // 帖子标题,可为空 - subTitle String? - content String? // 帖子内容,可为空 - important Boolean? //是否重要/精选/突出 - domainId String? @map("domain_id") - terms Term[] @relation("post_term") - order Float? @default(0) @map("order") - duration Int? - rating Int? @default(0) - students Staff[] @relation("post_student") - depts Department[] @relation("post_dept") - views Int @default(0) @map("views") - hates Int @default(0) @map("hates") - likes Int @default(0) @map("likes") + id String @id @default(cuid()) // 帖子唯一标识,使用 cuid() 生成默认值 + type String? // Post类型,课程、章节、小节、讨论都用Post实现 + level String? + state String? + title String? // 帖子标题,可为空 + subTitle String? + content String? // 帖子内容,可为空 + important Boolean? //是否重要/精选/突出 + domainId String? @map("domain_id") + terms Term[] @relation("post_term") + order Float? @default(0) @map("order") + duration Int? + rating Int? @default(0) + students Staff[] @relation("post_student") + depts Department[] @relation("post_dept") + views Int @default(0) @map("views") + hates Int @default(0) @map("hates") + likes Int @default(0) @map("likes") // 索引 // 日期时间类型字段 - createdAt DateTime @default(now()) @map("created_at") - publishedAt DateTime? @map("published_at") // 发布时间 - updatedAt DateTime @map("updated_at") - deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 - instructors PostInstructor[] + createdAt DateTime @default(now()) @map("created_at") + publishedAt DateTime? @map("published_at") // 发布时间 + updatedAt DateTime @map("updated_at") + deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 + instructors PostInstructor[] // 关系类型字段 - authorId String? @map("author_id") - author Staff? @relation(fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型 - enrollments Enrollment[] // 学生报名记录 - visits Visit[] // 访问记录,关联 Visit 模型 - parentId String? @map("parent_id") - parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 - children Post[] @relation("PostChildren") // 子级帖子列表,关联 Post 模型 - hasChildren Boolean? @default(false) @map("has_children") + authorId String? @map("author_id") + author Staff? @relation(fields: [authorId], references: [id]) // 帖子作者,关联 Staff 模型 + enrollments Enrollment[] // 学生报名记录 + visits Visit[] // 访问记录,关联 Visit 模型 + parentId String? @map("parent_id") + parent Post? @relation("PostChildren", fields: [parentId], references: [id]) // 父级帖子,关联 Post 模型 + children Post[] @relation("PostChildren") // 子级帖子列表,关联 Post 模型 + hasChildren Boolean? @default(false) @map("has_children") // 闭包表关系 - ancestors PostAncestry[] @relation("DescendantPosts") - descendants PostAncestry[] @relation("AncestorPosts") - resources Resource[] // 附件列表 - meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int + ancestors PostAncestry[] @relation("DescendantPosts") + descendants PostAncestry[] @relation("AncestorPosts") + resources Resource[] // 附件列表 + meta Json? // 封面url 视频url objectives具体的学习目标 rating评分Int // 索引 @@index([type, domainId]) @@ -247,12 +246,13 @@ model Post { } model PostAncestry { - id String @id @default(cuid()) - ancestorId String? @map("ancestor_id") - descendantId String @map("descendant_id") - relDepth Int @map("rel_depth") - ancestor Post? @relation("AncestorPosts", fields: [ancestorId], references: [id]) - descendant Post @relation("DescendantPosts", fields: [descendantId], references: [id]) + id String @id @default(cuid()) + ancestorId String? @map("ancestor_id") + descendantId String @map("descendant_id") + relDepth Int @map("rel_depth") + ancestor Post? @relation("AncestorPosts", fields: [ancestorId], references: [id]) + descendant Post @relation("DescendantPosts", fields: [descendantId], references: [id]) + // 复合索引优化 // 索引建议 @@index([ancestorId]) // 针对祖先的查询 @@ -276,23 +276,24 @@ model Message { visits Visit[] createdAt DateTime @default(now()) @map("created_at") updatedAt DateTime? @updatedAt @map("updated_at") + @@index([type, createdAt]) @@map("message") } model Visit { - id String @id @default(cuid()) @map("id") + id String @id @default(cuid()) @map("id") type String? - views Int @default(1) @map("views") + views Int @default(1) @map("views") // sourceIP String? @map("source_ip") // 关联关系 visitorId String? @map("visitor_id") visitor Staff? @relation(fields: [visitorId], references: [id]) - postId String? @map("post_id") - post Post? @relation(fields: [postId], references: [id]) - message Message? @relation(fields: [messageId], references: [id]) - messageId String? @map("message_id") - lectureId String? @map("lecture_id") // 课时ID + postId String? @map("post_id") + post Post? @relation(fields: [postId], references: [id]) + message Message? @relation(fields: [messageId], references: [id]) + messageId String? @map("message_id") + lectureId String? @map("lecture_id") // 课时ID createdAt DateTime @default(now()) @map("created_at") // 创建时间 updatedAt DateTime @updatedAt @map("updated_at") // 更新时间 deletedAt DateTime? @map("deleted_at") // 删除时间,可为空 @@ -403,14 +404,16 @@ model NodeEdge { @@index([targetId]) @@map("node_edge") } + model Animal { - id String @id @default(cuid()) - name String - age Int - gender Boolean + id String @id @default(cuid()) + name String + age Int + gender Boolean personId String? - person Person? @relation(fields: [personId], references: [id]) + person Person? @relation(fields: [personId], references: [id]) } + model Person { id String @id @default(cuid()) name String @@ -418,16 +421,17 @@ model Person { gender Boolean animals Animal[] } -model ShareCode { - id String @id @default(cuid()) - code String? @unique - fileId String? @unique - createdAt DateTime @default(now()) - expiresAt DateTime? @map("expires_at") - isUsed Boolean? @default(false) - fileName String? @map("file_name") +model ShareCode { + id String @id @default(cuid()) + code String? @unique + fileId String? @unique + createdAt DateTime @default(now()) + expiresAt DateTime? @map("expires_at") + isUsed Boolean? @default(false) + fileName String? @map("file_name") + canUseTimes Int? @@index([code]) @@index([fileId]) @@index([expiresAt]) -} \ No newline at end of file +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e925877..e827539 100755 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -380,6 +380,9 @@ importers: react-dom: specifier: 18.2.0 version: 18.2.0(react@18.2.0) + react-fast-marquee: + specifier: ^1.6.5 + version: 1.6.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0) react-hook-form: specifier: ^7.54.2 version: 7.54.2(react@18.2.0) @@ -6657,6 +6660,12 @@ packages: react-fast-compare@3.2.2: resolution: {integrity: sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ==} + react-fast-marquee@1.6.5: + resolution: {integrity: sha512-swDnPqrT2XISAih0o74zQVE2wQJFMvkx+9VZXYYNSLb/CUcAzU9pNj637Ar2+hyRw6b4tP6xh4GQZip2ZCpQpg==} + peerDependencies: + react: '>= 16.8.0 || ^18.0.0' + react-dom: '>= 16.8.0 || ^18.0.0' + react-hook-form@7.54.2: resolution: {integrity: sha512-eHpAUgUjWbZocoQYUHposymRb4ZP6d0uwUnooL2uOybA9/3tPUvoAKqEWK1WaSiTxxOfTpffNZP7QwlnM3/gEg==} engines: {node: '>=18.0.0'} @@ -14797,6 +14806,11 @@ snapshots: react-fast-compare@3.2.2: {} + react-fast-marquee@1.6.5(react-dom@18.2.0(react@18.2.0))(react@18.2.0): + dependencies: + react: 18.2.0 + react-dom: 18.2.0(react@18.2.0) + react-hook-form@7.54.2(react@18.2.0): dependencies: react: 18.2.0 diff --git a/web-dist.zip b/web-dist.zip index 660c635..912720a 100644 Binary files a/web-dist.zip and b/web-dist.zip differ