diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml index 0dd120b..f88996c 100644 --- a/.github/workflows/maven.yml +++ b/.github/workflows/maven.yml @@ -51,7 +51,7 @@ jobs: cache: maven - name: Build Frontend - run: cd web-front && npm install && npm run build + run: cd web-front && yarn install && yarn run build - name: Build with Maven run: mvn -B package --file pom.xml diff --git a/web-front/src/components/DirectoryTree.vue b/web-front/src/components/DirectoryTree.vue new file mode 100644 index 0000000..7cd2e73 --- /dev/null +++ b/web-front/src/components/DirectoryTree.vue @@ -0,0 +1,993 @@ + + + + + \ No newline at end of file diff --git a/web-front/src/router/index.js b/web-front/src/router/index.js new file mode 100644 index 0000000..915540d --- /dev/null +++ b/web-front/src/router/index.js @@ -0,0 +1,17 @@ +import { createRouter, createWebHistory } from 'vue-router' +import Home from '@/views/Home.vue' +import ShowFile from '@/views/ShowFile.vue' +import ShowList from '@/views/ShowList.vue' + +const routes = [ + { path: '/', component: Home }, + { path: '/showFile', component: ShowFile }, + { path: '/showList', component: ShowList } +] + +const router = createRouter({ + history: createWebHistory('/'), + routes +}) + +export default router \ No newline at end of file diff --git a/web-front/src/utils/fileTypeUtils.js b/web-front/src/utils/fileTypeUtils.js new file mode 100644 index 0000000..5a26fa5 --- /dev/null +++ b/web-front/src/utils/fileTypeUtils.js @@ -0,0 +1,85 @@ +const fileTypeUtils = { + getFileExtension(filename) { + if (!filename) return '' + return filename.split('.').pop() + }, + getFileTypeClass(file) { + if (file.fileType === 'folder') return 'folder' + const ext = this.getFileExtension(file.fileName) + const fileTypes = { + 'image': ['jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp'], + 'document': ['pdf', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'rtf'], + 'archive': ['zip', 'rar', '7z', 'tar', 'gz'], + 'audio': ['mp3', 'wav', 'ogg', 'flac'], + 'video': ['mp4', 'avi', 'mov', 'wmv', 'mkv', 'flv'], + 'code': ['html', 'htm', 'css', 'js', 'json', 'php', 'py', 'java', 'c', 'cpp', 'h', 'sh', 'bat', 'md'] + } + for (const [type, extensions] of Object.entries(fileTypes)) { + if (extensions.includes(ext.toLowerCase())) { + return type + } + } + return 'document' + }, + getFileIcon(file) { + if (file.fileType === 'folder') return 'fas fa-folder' + const ext = this.getFileExtension(file.fileName) + const iconMap = { + 'jpg': 'fas fa-file-image', 'jpeg': 'fas fa-file-image', 'png': 'fas fa-file-image', + 'gif': 'fas fa-file-image', 'bmp': 'fas fa-file-image', 'svg': 'fas fa-file-image', 'webp': 'fas fa-file-image', + 'pdf': 'fas fa-file-pdf', 'doc': 'fas fa-file-word', 'docx': 'fas fa-file-word', + 'xls': 'fas fa-file-excel', 'xlsx': 'fas fa-file-excel', 'ppt': 'fas fa-file-powerpoint', 'pptx': 'fas fa-file-powerpoint', + 'txt': 'fas fa-file-alt', 'rtf': 'fas fa-file-alt', + 'zip': 'fas fa-file-archive', 'rar': 'fas fa-file-archive', '7z': 'fas fa-file-archive', + 'tar': 'fas fa-file-archive', 'gz': 'fas fa-file-archive', + 'mp3': 'fas fa-file-audio', 'wav': 'fas fa-file-audio', 'ogg': 'fas fa-file-audio', 'flac': 'fas fa-file-audio', + 'mp4': 'fas fa-file-video', 'avi': 'fas fa-file-video', 'mov': 'fas fa-file-video', + 'wmv': 'fas fa-file-video', 'mkv': 'fas fa-file-video', 'flv': 'fas fa-file-video', + 'html': 'fas fa-file-code', 'htm': 'fas fa-file-code', 'css': 'fas fa-file-code', + 'js': 'fas fa-file-code', 'json': 'fas fa-file-code', 'php': 'fas fa-file-code', + 'py': 'fas fa-file-code', 'java': 'fas fa-file-code', 'c': 'fas fa-file-code', + 'cpp': 'fas fa-file-code', 'h': 'fas fa-file-code', 'sh': 'fas fa-file-code', + 'bat': 'fas fa-file-code', 'md': 'fas fa-file-code' + } + return iconMap[ext.toLowerCase()] || 'fas fa-file' + }, + extractFileNameAndExt(url) { + if (!url) return { name: '', ext: '' } + const filenameParams = [ + 'response-content-disposition', 'filename', 'filename*', 'fn', 'fname', 'download_name' + ]; + let name = null; + try { + const u = new URL(url, window.location.origin); + for (const param of filenameParams) { + const value = u.searchParams.get(param); + if (value) { + if (param === 'response-content-disposition') { + const match = value.match(/filename\*?=(.*'')?(?.*)/i); + name = match && match.groups && match.groups['FN'] ? match.groups['FN'] : value; + } else { + name = value; + } + break; + } + } + if (name) { + name = decodeURIComponent(name).replace(/['"]/g, ''); + } else { + const decodedUrl = decodeURIComponent(url); + const paths = decodedUrl.split('/'); + name = paths[paths.length - 1].split('?')[0]; + } + let ext = ''; + if (name) { + const spl = name.split('.'); + ext = spl.length > 1 ? spl[spl.length - 1].toLowerCase() : ''; + } + return { name, ext }; + } catch { + return { name: '', ext: '' } + } + } +} + +export default fileTypeUtils \ No newline at end of file diff --git a/web-front/src/views/Home.vue b/web-front/src/views/Home.vue new file mode 100644 index 0000000..f96f18d --- /dev/null +++ b/web-front/src/views/Home.vue @@ -0,0 +1,793 @@ + + + + + diff --git a/web-front/src/views/ShowFile.vue b/web-front/src/views/ShowFile.vue new file mode 100644 index 0000000..9b675a3 --- /dev/null +++ b/web-front/src/views/ShowFile.vue @@ -0,0 +1,118 @@ + + + + + \ No newline at end of file diff --git a/web-front/src/views/ShowList.vue b/web-front/src/views/ShowList.vue new file mode 100644 index 0000000..b668205 --- /dev/null +++ b/web-front/src/views/ShowList.vue @@ -0,0 +1,107 @@ + + + + + \ No newline at end of file diff --git a/web-service/src/main/resources/http-tools/admin-api/api-test.http b/web-service/src/main/resources/http-tools/admin-api/api-test.http new file mode 100644 index 0000000..8a01dae --- /dev/null +++ b/web-service/src/main/resources/http-tools/admin-api/api-test.http @@ -0,0 +1,90 @@ +# 网盘分享链接云解析服务 API 测试 +# 本文件包含了系统所有API接口的测试请求 +# 使用方法: +# 1. 先运行登录接口获取token +# 2. 将返回的token替换所有请求中的YOUR_TOKEN_HERE +# 3. 对于需要ID的请求,将实际ID替换TOKEN_ID + +### 用户接口 ### + +### 登录接口 +POST http://localhost:6400/api/user/login +Content-Type: application/json + +{ + "username": "admin", + "password": "admin123" +} + +### 用户注册 +POST http://localhost:6400/api/user/register +Content-Type: application/json + +{ + "username": "testuser", + "password": "password123", + "email": "testuser@example.com", + "phone": "13800138000" +} + +### 获取用户信息 +# 使用登录接口返回的token替换下面的YOUR_TOKEN_HERE +GET http://localhost:6400/api/user/info +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjhhN2E3ZDc1LWUxNDEtNDFiOS05ODFhLWJmZGNjNzU2NjQyZCIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3NTA4MjUxMDMxOTEsImlhdCI6MTc1MDczODcwMzE5MSwiaXNzIjoibmV0ZGlzay1mYXN0LWRvd25sb2FkIn0.z4Dhwji1_yHEVx0sb3DN1n6HjlRmG8-Qr0Th5XIVeHc + +### 验证Token +POST http://localhost:6400/api/user/validate-token +Content-Type: application/json + +{ + "token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjhhN2E3ZDc1LWUxNDEtNDFiOS05ODFhLWJmZGNjNzU2NjQyZCIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3NTA4MjUxMDMxOTEsImlhdCI6MTc1MDczODcwMzE5MSwiaXNzIjoibmV0ZGlzay1mYXN0LWRvd25sb2FkIn0.z4Dhwji1_yHEVx0sb3DN1n6HjlRmG8-Qr0Th5XIVeHc" +} + +### 更新用户信息 +PUT http://localhost:6400/api/user/update +Content-Type: application/json +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjhhN2E3ZDc1LWUxNDEtNDFiOS05ODFhLWJmZGNjNzU2NjQyZCIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3NTA4MjUxMDMxOTEsImlhdCI6MTc1MDczODcwMzE5MSwiaXNzIjoibmV0ZGlzay1mYXN0LWRvd25sb2FkIn0.z4Dhwji1_yHEVx0sb3DN1n6HjlRmG8-Qr0Th5XIVeHc + +{ + "email": "new-email@example.com", + "phone": "13900139000", + "avatar": "https://example.com/avatar.jpg" +} + +### 管理员接口 ### + +### 获取所有网盘Token +GET http://localhost:6400/api/admin/tokens +Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjhhN2E3ZDc1LWUxNDEtNDFiOS05ODFhLWJmZGNjNzU2NjQyZCIsInVzZXJuYW1lIjoiYWRtaW4iLCJyb2xlIjoiYWRtaW4iLCJleHAiOjE3NTA4MjUxMDMxOTEsImlhdCI6MTc1MDczODcwMzE5MSwiaXNzIjoibmV0ZGlzay1mYXN0LWRvd25sb2FkIn0.z4Dhwji1_yHEVx0sb3DN1n6HjlRmG8-Qr0Th5XIVeHc + +### 添加网盘Token +POST http://localhost:6400/api/admin/token +Content-Type: application/json +Authorization: Bearer YOUR_TOKEN_HERE + +{ + "type": "yidong", + "description": "移动云盘token", + "token": "abc123xyz456" +} + +### 获取单个网盘Token +# 替换下面的TOKEN_ID为实际的token ID +GET http://localhost:6400/api/admin/token/TOKEN_ID +Authorization: Bearer YOUR_TOKEN_HERE + +### 更新网盘Token +# 替换下面的TOKEN_ID为实际的token ID +PUT http://localhost:6400/api/admin/token/TOKEN_ID +Content-Type: application/json +Authorization: Bearer YOUR_TOKEN_HERE + +{ + "description": "更新后的描述", + "token": "new-token-value" +} + +### 删除网盘Token +# 替换下面的TOKEN_ID为实际的token ID +DELETE http://localhost:6400/api/admin/token/TOKEN_ID +Authorization: Bearer YOUR_TOKEN_HERE diff --git a/web-service/src/main/resources/http-tools/admin-api/pan-api-test.http b/web-service/src/main/resources/http-tools/admin-api/pan-api-test.http new file mode 100644 index 0000000..373eb08 --- /dev/null +++ b/web-service/src/main/resources/http-tools/admin-api/pan-api-test.http @@ -0,0 +1,25 @@ +POST https://login.123pan.com/api/user/sign_in +Accept: application/json, text/plain, */* +Accept-Language: zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6 +App-Version: 3 +Connection: keep-alive +Content-Type: application/json +LoginUuid: 694eff443c1896851f0fa32abbb8c6ec69a422aa21721f4556d1e9f07a568bee +Referer: https://login.123pan.com/ +Sec-Fetch-Dest: empty +Sec-Fetch-Mode: cors +Sec-Fetch-Site: same-origin +User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/137.0.0.0 Safari/537.36 Edg/137.0.0.0 +platform: web +sec-ch-ua: "Microsoft Edge";v="137", "Chromium";v="137", "Not/A)Brand";v="24" +sec-ch-ua-mobile: ?0 +sec-ch-ua-platform: "macOS" + +{ + "passport": "", + "password": "", + "remember": true +} + +### +POST http:// \ No newline at end of file