diff --git a/.gitignore b/.gitignore index e64c75db..98aad444 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ node_modules dist package-lock.json -yarn.lock # local env files .env.local diff --git a/server/package.json b/server/package.json index fc06b434..5222380a 100644 --- a/server/package.json +++ b/server/package.json @@ -48,8 +48,7 @@ "reflect-metadata": "^0.1.13", "rxjs": "^7.8.1", "svg-captcha": "^1.4.0", - "typeorm": "^0.3.19", - "xss": "^1.0.15" + "typeorm": "^0.3.19" }, "devDependencies": { "@nestjs/cli": "^10.0.0", diff --git a/server/src/enums/index.ts b/server/src/enums/index.ts index 897a4b90..5dc930dc 100644 --- a/server/src/enums/index.ts +++ b/server/src/enums/index.ts @@ -6,7 +6,7 @@ export enum RECORD_STATUS { PUBLISHED = 'published', // 发布 REMOVED = 'removed', // 删除 FORCE_REMOVED = 'forceRemoved', // 从回收站删除 - COMOPUTETING = 'computing', // 计算中 + COMPUTING = 'computing', // 计算中 FINISHED = 'finished', // 已完成 ERROR = 'error', // 错误 } diff --git a/server/src/guards/session.guard.ts b/server/src/guards/session.guard.ts index 041f97d8..4a4a51f5 100644 --- a/server/src/guards/session.guard.ts +++ b/server/src/guards/session.guard.ts @@ -2,25 +2,16 @@ import { Injectable, CanActivate, ExecutionContext } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { get } from 'lodash'; import { NoPermissionException } from 'src/exceptions/noPermissionException'; -import { SurveyNotFoundException } from 'src/exceptions/surveyNotFoundException'; import { SessionService } from 'src/modules/survey/services/session.service'; -import { SurveyMetaService } from 'src/modules/survey/services/surveyMeta.service'; -import { WorkspaceMemberService } from 'src/modules/workspace/services/workspaceMember.service'; -import { CollaboratorService } from 'src/modules/survey/services/collaborator.service'; - @Injectable() export class SessionGuard implements CanActivate { constructor( private reflector: Reflector, private readonly sessionService: SessionService, - private readonly surveyMetaService: SurveyMetaService, - private readonly workspaceMemberService: WorkspaceMemberService, - private readonly collaboratorService: CollaboratorService, ) {} async canActivate(context: ExecutionContext): Promise { const request = context.switchToHttp().getRequest(); - const user = request.user; const sessionIdKey = this.reflector.get( 'sessionId', context.getHandler(), @@ -31,64 +22,8 @@ export class SessionGuard implements CanActivate { if (!sessionId) { throw new NoPermissionException('没有权限'); } - - const saveSession = await this.sessionService.findOne(sessionId); - - request.saveSession = saveSession; - - const surveyId = saveSession.surveyId; - - const surveyMeta = await this.surveyMetaService.getSurveyById({ surveyId }); - - if (!surveyMeta) { - throw new SurveyNotFoundException('问卷不存在'); - } - - request.surveyMeta = surveyMeta; - - // 兼容老的问卷没有ownerId - if ( - surveyMeta.ownerId === user._id.toString() || - surveyMeta.owner === user.username - ) { - // 问卷的owner,可以访问和操作问卷 - return true; - } - - if (surveyMeta.workspaceId) { - const memberInfo = await this.workspaceMemberService.findOne({ - workspaceId: surveyMeta.workspaceId, - userId: user._id.toString(), - }); - if (!memberInfo) { - throw new NoPermissionException('没有权限'); - } - return true; - } - - const permissions = this.reflector.get( - 'surveyPermission', - context.getHandler(), - ); - - if (!Array.isArray(permissions) || permissions.length === 0) { - throw new NoPermissionException('没有权限'); - } - - const info = await this.collaboratorService.getCollaborator({ - surveyId, - userId: user._id.toString(), - }); - - if (!info) { - throw new NoPermissionException('没有权限'); - } - request.collaborator = info; - if ( - permissions.some((permission) => info.permissions.includes(permission)) - ) { - return true; - } - throw new NoPermissionException('没有权限'); + const sessionInfo = await this.sessionService.findOne(sessionId); + request.sessionInfo = sessionInfo; + return true; } } diff --git a/server/src/models/surveyHistory.entity.ts b/server/src/models/surveyHistory.entity.ts index 4d573c4b..5d7650e4 100644 --- a/server/src/models/surveyHistory.entity.ts +++ b/server/src/models/surveyHistory.entity.ts @@ -19,7 +19,4 @@ export class SurveyHistory extends BaseEntity { username: string; _id: string; }; - - @Column('string') - sessionId: string; } diff --git a/server/src/modules/auth/services/auth.service.ts b/server/src/modules/auth/services/auth.service.ts index 1573ec56..059119c4 100644 --- a/server/src/modules/auth/services/auth.service.ts +++ b/server/src/modules/auth/services/auth.service.ts @@ -35,13 +35,4 @@ export class AuthService { } return user; } - - async expiredCheck(token: string) { - try { - verify(token, this.configService.get('XIAOJU_SURVEY_JWT_SECRET')); - } catch (err) { - return true; - } - return false; - } } diff --git a/server/src/modules/survey/__test/surveyHistory.service.spec.ts b/server/src/modules/survey/__test/surveyHistory.service.spec.ts index 07532feb..78001c63 100644 --- a/server/src/modules/survey/__test/surveyHistory.service.spec.ts +++ b/server/src/modules/survey/__test/surveyHistory.service.spec.ts @@ -83,7 +83,6 @@ describe('SurveyHistoryService', () => { schema, type, user, - sessionId: '', }); expect(spyCreate).toHaveBeenCalledWith({ diff --git a/server/src/modules/survey/controllers/session.controller.ts b/server/src/modules/survey/controllers/session.controller.ts index 33b87a46..88fce79b 100644 --- a/server/src/modules/survey/controllers/session.controller.ts +++ b/server/src/modules/survey/controllers/session.controller.ts @@ -67,7 +67,9 @@ export class SessionController { @Post('/seize') @HttpCode(200) + @UseGuards(SurveyGuard) @UseGuards(SessionGuard) + @SetMetadata('sessionId', 'body.sessionId') @SetMetadata('surveyPermission', [SURVEY_PERMISSION.SURVEY_CONF_MANAGE]) @UseGuards(Authentication) @@ -75,11 +77,11 @@ export class SessionController { @Request() req, ) { - const saveSession = req.saveSession; + const sessionInfo = req.sessionInfo; await this.sessionService.updateSessionToEditing({ - sessionId: saveSession._id.toString(), - surveyId: saveSession.surveyId, + sessionId: sessionInfo._id.toString(), + surveyId: sessionInfo.surveyId, }); return { diff --git a/server/src/modules/survey/services/downloadTask.service.ts b/server/src/modules/survey/services/downloadTask.service.ts index 7f276075..fe81043b 100644 --- a/server/src/modules/survey/services/downloadTask.service.ts +++ b/server/src/modules/survey/services/downloadTask.service.ts @@ -156,7 +156,7 @@ export class DownloadTaskService { { $set: { curStatus: { - status: RECORD_STATUS.COMOPUTETING, + status: RECORD_STATUS.COMPUTING, date: Date.now(), }, }, diff --git a/server/src/modules/survey/template/surveyTemplate/survey/normal.json b/server/src/modules/survey/template/surveyTemplate/survey/normal.json index 606c6e08..5d940a75 100644 --- a/server/src/modules/survey/template/surveyTemplate/survey/normal.json +++ b/server/src/modules/survey/template/surveyTemplate/survey/normal.json @@ -43,7 +43,6 @@ "options": [ { "text": "选项1", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", @@ -52,7 +51,6 @@ }, { "text": "选项2", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", diff --git a/server/src/modules/survey/template/surveyTemplate/survey/register.json b/server/src/modules/survey/template/surveyTemplate/survey/register.json index d7fa4a1b..a5e502d2 100644 --- a/server/src/modules/survey/template/surveyTemplate/survey/register.json +++ b/server/src/modules/survey/template/surveyTemplate/survey/register.json @@ -47,7 +47,6 @@ { "text": "课程1", "hash": "115019", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", @@ -56,7 +55,6 @@ { "text": "课程2", "hash": "115020", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", @@ -65,7 +63,6 @@ { "text": "课程3", "hash": "115021", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", @@ -74,7 +71,6 @@ { "text": "课程4", "hash": "115022", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", diff --git a/server/src/modules/survey/template/surveyTemplate/survey/vote.json b/server/src/modules/survey/template/surveyTemplate/survey/vote.json index 34de3afe..4187c6d2 100644 --- a/server/src/modules/survey/template/surveyTemplate/survey/vote.json +++ b/server/src/modules/survey/template/surveyTemplate/survey/vote.json @@ -46,7 +46,6 @@ "options": [ { "text": "选项1", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", @@ -55,7 +54,6 @@ }, { "text": "选项2", - "imageUrl": "", "others": false, "mustOthers": false, "othersKey": "", diff --git a/server/src/modules/survey/template/surveyTemplate/templateBase.json b/server/src/modules/survey/template/surveyTemplate/templateBase.json index fd3e76e9..70726a46 100644 --- a/server/src/modules/survey/template/surveyTemplate/templateBase.json +++ b/server/src/modules/survey/template/surveyTemplate/templateBase.json @@ -51,6 +51,7 @@ }, "pageConf": [], "logicConf": { - "showLogicConf": [] + "showLogicConf": [], + "jumpLogicConf": [] } } diff --git a/server/src/utils/xss.ts b/server/src/utils/xss.ts deleted file mode 100644 index b7ea7ee5..00000000 --- a/server/src/utils/xss.ts +++ /dev/null @@ -1,53 +0,0 @@ -import xss from 'xss'; - -const myxss = new (xss as any).FilterXSS({ - onIgnoreTagAttr(tag, name, value) { - if (name === 'style' || name === 'class') { - return `${name}="${value}"`; - } - return undefined; - }, - onIgnoreTag(tag, html) { - // 过滤为空,否则不过滤为空 - const re1 = new RegExp('<.+?>', 'g'); - if (re1.test(html)) { - return ''; - } else { - return html; - } - }, -}); - -export const cleanRichTextWithMediaTag = (text) => { - if (!text) { - return text === 0 ? 0 : ''; - } - const html = transformHtmlTag(text) - .replace(//g, '[图片]') - .replace(//g, '[视频]'); - const content = html.replace(/<[^<>]+>/g, '').replace(/ /g, ''); - - return content; -}; - -export function escapeHtml(html) { - return html.replace(//g, '>'); -} -export const transformHtmlTag = (html) => { - if (!html) return ''; - if (typeof html !== 'string') return html + ''; - return html - .replace(html ? /&(?!#?\w+;)/g : /&/g, '&') - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/"/g, '"') - .replace(/'/g, "'") - .replace(/\\\n/g, '\\n'); - //.replace(/ /g, "") -}; - -const filterXSSClone = myxss.process.bind(myxss); - -export const filterXSS = (html) => filterXSSClone(transformHtmlTag(html)); - -export const escapeFilterXSS = (html) => escapeHtml(filterXSS(html)); diff --git a/web/src/common/localstorage.ts b/web/src/common/localstorage.ts new file mode 100644 index 00000000..0b5526c0 --- /dev/null +++ b/web/src/common/localstorage.ts @@ -0,0 +1,58 @@ +interface ExtendedItem { + value: any + expires?: number // 可选属性 +} + +const localstorage = { + // 检查是否支持localStorage + isSupported(): boolean { + return typeof window !== 'undefined' && 'localStorage' in window + }, + + // 设置值 + setItem(key: string, value: any, expires?: number): void { + if (!this.isSupported()) return + + let item: ExtendedItem = { value } + + if (expires !== undefined) { + item = { ...item, expires: Date.now() + expires * 1000 } + } + + const serializedValue = JSON.stringify(item) + + localStorage.setItem(key, serializedValue) + }, + + // 获取值 + getItem(key: string): T | null { + if (!this.isSupported()) return null + + const serializedValue = localStorage.getItem(key) as string + if (!serializedValue) return null + + let item: any + try { + item = JSON.parse(serializedValue) + } catch (e) { + console.error('Error parsing JSON from localStorage') + return null + } + + if (item.expires && item.expires < Date.now()) { + this.removeItem(key) + return null + } + + return item.value as T + }, + + // 移除值 + removeItem(key: string): void { + if (!this.isSupported()) return + + localStorage.removeItem(key) + } +} + +export default localstorage diff --git a/web/src/common/xss.js b/web/src/common/xss.js index 72608018..dea4b2e8 100644 --- a/web/src/common/xss.js +++ b/web/src/common/xss.js @@ -9,7 +9,7 @@ const myxss = new xss.FilterXSS({ }, onIgnoreTag(tag, html) { // 过滤为空,否则不过滤为空 - var re1 = new RegExp('<.+?>', 'g') + const re1 = new RegExp('<.+?>', 'g') if (re1.test(html)) { return '' } else { diff --git a/web/src/management/App.vue b/web/src/management/App.vue index bd20b829..0041c563 100644 --- a/web/src/management/App.vue +++ b/web/src/management/App.vue @@ -3,7 +3,7 @@ - \ No newline at end of file diff --git a/web/src/render/components/QuestionWrapper.vue b/web/src/render/components/QuestionWrapper.vue index 4edd67ca..80a7cebb 100644 --- a/web/src/render/components/QuestionWrapper.vue +++ b/web/src/render/components/QuestionWrapper.vue @@ -9,17 +9,18 @@ > diff --git a/web/src/render/hooks/useQuestionInfo.ts b/web/src/render/hooks/useQuestionInfo.ts index dabf8569..baff1d7d 100644 --- a/web/src/render/hooks/useQuestionInfo.ts +++ b/web/src/render/hooks/useQuestionInfo.ts @@ -4,15 +4,17 @@ export const useQuestionInfo = (field: string) => { const questionstore = useQuestionStore() const questionTitle = cleanRichText(questionstore.questionData[field]?.title) - const getOptionTitle = (value:any) => { - const options = questionstore.questionData[field]?.options || [] - if (value instanceof Array) { - return options - .filter((item:any) => value.includes(item.hash)) - .map((item:any) => cleanRichText(item.text)) - } else { - return options.filter((item:any) => item.hash === value).map((item:any) => cleanRichText(item.text)) - } + const getOptionTitle = (value: any) => { + const options = questionstore.questionData[field]?.options || [] + if (value instanceof Array) { + return options + .filter((item: any) => value.includes(item.hash)) + .map((item: any) => cleanRichText(item.text)) + } else { + return options + .filter((item: any) => item.hash === value) + .map((item: any) => cleanRichText(item.text)) } + } return { questionTitle, getOptionTitle } } diff --git a/web/src/render/index.html b/web/src/render/index.html index cd6aaa9b..6358a23d 100644 --- a/web/src/render/index.html +++ b/web/src/render/index.html @@ -9,9 +9,9 @@