Browse Source

fix:文件加密修复

master
prr 7 months ago
parent
commit
c7eb5f72e2
  1. 3
      frontend/auto-imports.d.ts
  2. 17
      frontend/src/components/builtin/FileList.vue
  3. 8
      frontend/src/components/builtin/FileTree.vue
  4. 131
      frontend/src/components/chat/ChatUserSetting.vue
  5. 9
      frontend/src/components/computer/Computer.vue
  6. 12
      frontend/src/components/setting/SetFilePwd.vue
  7. 25
      frontend/src/components/window/IframeFile.vue
  8. 9
      frontend/src/hook/useComputer.ts
  9. 45
      frontend/src/system/core/FileOs.ts
  10. 1
      frontend/src/system/core/FileSystem.ts
  11. 17
      frontend/src/util/sharePath.ts

3
frontend/auto-imports.d.ts

@ -3,7 +3,6 @@
// @ts-nocheck // @ts-nocheck
// noinspection JSUnusedGlobalSymbols // noinspection JSUnusedGlobalSymbols
// Generated by unplugin-auto-import // Generated by unplugin-auto-import
// biome-ignore lint: disable
export {} export {}
declare global { declare global {
const EffectScope: typeof import('vue')['EffectScope'] const EffectScope: typeof import('vue')['EffectScope']
@ -71,6 +70,6 @@ declare global {
// for type re-export // for type re-export
declare global { declare global {
// @ts-ignore // @ts-ignore
export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue'
import('vue') import('vue')
} }

17
frontend/src/components/builtin/FileList.vue

@ -130,6 +130,8 @@ function handleOnOpen(item: OsFileWithoutContent) {
choose.path.push(item.path) choose.path.push(item.path)
choose.close() choose.close()
} else { } else {
// console.log(' file list:',props.fileList);
props.onOpen(item); props.onOpen(item);
emitEvent('desktop.app.open'); emitEvent('desktop.app.open');
} }
@ -239,6 +241,7 @@ function startDragApp(mouse: DragEvent, item: OsFileWithoutContent) {
} }
function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index: number) { function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index: number) {
if (chosenIndexs.value.length <= 1) { if (chosenIndexs.value.length <= 1) {
chosenIndexs.value = [props.fileList.findIndex((app) => app.path === item.path)]; chosenIndexs.value = [props.fileList.findIndex((app) => app.path === item.path)];
} }
@ -280,7 +283,7 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
// }, // },
]; ];
if (item.isDirectory) { if (item.isDirectory && !item.isShare) {
if (getSystemKey('storeType') == 'local') { if (getSystemKey('storeType') == 'local') {
menuArr.push({ menuArr.push({
label: t('zip'), label: t('zip'),
@ -364,7 +367,10 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
chosenIndexs.value = []; chosenIndexs.value = [];
}, },
}, },
{
];
if (item.isShare && item.path.indexOf('/F/othershare') !== 0) {
fileMenus.push({
label: t('delete'), label: t('delete'),
click: async () => { click: async () => {
for (let i = 0; i < chosenIndexs.value.length; i++) { for (let i = 0; i < chosenIndexs.value.length; i++) {
@ -376,11 +382,10 @@ function handleRightClick(mouse: MouseEvent, item: OsFileWithoutContent, index:
sys.refershAppList() sys.refershAppList()
} }
}, },
}, })
}
];
const userType = sys.getConfig('userType'); const userType = sys.getConfig('userType');
if (userType == 'member') { if (userType == 'member' && !item.isShare) {
menuArr.push( menuArr.push(
{ {

8
frontend/src/components/builtin/FileTree.vue

@ -127,8 +127,14 @@ async function onOpenArrow(item: FileWithOpen) {
if (item.isOpen && !item.subFileList?.length) { if (item.isOpen && !item.subFileList?.length) {
return; return;
} }
item.isOpen = !item.isOpen; item.isOpen = !item.isOpen;
if(isShareFile(item.path)) { // if (item.isShare) {
// item.subFileList = (await sys.fs.readShareFileDir(getSystemConfig().userInfo.id, item?.path)).filter((file:any) => {
// return file.isDirectory;
// });
// } else
if(isShareFile(item.path)) {
item.subFileList = (await sys.fs.sharedir(getSystemConfig().userInfo.id, item?.path)).filter((file:any) => { item.subFileList = (await sys.fs.sharedir(getSystemConfig().userInfo.id, item?.path)).filter((file:any) => {
return file.isDirectory; return file.isDirectory;
}); });

131
frontend/src/components/chat/ChatUserSetting.vue

@ -1,35 +1,100 @@
<script setup> <script setup>
import { ref } from "vue"; import { t } from '@/i18n';
import { useChatStore } from "@/stores/chat"; import { ref, onMounted } from "vue";
const store = useChatStore() import { useChatStore } from "@/stores/chat";
const form = ref({ import { fetchGet, getSystemConfig } from "@/system/config";
nickname: '', const store = useChatStore()
}) const form = ref({
nickname: '',
})
const userInfo = getSystemConfig().userInfo
const dialogShow = ref(false)
let imgList = reactive([])
const showChange = (val) =>{
dialogShow.value = val
}
const toChangeHead = ()=>{
console.log('换头像');
}
//
const getHeadList = async() => {
const res = await fetchGet(`${userInfo.url}/files/avatarlist`)
if (res.ok) {
imgList = await res.json()
imgList = imgList.data.map(item => {
item.isChoose = false
return item
})
imgList[0].isChoose = true
}
console.log('imgList:' , imgList);
}
let pos = ref(0)
//
const chooseImg = (index) => {
imgList.forEach(item => item.isChoose = false)
imgList[index].isChoose = true
pos.value = index
}
const onSubmit = () => {
console.log('submit!')
}
onMounted(()=>{
getHeadList()
})
</script>
<template>
<div>
<el-form :model="form" label-width="160px" style="padding: 30px;">
<el-form-item label="头像">
<el-avatar :size="90" :src="store.userInfo.avatar" @click="showChange(true)"/>
</el-form-item>
<el-form-item label="昵称">
<el-input v-model="store.userInfo.nickname" />
</el-form-item>
<el-form-item label="手机号">
<el-input v-model="store.userInfo.phone" />
</el-form-item>
<el-form-item label="工号">
<el-input v-model="store.userInfo.job_number" />
</el-form-item>
<el-form-item label="自我介绍">
<el-input v-model="store.userInfo.desc" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
</el-form-item>
</el-form>
<el-dialog v-model="dialogShow" title="修改头像" width="500px" draggable>
<div>
<el-avatar
v-for="(item, index) in imgList"
:key="item.name"
:size="90"
:src="item.url"
:class="{'is-active': item.isChoose}"
@click="chooseImg(index)"/>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="showChange(false)">{{ t("cancel") }}</el-button>
<el-button type="primary" @click="toChangeHead">
{{ t("confirm") }}
</el-button>
</div>
</template>
</el-dialog>
const onSubmit = () => { </div>
console.log('submit!') </template>
}
</script> <style scoped lang="scss">
<template> .el-avatar {
<el-form :model="form" label-width="160px" style="padding: 30px;"> margin: 10px;
<el-form-item label="头像"> }
<el-avatar shape="square" :size="80" :src="store.userInfo.avatar"/> .is-active {
</el-form-item> width: 100px;
<el-form-item label="昵称"> height: 100px;
<el-input v-model="store.userInfo.nickname" /> border: 2px solid red;
</el-form-item> }
<el-form-item label="手机号"> </style>
<el-input v-model="store.userInfo.phone" />
</el-form-item>
<el-form-item label="工号">
<el-input v-model="store.userInfo.job_number" />
</el-form-item>
<el-form-item label="自我介绍">
<el-input v-model="store.userInfo.desc" />
</el-form-item>
<el-form-item>
<el-button type="primary" @click="onSubmit">保存</el-button>
</el-form-item>
</el-form>
</template>

9
frontend/src/components/computer/Computer.vue

@ -106,6 +106,7 @@ import { useFileDrag } from "@/hook/useFileDrag";
import { useComputer } from "@/hook/useComputer"; import { useComputer } from "@/hook/useComputer";
import { Rect, useRectChosen } from "@/hook/useRectChosen"; import { Rect, useRectChosen } from "@/hook/useRectChosen";
import { getSystemConfig } from "@/system/config"; import { getSystemConfig } from "@/system/config";
import { isShareFile, isRootShare, turnLocalPath } from "@/util/sharePath.ts";
const { choseStart, chosing, choseEnd, getRect, Chosen } = useRectChosen(); const { choseStart, chosing, choseEnd, getRect, Chosen } = useRectChosen();
@ -304,8 +305,12 @@ async function onTreeOpen(path: string) {
chosenTreePath.value = path; chosenTreePath.value = path;
let file:any let file:any
// const file = await system.fs.stat(path); // const file = await system.fs.stat(path);
if (path.substring(0,2) == '/F') { if (isRootShare(path)) {
file = path.indexOf('/F/myshare') !== -1 ? shareFileList.value[0] : shareFileList.value[1] file = path === '/F/myshare' ? shareFileList.value[0] : shareFileList.value[1]
} else if (isShareFile(path)) {
file = await system.fs.getShareInfo(path)
file = file.fi
file.path = turnLocalPath(file.path, path, 1)
} else { } else {
file = await system.fs.stat(path) file = await system.fs.stat(path)
} }

12
frontend/src/components/setting/SetFilePwd.vue

@ -3,7 +3,7 @@
<div v-if="setPwd"> <div v-if="setPwd">
<div class="setting-item" > <div class="setting-item" >
<label>文件密码</label> <label>文件密码</label>
<el-input v-model="filePwd" placeholder="请输入文件加密密码" type="password"/> <el-input v-model="filePwd" placeholder="请设置6-10位的密码" type="password"/>
</div> </div>
<div class="setting-item"> <div class="setting-item">
<label></label> <label></label>
@ -33,7 +33,11 @@ const params = {
} }
// //
async function toSetFilePwd() { async function toSetFilePwd() {
params.pwd = filePwd.value || md5(filePwd.value) if (filePwd.value.length < 6 || filePwd.value.length > 10) {
notifyError("密码长度应该在6-10位之间")
return
}
params.pwd = md5(filePwd.value)
params.isPwd = filePwd.value === '' ? 0 : 1 params.isPwd = filePwd.value === '' ? 0 : 1
const url = getApiUrl() + '/file/setfilepwd' const url = getApiUrl() + '/file/setfilepwd'
const header = { const header = {
@ -51,10 +55,10 @@ async function toSetFilePwd() {
} }
setSystemKey('file',params) setSystemKey('file',params)
} }
function clearPwd() { async function clearPwd() {
setPwd.value = false setPwd.value = false
filePwd.value = '' filePwd.value = ''
toSetFilePwd() await fetchGet(`${getApiUrl()}/file/changeispwd?ispwd=0`)
} }
onMounted(()=>{ onMounted(()=>{
setPwd.value = params.isPwd ? true : false setPwd.value = params.isPwd ? true : false

25
frontend/src/components/window/IframeFile.vue

@ -7,6 +7,7 @@ import { BrowserWindow, Notify, System, Dialog } from "@/system";
import { ref, onMounted, inject, onUnmounted, toRaw } from "vue"; import { ref, onMounted, inject, onUnmounted, toRaw } from "vue";
import { isBase64, base64ToBuffer } from "@/util/file"; import { isBase64, base64ToBuffer } from "@/util/file";
import { getSplit, getSystemConfig } from "@/system/config"; import { getSplit, getSystemConfig } from "@/system/config";
import { isShareFile } from "@/util/sharePath.ts";
const SP = getSplit(); const SP = getSplit();
const sys: any = inject<System>("system"); const sys: any = inject<System>("system");
@ -61,8 +62,22 @@ const eventHandler = async (e: MessageEvent) => {
} else { } else {
path = `${SP}C${SP}Users${SP}Desktop${SP}${title}.${ext}`; path = `${SP}C${SP}Users${SP}Desktop${SP}${title}.${ext}`;
} }
//
if (await sys?.fs.exists(path)) { const isShare = ref(false)
const isWrite = ref(0)
if (isShareFile(path)) {
const file = await sys?.fs.getShareInfo(path)
console.log('文件信息:',file);
isShare.value = true
isWrite.value = file.fs.is_write
if (!isWrite.value) {
new Notify({
title: "提示",
content: "该文件没有编辑权限",
});
return;
}
}else if (await sys?.fs.exists(path)) {
let res = await Dialog.showMessageBox({ let res = await Dialog.showMessageBox({
type: "info", type: "info",
title: "提示", title: "提示",
@ -84,11 +99,11 @@ const eventHandler = async (e: MessageEvent) => {
//console.log(data.content) //console.log(data.content)
} }
} }
//console.log(data.content) const res = isShare ? await sys?.fs.writeShareFile(path, data.content, isWrite.value) : await sys?.fs.writeFile(path, data.content);
await sys?.fs.writeFile(path, data.content); console.log('编写文件:', res);
new Notify({ new Notify({
title: "提示", title: "提示",
content: "文件已保存", content: res.code === 0 ? '文件已保存' : '文件保存失败',
}); });
sys.refershAppList(); sys.refershAppList();
} else if (eventData.type == "initSuccess") { } else if (eventData.type == "initSuccess") {

9
frontend/src/hook/useComputer.ts

@ -1,6 +1,5 @@
import * as fspath from '../system/core/Path'; import * as fspath from '../system/core/Path';
import { OsFileWithoutContent } from '../system/core/FileSystem'; import { OsFileWithoutContent } from '../system/core/FileSystem';
import { turnFilePath } from "@/util/sharePath.ts";
export type RouterPath = string; export type RouterPath = string;
export const useComputer = (adpater: { export const useComputer = (adpater: {
@ -22,6 +21,7 @@ export const useComputer = (adpater: {
if (path === '') path = '/'; if (path === '') path = '/';
else if (path === '/') path = '/'; else if (path === '/') path = '/';
else if (path.endsWith('/')) path = path.substr(0, path.length - 1); else if (path.endsWith('/')) path = path.substr(0, path.length - 1);
if(path.substring(0,2) !== '/F') { if(path.substring(0,2) !== '/F') {
const isExist = await adpater.exists(path); const isExist = await adpater.exists(path);
if (!isExist) { if (!isExist) {
@ -40,13 +40,13 @@ export const useComputer = (adpater: {
adpater.setFileList(result); adpater.setFileList(result);
return; return;
} }
if (!(await isVia(currentPath))) return; if (!(await isVia(currentPath))) return;
// console.log('use computer refresh:', currentPath); // console.log('use computer refresh:', currentPath);
let result let result
if (currentPath === '/F/myshare' || currentPath === '/F/othershare') { if (currentPath === '/F/myshare' || currentPath === '/F/othershare') {
result = await adpater.sharedir(currentPath) result = await adpater.sharedir(currentPath)
} else if (currentPath.indexOf('/F') === 0) { } else if (currentPath.indexOf('/F') === 0) {
// result = await adpater.readShareDir(file?.path || currentPath)
result = await adpater.readShareDir(currentPath) result = await adpater.readShareDir(currentPath)
} else { } else {
result = await adpater.readdir(currentPath); result = await adpater.readdir(currentPath);
@ -72,9 +72,10 @@ export const useComputer = (adpater: {
}; };
const openFolder = (file: OsFileWithoutContent) => { const openFolder = (file: OsFileWithoutContent) => {
if (adpater.isDirectory(file)) { if (adpater.isDirectory(file)) {
const path = file?.isShare ? turnFilePath(file) : file.path // const path = file?.isShare ? file.titleName : file.path
const path = file.path
adpater.setRouter(path); adpater.setRouter(path);
refersh(); refersh(file);
} else { } else {
adpater.openFile(file.path); adpater.openFile(file.path);
} }

45
frontend/src/system/core/FileOs.ts

@ -2,9 +2,9 @@ import { getFileUrl,fetchGet,fetchPost } from "../config.ts";
const API_BASE_URL = getFileUrl() const API_BASE_URL = getFileUrl()
import { OsFileMode } from '../core/FileMode'; import { OsFileMode } from '../core/FileMode';
import { getSystemConfig } from "@/system/config"; import { getSystemConfig } from "@/system/config";
import { turnServePath, turnLocalPath } from "@/util/sharePath.ts"; import { turnServePath, turnLocalPath, isRootShare } from "@/util/sharePath.ts";
import { OsFile } from "./FileSystem.ts"; import { OsFile } from "./FileSystem.ts";
// import { notifyError } from "@/util/msg";
export async function handleReadDir(path: any): Promise<any> { export async function handleReadDir(path: any): Promise<any> {
const res = await fetchGet(`${API_BASE_URL}/read?path=${encodeURIComponent(path)}`); const res = await fetchGet(`${API_BASE_URL}/read?path=${encodeURIComponent(path)}`);
if (!res.ok) { if (!res.ok) {
@ -60,6 +60,15 @@ export async function handleShareDetail(path: string, id: number): Promise<any>
} }
return await res.json(); return await res.json();
} }
// 编写共享文件
export async function handleWriteShareFile(path: string, content: any, isWrite: number): Promise<any> {
const formData = getFormData(content);
const res = await fetchPost(`${API_BASE_URL}/writewithchmod?path=${path}&iswrite=${isWrite}`, formData);
if (!res.ok) {
return false;
}
return await res.json();
}
export async function handleStat(path: string): Promise<any> { export async function handleStat(path: string): Promise<any> {
const res = await fetchGet(`${API_BASE_URL}/stat?path=${encodeURIComponent(path)}`); const res = await fetchGet(`${API_BASE_URL}/stat?path=${encodeURIComponent(path)}`);
if (!res.ok) { if (!res.ok) {
@ -244,24 +253,20 @@ export const useOsFile = () => {
return { return {
// 分享 // 分享
async sharedir(id: number, path: string) { async sharedir(id: number, path: string) {
const response = await handleReadShareDir(id, path); // console.log('是否是根:', isRootShare(path),path);
const fun = isRootShare(path) ? handleReadShareDir : handleShareDir
const response = await fun(id, path);
if (response && response.data) { if (response && response.data) {
return response.data.map((item: {[key: string]: OsFile}) => { return response.data.map((item: {[key: string]: OsFile}) => {
item.fi.isShare = true item.fi.isShare = true
item.fi.path = turnLocalPath(item.fi.path ,path) item.fi.path = turnLocalPath(item.fi.path ,path)
// item.fi.titleName = turnLocalPath(item.fi.titleName, path)
return item.fi return item.fi
}) })
} }
return []; return [];
}, },
async readShareFile(path: string) { async readShareFile(path: string) {
const file = await handleShareDetail(path,getSystemConfig()?.userInfo.id)
// console.log('文件信息:',file, file.data.fs.is_write);
if(!file.data.fs.is_write) {
alert('该文件不能编辑')
console.log('该文件不能编辑');
return false
}
const response = await handleReadShareFile(path); const response = await handleReadShareFile(path);
if (response && response.data) { if (response && response.data) {
return response.data; return response.data;
@ -271,7 +276,11 @@ export const useOsFile = () => {
async readShareFileDir(id: number, path: string) { async readShareFileDir(id: number, path: string) {
const response = await handleShareDir(id,path); const response = await handleShareDir(id,path);
if (response && response.data) { if (response && response.data) {
return response.data; return response.data.map((item: {[key: string]: OsFile}) => {
item.fi.isShare = true
item.fi.path = turnLocalPath(item.fi.path ,path,1)
return item.fi
})
} }
return []; return [];
}, },
@ -282,6 +291,15 @@ export const useOsFile = () => {
} }
return []; return [];
}, },
//编写共享文件
async writeShareFile(path: string, content: any, isWrite: number) {
path = turnServePath(path)
const response = await handleWriteShareFile(path, content, isWrite);
if (response) {
return response;
}
return false;
},
async readdir(path: string) { async readdir(path: string) {
const response = await handleReadDir(path); const response = await handleReadDir(path);
if (response && response.data) { if (response && response.data) {
@ -328,14 +346,15 @@ export const useOsFile = () => {
return false; return false;
}, },
async rename(oldPath: string, newPath: string) { async rename(oldPath: string, newPath: string) {
const response = await handleRename(oldPath, newPath); const response = await handleRename(turnServePath(oldPath), turnServePath(newPath));
if (response) { if (response) {
return response; return response;
} }
return false; return false;
}, },
async rmdir(path: string) { async rmdir(path: string) {
const response = await handleRmdir(path); const fun = path.indexOf('/F') === 0 ? handleShareUnlink : handleRmdir
const response = await fun(path);
if (response) { if (response) {
return response; return response;
} }

1
frontend/src/system/core/FileSystem.ts

@ -103,6 +103,7 @@ class OsFile extends OsFileInfo {
isSys?: number; // 文件是否是系统文件(可选) isSys?: number; // 文件是否是系统文件(可选)
isShare?: boolean; // 文件是否为共享文件 isShare?: boolean; // 文件是否为共享文件
isPwd?: boolean; //文件是否上锁 isPwd?: boolean; //文件是否上锁
titleName?: string; // 文件原本路径
/** /**
* OsFile * OsFile

17
frontend/src/util/sharePath.ts

@ -1,18 +1,27 @@
import { OsFileWithoutContent } from '../system/core/FileSystem'; import { OsFileWithoutContent } from '../system/core/FileSystem';
export function turnServePath(path: string): string { export function turnServePath(path: string): string {
const replaceUrl = path.indexOf('/F/myshare') === 0 ? '/F/myshare' : '/F/othershare' const replaceUrl = path.indexOf('/F/myshare') === 0 ? '/F/myshare' : '/F/othershare'
return path.replace(replaceUrl, 'data/userData') return path.replace(replaceUrl, 'data/userData')
} }
export function turnLocalPath(path: string, newTemp: string): string { //将浏览器地址转化为本地地址
export function turnLocalPath(path: string, newTemp: string, type?: number): string {
if (type && type === 1) {
const arr = newTemp.split('/')
newTemp = `/${arr[1]}/${arr[2]}`
}
return path.replace('data/userData', newTemp) return path.replace('data/userData', newTemp)
} }
export function isShareFile(path: string) : boolean { export function isShareFile(path: string) : boolean {
// console.log('是否是共享文件:',path,path.indexOf('/F/myshare') === 0 || path.indexOf('/F/othershare') === 0); // console.log('是否是共享文件:',path,path.indexOf('/F/myshare') === 0 || path.indexOf('/F/othershare') === 0);
return path.indexOf('/F/myshare') === 0 || path.indexOf('/F/othershare') === 0 return path.indexOf('/F/myshare') === 0 || path.indexOf('/F/othershare') === 0
} }
//是否是分享根目录
export function isRootShare(path: string) : boolean {
// console.log('根路径:',path === '/F/myshare' || path === '/F/othershare');
return path === '/F/myshare' || path === '/F/othershare'
}
//返回路径 //返回路径
export function turnFilePath(file: OsFileWithoutContent) : string { export function turnFilePath(file: OsFileWithoutContent) : string {
// console.log('是否是共享文件:',path,path.indexOf('/F/myshare') === 0 || path.indexOf('/F/othershare') === 0);
const arr = file.path.split('/') const arr = file.path.split('/')
return `/${arr[1]}/${arr[2]}/${arr[arr.length - 1]}` return `/${arr[1]}/${arr[2]}/${arr[arr.length - 1]}`
} }
Loading…
Cancel
Save