From 119a2cf36f53194da375b155df83c514529630bc Mon Sep 17 00:00:00 2001 From: godo Date: Wed, 27 Nov 2024 09:04:45 +0800 Subject: [PATCH] change file encoding --- frontend/public/markdown/scripts/index.js | 46 ++++++- frontend/public/text/js/base.js | 6 +- frontend/src/components/window/IframeFile.vue | 127 +++++++++--------- frontend/src/system/index.ts | 18 +-- godo/files/os.go | 9 ++ godo/files/pwdfile.go | 42 +++--- godo/libs/msg.go | 3 + packages/word/src/main.ts | 79 +++++++---- 8 files changed, 206 insertions(+), 124 deletions(-) diff --git a/frontend/public/markdown/scripts/index.js b/frontend/public/markdown/scripts/index.js index f8c1c3f..6452199 100644 --- a/frontend/public/markdown/scripts/index.js +++ b/frontend/public/markdown/scripts/index.js @@ -344,19 +344,55 @@ const debouncedHandleKeyDown = (event) => { saveData(); } }; +function isBase64(str) { + if (str === '' || str.trim() === '') { + return false; + } + try { + return btoa(atob(str)) == str; + } catch (err) { + return false; + } +} +function decodeBase64(base64String) { + // 将Base64字符串分成每64个字符一组 + const padding = (base64String.length % 4) === 0 ? 0 : 4 - (base64String.length % 4); + base64String += '='.repeat(padding); + + // 使用atob()函数解码Base64字符串 + const binaryString = atob(base64String); + + // 将二进制字符串转换为TypedArray + const bytes = new Uint8Array(binaryString.length); + for (let i = 0; i < binaryString.length; i++) { + bytes[i] = binaryString.charCodeAt(i); + } + + // 将TypedArray转换为字符串 + return new TextDecoder('utf-8').decode(bytes); +} const eventHandler = (e) => { const eventData = e.data - // if (eventData.type === 'start') { - // markdownTitle = '未命名文稿' - // } + if (eventData.type === 'start') { + markdownTitle = eventData.title || '未命名文稿' + return + } // console.log(markdownTitle) if (eventData.type === 'init') { - markdownTitle = eventData.title ? eventData.title : '未命名文稿' const data = eventData.data + markdownTitle = data.title || '未命名文稿' if (!data) { return; } - cherry.setMarkdown(atob(data.content)); + let content = data.content; + + if (isBase64(content)) { + content = decodeBase64(content); + } else if (content instanceof ArrayBuffer) { + content = new TextDecoder('utf-8').decode(content); + } + + cherry.setMarkdown(content); } } window.addEventListener('load', () => { diff --git a/frontend/public/text/js/base.js b/frontend/public/text/js/base.js index 94cca8c..371c38b 100644 --- a/frontend/public/text/js/base.js +++ b/frontend/public/text/js/base.js @@ -915,18 +915,18 @@ const eventHandler = (e) => { renameEditor({ name: eventData.title }); } if (eventData.type === 'init') { - console.log(eventData) + //console.log(eventData) //const data = JSON.parse(eventData.data) const data = eventData.data if (!data || !data.title) { return; } - console.log(data) + //console.log(data) if(data.ext && data.ext != 'txt') { //Editor.query().setName("extension", data.ext); data.title = data.title + "." + data.ext; } - console.log(data.title) + //console.log(data.title) renameEditor({ name: data.title }); //console.log(data) if (data.content) { diff --git a/frontend/src/components/window/IframeFile.vue b/frontend/src/components/window/IframeFile.vue index b06901b..5f2c47a 100644 --- a/frontend/src/components/window/IframeFile.vue +++ b/frontend/src/components/window/IframeFile.vue @@ -37,7 +37,7 @@ const storeRef = ref(null); let hasInit = false; const eventHandler = async (e: MessageEvent) => { const eventData = e.data; - + if (eventData.type == props.eventType) { let data = JSON.parse(eventData.data); let title = data.title; @@ -49,6 +49,9 @@ const eventHandler = async (e: MessageEvent) => { if (data.ext) { ext = data.ext; } + if (title.indexOf('.' + ext) > -1) { + title = title.replace('.' + ext, ""); + } // console.log(ext) // console.log(data) if (win.config && win.config.path) { @@ -131,19 +134,19 @@ const eventHandler = async (e: MessageEvent) => { // console.log(title); title = title.split(SP).pop(); - if (!content && win?.config.path) { - const header = { - pwd: '' - }; - const filePwd = getSystemConfig().fileInputPwd - const pos = filePwd.findIndex((item: any) => item.path == win?.config.path) - //console.log('路径:', win?.config.path, pos, filePwd); - const userType = getSystemConfig().userType - if (pos !== -1) { - header.pwd = userType == 'person' ? md5(filePwd[pos].pwd) : filePwd[pos].pwd - } - content = await sys?.fs.readFile(win?.config.path, header); - } + // if (!content && win?.config.path) { + // const header = { + // pwd: '' + // }; + // const filePwd = getSystemConfig().fileInputPwd + // const pos = filePwd.findIndex((item: any) => item.path == win?.config.path) + // //console.log('路径:', win?.config.path, pos, filePwd); + // const userType = getSystemConfig().userType + // if (pos !== -1) { + // header.pwd = userType == 'person' ? md5(filePwd[pos].pwd) : filePwd[pos].pwd + // } + // content = await sys?.fs.readFile(win?.config.path, header); + // } content = toRaw(content); if (content && content !== "") { storeRef.value?.contentWindow?.postMessage( @@ -173,59 +176,59 @@ const eventHandler = async (e: MessageEvent) => { const path = win?.config?.path; //console.log(path,data) const winMind = new BrowserWindow({ - title:data.title, + title: data.title, url: "/mind/index.html", frame: true, - config: { - ext: 'mind', - path: path, + config: { + ext: 'mind', + path: path, content: data.content - }, - icon: "gallery", - width: 700, - height: 500, - x: 100, - y: 100, - //center: true, - minimizable: false, - resizable: true, - }); - winMind.show() - - } - else if (eventData.type == 'aiCreater') { - console.log('传递内容: ',eventData) - let postData:any = {} - if(eventData.data){ - postData.content = eventData.data - } - if(eventData.title){ - postData.title = eventData.title + }, + icon: "gallery", + width: 700, + height: 500, + x: 100, + y: 100, + //center: true, + minimizable: false, + resizable: true, + }); + winMind.show() + } - if(eventData.category){ - postData.category = eventData.category + else if (eventData.type == 'aiCreater') { + console.log('传递内容: ', eventData) + let postData: any = {} + if (eventData.data) { + postData.content = eventData.data + } + if (eventData.title) { + postData.title = eventData.title + } + if (eventData.category) { + postData.category = eventData.category + } + //console.log(postData,eventData.action) + // 模拟AI返回数据 + const res: any = await askAi(postData, eventData.action); + storeRef.value?.contentWindow?.postMessage( + { + type: 'aiReciver', + data: res, + action: eventData.action + }, + "*" + ); } - //console.log(postData,eventData.action) - // 模拟AI返回数据 - const res:any = await askAi(postData, eventData.action); - storeRef.value?.contentWindow?.postMessage( - { - type: 'aiReciver', - data: res, - action: eventData.action - }, - "*" - ); - } -// else if (eventData.type == 'aiReciver') { -// storeRef.value?.contentWindow?.postMessage( -// { -// type: eventData.type, -// data: '----经过AI处理后的数据-----', -// }, -// "*" -// ); -// } + // else if (eventData.type == 'aiReciver') { + // storeRef.value?.contentWindow?.postMessage( + // { + // type: eventData.type, + // data: '----经过AI处理后的数据-----', + // }, + // "*" + // ); + // } }; //删除本地暂存的文件密码 const delFileInputPwd = async () => { diff --git a/frontend/src/system/index.ts b/frontend/src/system/index.ts index 1f24f36..d0b34bf 100644 --- a/frontend/src/system/index.ts +++ b/frontend/src/system/index.ts @@ -461,14 +461,14 @@ export class System { pwd: '' } //判断文件是否需要输入密码 - // if (fileStat.isPwd && path.indexOf('.exe') === -1) { - // const temp = await Dialog.showInputBox() - // if (temp.response !== 1) { - // return - // } - // // header.salt = filePwd.file.salt || 'vIf_wIUedciAd0nTm6qjJA==' - // header.pwd = temp?.inputPwd || '' - // } + if (fileStat.isPwd && path.indexOf('.exe') === -1) { + const temp = await Dialog.showInputBox() + if (temp.response !== 1) { + return + } + // header.salt = filePwd.file.salt || 'vIf_wIUedciAd0nTm6qjJA==' + header.pwd = temp?.inputPwd || '' + } // 读取文件内容 const fileContent = await this.fs.readFile(path, header); // if (fileContent === false && fileStat.isPwd) { @@ -482,7 +482,7 @@ export class System { } // header.salt = filePwd.file.salt || 'vIf_wIUedciAd0nTm6qjJA==' header.pwd = temp?.inputPwd ? md5(temp?.inputPwd) : '' - const reOpen = await this.fs.readFile(path, header); + //const reOpen = await this.fs.readFile(path, header); } //用户文件加密密码存储 if (fileStat.isPwd) { diff --git a/godo/files/os.go b/godo/files/os.go index 4bccde7..348f2a2 100644 --- a/godo/files/os.go +++ b/godo/files/os.go @@ -25,6 +25,7 @@ import ( "io/fs" "os" "path/filepath" + "regexp" "strings" "time" ) @@ -336,5 +337,13 @@ func IsPwdFile(fileData []byte) bool { } else if fileData[0] != '@' || fileData[33] != '@' { return false } + // 提取中间的字符串 + middleStr := string(fileData[1:33]) + + // 使用正则表达式验证中间的字符串是否为 MD5 加密的字符串 + md5Regex := regexp.MustCompile(`^[a-fA-F0-9]{32}$`) + if !md5Regex.MatchString(middleStr) { + return false + } return true } diff --git a/godo/files/pwdfile.go b/godo/files/pwdfile.go index ae4864c..cf24236 100644 --- a/godo/files/pwdfile.go +++ b/godo/files/pwdfile.go @@ -1,7 +1,6 @@ package files import ( - "encoding/base64" "encoding/json" "fmt" "godo/libs" @@ -43,8 +42,8 @@ func HandleWriteFile(w http.ResponseWriter, r *http.Request) { libs.HTTPError(w, http.StatusInternalServerError, err.Error()) return } + //log.Printf("fileData: %s", string(fileData)) configPwd, ishas := libs.GetConfig("filePwd") - //log.Printf("configPwd: %s", configPwd) // 如果不是加密文件或者exe文件 if !ishas || strings.HasPrefix(string(fileData), "link::") { // 没开启加密,直接明文写入 @@ -66,17 +65,22 @@ func HandleWriteFile(w http.ResponseWriter, r *http.Request) { libs.HTTPError(w, http.StatusInternalServerError, "配置文件密码格式错误") return } - _, err = newFile.WriteString(fmt.Sprintf("@%s@", configPwdStr)) - if err != nil { - libs.HTTPError(w, http.StatusInternalServerError, "密码写入失败") - return - } + // 拼接密码和加密后的数据 + passwordPrefix := fmt.Sprintf("@%s@", configPwdStr) + // _, err = newFile.WriteString(fmt.Sprintf("@%s@", configPwdStr)) + // if err != nil { + // libs.HTTPError(w, http.StatusInternalServerError, "密码写入失败") + // return + // } entryData, err := libs.EncryptData(fileData, []byte(configPwdStr)) if err != nil { libs.HTTPError(w, http.StatusInternalServerError, "文件加密失败") return } - _, err = newFile.Write(entryData) + // 将密码前缀和加密数据拼接成一个完整的字节切片 + completeData := []byte(passwordPrefix + string(entryData)) + // 一次性写入文件 + _, err = newFile.Write(completeData) if err != nil { libs.HTTPError(w, http.StatusInternalServerError, "文件写入失败") return @@ -123,8 +127,8 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { if !isPwd { // 未加密文件,直接返回 - content := base64.StdEncoding.EncodeToString(fileData) - res := libs.APIResponse{Message: "文件读取成功", Data: content} + //content := base64.StdEncoding.EncodeToString(fileData) + res := libs.APIResponse{Message: "文件读取成功", Data: fileData} json.NewEncoder(w).Encode(res) return } @@ -134,18 +138,19 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { if Pwd == "" { configPwd, ishas := libs.GetConfig("filePwd") if !ishas { - libs.ErrorMsg(w, "未设置密码") + libs.Error(w, "未设置密码", "needPwd") return } configPwdStr, ok := configPwd.(string) if !ok { - libs.ErrorMsg(w, "后端配置文件密码格式错误") + libs.Error(w, "后端配置文件密码格式错误", "needPwd") return } // 校验密码 if filePwd != configPwdStr { - res := libs.APIResponse{Message: "密码错误,请输入正确的密码", Code: -1, Error: "needPwd"} - json.NewEncoder(w).Encode(res) + libs.Error(w, "密码错误,请输入正确的密码", "errPwd") + // res := libs.APIResponse{Message: "密码错误,请输入正确的密码", Code: -1, Error: "needPwd"} + // json.NewEncoder(w).Encode(res) return } decryptData, err := libs.DecryptData(fileData[34:], []byte(filePwd)) @@ -153,8 +158,9 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { libs.ErrorMsg(w, err.Error()) return } - content := base64.StdEncoding.EncodeToString(decryptData) - res := libs.APIResponse{Message: "加密文件读取成功", Data: content} + + //content := base64.StdEncoding.EncodeToString(decryptData) + res := libs.APIResponse{Message: "加密文件读取成功", Data: decryptData} json.NewEncoder(w).Encode(res) return } else { @@ -169,8 +175,8 @@ func HandleReadFile(w http.ResponseWriter, r *http.Request) { libs.ErrorMsg(w, err.Error()) return } - content := base64.StdEncoding.EncodeToString(decryptData) - res := libs.APIResponse{Message: "加密文件读取成功", Data: content} + //content := base64.StdEncoding.EncodeToString(decryptData) + res := libs.APIResponse{Message: "加密文件读取成功", Data: decryptData} json.NewEncoder(w).Encode(res) } } diff --git a/godo/libs/msg.go b/godo/libs/msg.go index 4a75a3a..85ba400 100644 --- a/godo/libs/msg.go +++ b/godo/libs/msg.go @@ -46,6 +46,9 @@ func ErrorMsg(w http.ResponseWriter, message string) { func ErrorData(w http.ResponseWriter, data any, message string) { WriteJSONResponse(w, APIResponse{Message: message, Data: data, Code: -1}, 200) } +func Error(w http.ResponseWriter, message string, err string) { + WriteJSONResponse(w, APIResponse{Message: message, Error: err, Code: -1}, 200) +} func SuccessMsg(w http.ResponseWriter, data any, message string) { WriteJSONResponse(w, APIResponse{Message: message, Data: data, Code: 0}, 200) } diff --git a/packages/word/src/main.ts b/packages/word/src/main.ts index b0f8429..ec3f56b 100644 --- a/packages/word/src/main.ts +++ b/packages/word/src/main.ts @@ -27,7 +27,7 @@ import { Signature } from './components/signature/Signature' import { debounce, nextTick, scrollIntoView } from './utils' import barcode1DPlugin from "./plugins/barcode1d" import barcode2dPlugin from "./plugins/barcode2d" -import { floatingToolbarPlugin, changeAiTextarea} from "./plugins/floatingToolbar" +import { floatingToolbarPlugin, changeAiTextarea } from "./plugins/floatingToolbar" import excelPlugin from "./plugins/excel" import docxPlugin from './plugins/docx' window.onload = function () { @@ -58,7 +58,7 @@ window.onload = function () { instance.use(docxPlugin) const docxFileInput: any = document.querySelector("#file-docx"); const excelFileInput: any = document.querySelector("#file-excel"); - let wordTitle: any = '' + let godoWordTitle: any = "" // cypress使用 Reflect.set(window, 'editor', instance) @@ -1172,7 +1172,7 @@ window.onload = function () { function () { const title = articleTitle.value const category = articleType.value - + if (title == '' || category == '') { // alert() // new Dialog({ @@ -1213,7 +1213,7 @@ window.onload = function () { // outlineLoader.classList.remove('hide') // articleLoader.classList.add('hide') break - case 'article': + case 'article': watchArticle.classList.add('active-ai') watchOutline.classList.remove('active-ai') createArticleBtn?.classList.add('hide') @@ -1230,7 +1230,7 @@ window.onload = function () { // 查看大纲 watchOutline.onclick = () => switchView('outline') // 查看文章 - watchArticle.onclick = () => switchView('article') + watchArticle.onclick = () => switchView('article') // 生成文章 createArticleBtn.onclick = function () { switchView('article') @@ -1248,7 +1248,7 @@ window.onload = function () { // articleTextarea.value = AiResult.aiArticle } // 替换textarea内容 - function changeAiArticleTextarea (data: string , type: string) { + function changeAiArticleTextarea(data: string, type: string) { if (type == 'outline') { outlineTextarea.value = data outlineLoader.classList.add('hide') @@ -1950,11 +1950,11 @@ window.onload = function () { name: "导出文档", when: () => true, callback: (command) => { - if (wordTitle == '') { - wordTitle = window.prompt("请输入文档标题"); + if (godoWordTitle == '') { + godoWordTitle = window.prompt("请输入文档标题"); } command.executeExportDocx({ - fileName: wordTitle, + fileName: godoWordTitle, isFile: true }); }, @@ -2077,16 +2077,17 @@ window.onload = function () { }; reader.readAsArrayBuffer(file); }; - + const saveData = () => { - if (wordTitle == '') { - wordTitle = window.prompt("请输入文档标题"); + if (godoWordTitle == '') { + godoWordTitle = window.prompt("请输入文档标题"); } + console.log(godoWordTitle) instance.command.executeExportDocx({ - fileName: wordTitle, + fileName: godoWordTitle, isFile: false }) - + }; // 10. 快捷键注册 instance.register.shortcutList([ @@ -2151,7 +2152,7 @@ window.onload = function () { saveDom.addEventListener('click', () => { saveData() }) - const base64ToArrayBuffer = (base64:any) => { + const base64ToArrayBuffer = (base64: any) => { const binaryString = window.atob(base64); const len = binaryString.length; const bytes = new Uint8Array(len); @@ -2160,24 +2161,48 @@ window.onload = function () { } return bytes.buffer; } + function isBase64(str: any) { + if (str === '' || str.trim() === '') { + return false; + } + try { + return btoa(atob(str)) == str; + } catch (err) { + return false; + } + } const eventHandler = (e: any) => { const eventData = e.data // console.log(''); - - wordTitle = eventData.title ? eventData.title : '未命名文档' + + //godoWordTitle = eventData.title ? eventData.title : '未命名文档' + if (eventData.type === 'start') { + godoWordTitle = eventData.title + return + } if (eventData.type === 'init') { const data = eventData.data - if (!data) { + if (!data || !data.title) { return; } - const buffer = base64ToArrayBuffer(data.content) + godoWordTitle = data.title + if (isBase64(data.content)) { + data.content = base64ToArrayBuffer(data.content) + } + //const buffer = base64ToArrayBuffer(data.content) //console.log(buffer) - instance.command.executeImportDocx({ - arrayBuffer: buffer, - }); + if (data.content instanceof ArrayBuffer) { + instance.command.executeImportDocx({ + arrayBuffer: data.content, + }); + } else { + alert('导入失败') + } + return - } else if (eventData.type == 'aiReciver') { - console.log('接收到来自伏组件数据:', eventData); + } + if (eventData.type == 'aiReciver') { + //console.log('接收到来自伏组件数据:', eventData); if (eventData.action == 'creation_leader') { changeAiArticleTextarea(eventData.data, 'outline') } else if (eventData.action == 'creation_builder') { @@ -2188,10 +2213,10 @@ window.onload = function () { } } - + //window.addEventListener('load', () => { - window.parent.postMessage({ type: 'initSuccess' }, '*') - window.addEventListener('message', eventHandler) + window.parent.postMessage({ type: 'initSuccess' }, '*') + window.addEventListener('message', eventHandler) //}) window.addEventListener('unload', () => { window.removeEventListener('message', eventHandler)