├── .env ├── .gitignore ├── .npmrc ├── README.md ├── app.vue ├── assets └── main.less ├── components ├── Files │ ├── breadcrumbs.vue │ ├── item.vue │ ├── list.vue │ └── readme.vue ├── base │ ├── Footer.vue │ ├── Header.vue │ └── LeftMenu.vue └── income.vue ├── layouts └── default.vue ├── model ├── Interfaces.ts ├── SeverConfig.ts ├── initialization.ts ├── main.ts └── marked.ts ├── nuxt.config.ts ├── package-lock.json ├── package.json ├── pages ├── [...path].vue ├── about.vue ├── donate.vue └── init.vue ├── plugins ├── loading.ts └── vuetify.ts ├── public ├── README.md ├── ads.txt ├── comment.png ├── favicon.ico └── lazy_img.webp ├── server ├── OneDrive │ ├── cache.ts │ └── onedrive.ts ├── api │ ├── GetItem.post.ts │ └── GetReadme.post.ts ├── model │ ├── link.ts │ └── mysql.ts └── routes │ ├── ForzaHorizon5 │ └── ForzaHorizon5 │ │ └── [...any].ts │ └── Games │ └── [...any].ts ├── stores ├── useConfig.ts └── useMain.ts ├── tsconfig.json └── yarn.lock /.env: -------------------------------------------------------------------------------- 1 | site_siteName=小莫的网盘 2 | config_Name=pan 3 | onedrive_root=/public 4 | server_post=3001 -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | *.log* 3 | .nuxt 4 | .nitro 5 | .cache 6 | .output 7 | .env.* 8 | dist 9 | .vercel 10 | -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | shamefully-hoist=true -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # One Nuxt 2 | 3 | 演示地址: [小莫的云网盘](https://pan.aoe.top) | [小莫的游戏库](https://game.aoe.top) 4 | 5 | 使用 nuxt3 + vue3 + ts 构建的一个OneDrive网盘项目 6 | 7 | ### 特性 8 | - 使用Vue3 + TS编写 9 | - 前后端分离 10 | - 数据库缓存 11 | - 使用SSR后端渲染,优化SEO排名 12 | - 效率和OneD比并没有太大的提升 13 | 14 | ### 使用 15 | 16 | ``` 17 | npm i 18 | npm run dev 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /app.vue: -------------------------------------------------------------------------------- 1 | 24 | 29 | 35 | -------------------------------------------------------------------------------- /assets/main.less: -------------------------------------------------------------------------------- 1 | /* 滚动条美化 */ 2 | .scrollbar-hover::-webkit-scrollbar, 3 | .scrollbar-hover::-webkit-scrollbar-button, 4 | .scrollbar-hover::-webkit-scrollbar-thumb, 5 | .scrollbar-hover::-webkit-scrollbar-track { 6 | visibility: hidden; 7 | width: 3px; 8 | 9 | &:hover { 10 | visibility: visible; 11 | width: 7px 12 | } 13 | } 14 | 15 | ::-webkit-scrollbar { 16 | width: 7px; 17 | height: 7px 18 | } 19 | 20 | ::-webkit-scrollbar-button { 21 | width: 0; 22 | height: 0 23 | } 24 | 25 | ::-webkit-scrollbar-thumb { 26 | background-color: #b1b1b1; 27 | -webkit-background-clip: padding-box; 28 | background-clip: padding-box; 29 | border-radius: 5px; 30 | -webkit-box-shadow: inset 1px 1px 0 rgba(0, 0, 0, .1), inset 0 -1px 0 rgba(0, 0, 0, .07); 31 | border: 3px solid transparent; 32 | transition: .5s all 33 | } 34 | 35 | ::-webkit-scrollbar-thumb:hover { 36 | background-color: #b1b1b1; 37 | -webkit-box-shadow: inset 1px 1px 1px rgba(0, 0, 0, .25); 38 | border: 0 solid transparent 39 | } -------------------------------------------------------------------------------- /components/Files/breadcrumbs.vue: -------------------------------------------------------------------------------- 1 | 36 | 43 | 49 | -------------------------------------------------------------------------------- /components/Files/item.vue: -------------------------------------------------------------------------------- 1 | 74 | 86 | 92 | -------------------------------------------------------------------------------- /components/Files/list.vue: -------------------------------------------------------------------------------- 1 | 57 | 88 | 94 | 132 | 133 | -------------------------------------------------------------------------------- /components/Files/readme.vue: -------------------------------------------------------------------------------- 1 | 24 | 32 | 38 | -------------------------------------------------------------------------------- /components/base/Footer.vue: -------------------------------------------------------------------------------- 1 | 13 | 29 | 35 | -------------------------------------------------------------------------------- /components/base/Header.vue: -------------------------------------------------------------------------------- 1 | 3 | 12 | 18 | -------------------------------------------------------------------------------- /components/base/LeftMenu.vue: -------------------------------------------------------------------------------- 1 | 41 | 58 | 64 | -------------------------------------------------------------------------------- /components/income.vue: -------------------------------------------------------------------------------- 1 | 9 | 15 | 21 | -------------------------------------------------------------------------------- /layouts/default.vue: -------------------------------------------------------------------------------- 1 | 17 | 40 | 46 | -------------------------------------------------------------------------------- /model/Interfaces.ts: -------------------------------------------------------------------------------- 1 | export interface IFileList { 2 | id: number; 3 | file_fid: string; 4 | file_parent: string; 5 | file_name: string; 6 | file_type: string; 7 | file_size: number; 8 | file_downloadUrl?: string; 9 | childCount?: number; 10 | lastModifiedDateTime: string; 11 | file_up_time: string; 12 | } 13 | 14 | // 声明 ICallback(err, result) 函数类型 15 | export type ICallback = (err: any, result: any) => void; 16 | 17 | export interface ILink { 18 | id: number; 19 | short_link: string 20 | long_link: string 21 | click_cot: number 22 | } -------------------------------------------------------------------------------- /model/SeverConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | // console.log(process.env); 3 | 4 | // console.log(process.env.site_siteName, process.env.onedrive_root); 5 | 6 | 7 | export let SeverConfig = { 8 | token: { 9 | // scopes:'Files.ReadWrite.All profile openid email', // 请求的范围 10 | scopes: ['Files.ReadWrite.All', 'Files.Read.All', 'User.Read', 'offline_access'], // 请求的范围 11 | clientId: "8acae33a-68bf-479c-97ff-a75ee2615edd", // 应用程序的 clientId 12 | clientSecret: "hyx8Q~uvgquzXHdr~XUWCp6SNyuqMPEmR8ZL_c-7", // 应用程序的 clientSecret 13 | authority: "https://login.microsoftonline.com/common", // 应用程序的 authority 14 | redirectUri: "http://localhost:3001/init/redirect", // 应用程序的 redirectUri 15 | accessToken: "", // 应用程序的 accessToken 16 | refreshToken: process?.env?.refreshToken || "", // 应用程序的 refreshToken 17 | expires_on: 0, 18 | }, 19 | siteName: process?.env?.site_siteName || '小莫的云网盘', 20 | onedrive_root: process?.env?.onedrive_root || '/public', 21 | } -------------------------------------------------------------------------------- /model/initialization.ts: -------------------------------------------------------------------------------- 1 | import { SeverConfig } from '@/model/SeverConfig' 2 | import { RefreshToken } from '@/server/OneDrive/onedrive' 3 | 4 | export function initialization() { 5 | 6 | // console.log("SeverConfig:", SeverConfig); 7 | 8 | let token = SeverConfig.token 9 | 10 | let time = new Date().getTime(); 11 | let expires_on = token.expires_on * 1000; 12 | 13 | if ((!token.accessToken) || (expires_on != 0 && time > expires_on)) { 14 | console.log("token不存在或已过期, 重新更新!!!"); 15 | RefreshToken(token.refreshToken).then((data) => { 16 | console.log('expires_on', token.expires_on); 17 | }).catch(err => { 18 | console.log(`err:`, err); 19 | }) 20 | } 21 | 22 | } -------------------------------------------------------------------------------- /model/main.ts: -------------------------------------------------------------------------------- 1 | export function GetNowTime() { 2 | let now = new Date(); 3 | let year = now.getFullYear(); 4 | let month = now.getMonth() + 1; 5 | let day = now.getDate(); 6 | let hour = now.getHours(); 7 | let minute = now.getMinutes(); 8 | let second = now.getSeconds(); 9 | let time = year + "-" + month + "-" + day + " " + hour + ":" + minute + ":" + second; 10 | return time; 11 | } -------------------------------------------------------------------------------- /model/marked.ts: -------------------------------------------------------------------------------- 1 | import { marked } from 'marked'; 2 | import 'github-markdown-css/github-markdown-dark.css' 3 | 4 | marked.setOptions({ 5 | breaks: true, 6 | }) 7 | 8 | // const link: marked.TokenizerExtension | marked.RendererExtension | (marked.TokenizerExtension & marked.RendererExtension) = { 9 | // name: 'link', 10 | // start(this, src: string) { return 1; }, // 提示 Marked.js 停止并检查是否匹配 11 | // renderer(this: marked.RendererThis, token: marked.Tokens.Generic): string { 12 | // return `${token.text} `; 13 | // } 14 | // } 15 | 16 | // marked.use({ 17 | // extensions: [link] 18 | // }) 19 | 20 | export default marked; -------------------------------------------------------------------------------- /nuxt.config.ts: -------------------------------------------------------------------------------- 1 | // https://nuxt.com/docs/api/configuration/nuxt-config 2 | 3 | export default defineNuxtConfig({ 4 | css: [ 5 | 'vuetify/styles', 6 | '@mdi/font/css/materialdesignicons.css', 7 | 'assets/main.less' 8 | ], 9 | build: { 10 | transpile: ['vuetify'], 11 | }, 12 | // ssr: process.env.NODE_ENV != 'development', 13 | ssr: false, 14 | modules: [ 15 | 'nuxt-og-image', 16 | "nuxt-vercel-analytics", 17 | "nuxt-gtag", 18 | [ 19 | '@pinia/nuxt', 20 | { 21 | autoImports: [ 22 | // 自动引入 `usePinia()` 23 | 'defineStore', 24 | // 自动引入 `usePinia()` 并重命名为 `usePiniaStore()` 25 | ['defineStore', 'definePiniaStore'], 26 | ], 27 | }, 28 | ], 29 | ], 30 | imports: { 31 | dirs: ['./stores'], 32 | }, 33 | extends: [ 34 | 'nuxt-seo-kit' 35 | ], 36 | runtimeConfig: { 37 | public: { 38 | siteUrl: process?.env?.NUXT_PUBLIC_SITE_URL || 'https://pan.aoe.top', 39 | siteName: process?.env?.site_siteName || '小莫的云网盘', 40 | siteDescription: '小莫的网盘, 分享一些能用得上的东西~', 41 | language: 'zh', // prefer more explicit language codes like `en-AU` over `en` 42 | onedrive_root: process?.env?.onedrive_root || '/public', 43 | refreshToken: process?.env?.refreshToken || '', 44 | }, 45 | }, 46 | gtag: { 47 | id: 'G-L04H04RSS7' 48 | } 49 | 50 | }) 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "nuxt build", 5 | "dev": "nuxt dev", 6 | "dev2": "vercel env pull .env.development.local", 7 | "devPan": "npx nuxi dev --dotenv .env.pan", 8 | "devGame": "npx nuxi dev --dotenv .env.game", 9 | "preview": "npx nuxi preview --dotenv .env.pan", 10 | "runPan": "PORT=4000 npx nuxi preview --dotenv .env.pan", 11 | "runGame": "PORT=4001 npx nuxi preview --dotenv .env.game" 12 | }, 13 | "devDependencies": { 14 | "@types/marked": "^4.0.8", 15 | "@types/mysql": "^2.15.21", 16 | "@types/nprogress": "^0.2.0", 17 | "@types/request": "^2.48.8", 18 | "less": "^4.1.3", 19 | "nuxt": "^3.2.2", 20 | "nuxt-og-image": "^1.4.19", 21 | "nuxt-seo-kit": "^1.2.3", 22 | "source": "^0.0.3" 23 | }, 24 | "dependencies": { 25 | "@mdi/font": "^7.1.96", 26 | "@pinia/nuxt": "^0.4.6", 27 | "@vercel/analytics": "^1.0.1", 28 | "@vercel/kv": "^0.2.2", 29 | "axios": "^1.3.3", 30 | "github-markdown-css": "^5.2.0", 31 | "marked": "^4.2.12", 32 | "mysql2": "^3.1.2", 33 | "nprogress": "^0.2.0", 34 | "nuxt-gtag": "^0.5.7", 35 | "nuxt-vercel-analytics": "^0.2.2", 36 | "pinia": "^2.0.30", 37 | "request": "^2.88.2", 38 | "vuetify": "^3.1.4" 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /pages/[...path].vue: -------------------------------------------------------------------------------- 1 | 24 | 31 | 37 | -------------------------------------------------------------------------------- /pages/about.vue: -------------------------------------------------------------------------------- 1 | 18 | 23 | 29 | -------------------------------------------------------------------------------- /pages/donate.vue: -------------------------------------------------------------------------------- 1 | 9 | 35 | 41 | -------------------------------------------------------------------------------- /pages/init.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 14 | -------------------------------------------------------------------------------- /plugins/loading.ts: -------------------------------------------------------------------------------- 1 | import NProgress from 'nprogress'; 2 | 3 | export default defineNuxtPlugin(nuxtApp => { 4 | // console.log('loading.ts'); 5 | 6 | let bar = ref(null) 7 | nuxtApp.hook("app:beforeMount", () => { 8 | if (!bar.value) { 9 | bar.value = NProgress.configure({ 10 | easing: 'ease', 11 | speed: 500, 12 | showSpinner: false, 13 | trickleSpeed: 200, 14 | minimum: 0.3 15 | }) 16 | } 17 | }) 18 | 19 | nuxtApp.hook("page:start", () => { 20 | bar.value?.start() 21 | 22 | }) 23 | nuxtApp.hook("page:finish", () => { 24 | setTimeout(() => { 25 | bar.value?.done() 26 | }, 150) 27 | }) 28 | 29 | nuxtApp.hook("app:mounted", () => { 30 | setTimeout(() => { 31 | bar.value?.done() 32 | }, 150) 33 | }) 34 | nuxtApp.hook("app:error", () => { 35 | // 判断是否是客户端 36 | if (process.client) { 37 | bar.value?.done() 38 | } 39 | }) 40 | }) 41 | -------------------------------------------------------------------------------- /plugins/vuetify.ts: -------------------------------------------------------------------------------- 1 | import { createVuetify } from 'vuetify' 2 | import * as components from 'vuetify/components' 3 | import * as directives from 'vuetify/directives' 4 | 5 | export default defineNuxtPlugin(nuxtApp => { 6 | const vuetify = createVuetify({ 7 | components, 8 | directives, 9 | ssr: true, 10 | }) 11 | nuxtApp.vueApp.use(vuetify) 12 | }) 13 | -------------------------------------------------------------------------------- /public/README.md: -------------------------------------------------------------------------------- 1 | # 关于小莫 2 | 3 | 欢迎各位来到小莫的个人站,我是一只对各种事情都充满好奇和想法的野生小莫. 4 | 5 | ### 小莫的一些账号: 6 | - GitHub: https://github.com/3DMXM 7 | - steam: https://steamcommunity.com/id/3DMXM/ 8 | 9 | ### 小莫的一些作品 10 | - 原创 11 | - [荒野大镖客内置修改器RNT](https://mod.3dmgame.com/mod/147160) 12 | - [戴森内置修改器](https://mod.3dmgame.com/mod/173023) 13 | - [戴森球物品叠加Mod](https://mod.3dmgame.com/mod/172571) 14 | - [觅长生内置修改器](https://mod.3dmgame.com/mod/176840) 15 | - [暖雪内置修改器](https://mod.3dmgame.com/mod/181716) 16 | - [沙石镇时光内置修改器](https://mod.3dmgame.com/mod/185597) 17 | - [疯狂游戏大亨内置修改器](https://mod.3dmgame.com/mod/188197) 18 | - [太吾绘卷内置修改器](https://mod.3dmgame.com/mod/188315) 19 | - 翻译 20 | - [GTA5警察Mod-LSPDFR](https://mod.3dmgame.com/mod/69815) 21 | - [Unity Mod 管理工具(UMM)](https://mod.3dmgame.com/mod/44550) 22 | - [ReShade](https://mod.3dmgame.com/mod/47185) 23 | - [怪物猎人Mod管理工具 MHW Mod Manager](https://mod.3dmgame.com/mod/50098) 24 | - [只狼 FPS上限解锁补丁+无边框工具](https://mod.3dmgame.com/mod/71575) 25 | - [Pack File Manager(PFM)](https://mod.3dmgame.com/mod/131523) 26 | - [赛博朋克2077 存档编辑器](https://mod.3dmgame.com/mod/173275) 27 | - [NinjaRipper + 3ds max 插件](https://mod.3dmgame.com/mod/48574) 28 | - [MT Framework](https://mod.3dmgame.com/mod/48616) 29 | - [BepInEx 插件/Mod管理](https://mod.3dmgame.com/mod/172600) 30 | - [软件公司内置修改器](https://mod.3dmgame.com/mod/162709) 31 | - [荒野大镖客2 地图编辑器(Map Editor)](https://mod.3dmgame.com/mod/171751) 32 | -------------------------------------------------------------------------------- /public/ads.txt: -------------------------------------------------------------------------------- 1 | google.com, pub-5978423097771370, DIRECT, f08c47fec0942fa0 -------------------------------------------------------------------------------- /public/comment.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DMXM/one-nuxt/d2fefc2829afc9f1fef02cc0d565ce97d566488a/public/comment.png -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DMXM/one-nuxt/d2fefc2829afc9f1fef02cc0d565ce97d566488a/public/favicon.ico -------------------------------------------------------------------------------- /public/lazy_img.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/3DMXM/one-nuxt/d2fefc2829afc9f1fef02cc0d565ce97d566488a/public/lazy_img.webp -------------------------------------------------------------------------------- /server/OneDrive/cache.ts: -------------------------------------------------------------------------------- 1 | import { kv } from "@vercel/kv"; 2 | 3 | export class Cache { 4 | public static async set(key: string, value: any, opts: any = { ex: 60 * 60 * 1 }) { 5 | try { 6 | await kv.set(key, value, opts) 7 | } catch (error) { 8 | console.log(`error: ${error}`); 9 | } 10 | } 11 | 12 | public static async get(key: string) { 13 | // return kv.get(key) 14 | try { 15 | let data = await kv.get(key) 16 | return data 17 | } catch (error) { 18 | console.log(`error: ${error}`); 19 | return null 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /server/OneDrive/onedrive.ts: -------------------------------------------------------------------------------- 1 | 2 | import request from 'request'; 3 | import { SeverConfig } from '@/model/SeverConfig' 4 | import { GetNowTime } from '@/model/main' 5 | import { Cache } from '@/server/OneDrive/cache' 6 | import axios from 'axios' 7 | import { kv } from "@vercel/kv"; 8 | 9 | 10 | const { token } = SeverConfig; 11 | 12 | 13 | // 获取 RefreshToken 14 | export async function GetRefreshToken(code: string) { 15 | const url = "https://login.microsoftonline.com/common/oauth2/token"; 16 | const data = { 17 | grant_type: "authorization_code", 18 | client_id: token.clientId, 19 | client_secret: token.clientSecret, 20 | code: code, 21 | redirect_uri: token.redirectUri 22 | }; 23 | 24 | return axios.post(url, data, { 25 | headers: { 26 | "Content-Type": "application/x-www-form-urlencoded" 27 | } 28 | }) 29 | } 30 | 31 | // 获取 AccessToken 32 | export async function RefreshToken(refreshToken: string) { 33 | const url = "https://login.microsoftonline.com/common/oauth2/token"; 34 | const options = { 35 | method: "POST", 36 | url: url, 37 | headers: { 38 | "Content-Type": "application/x-www-form-urlencoded" 39 | }, 40 | form: { 41 | grant_type: "refresh_token", 42 | client_id: token.clientId, 43 | client_secret: token.clientSecret, 44 | refresh_token: refreshToken, 45 | redirect_uri: token.redirectUri, 46 | } 47 | }; 48 | 49 | return new Promise((resolve, reject) => { 50 | request(options, function (err, response, body) { 51 | if (!err) { 52 | let jsonData = JSON.parse(body); 53 | token.accessToken = jsonData.access_token; 54 | token.expires_on = jsonData.expires_on 55 | resolve(jsonData) 56 | // SaveToken(jsonData.access_token, jsonData.refresh_token, token.scopes, jsonData.expires_on, function (err2, result) { 57 | // // console.log("result", jsonData); 58 | // // callback(err2, [jsonData]); 59 | // callback(err2, [{ 60 | // accessToken: jsonData.access_token, 61 | // refreshToken: jsonData.refresh_token, 62 | // expiresOn: jsonData.expires_on, 63 | // }]) 64 | // }) 65 | } else { 66 | reject(err); 67 | } 68 | }) 69 | }) 70 | 71 | 72 | } 73 | 74 | 75 | // 获取文件列表 76 | export async function GetChildren(path: string): Promise { 77 | // 将 path 转换为url编码 78 | let UrlPath = encodeURIComponent(path); 79 | // 调用官方API 80 | const url = `https://graph.microsoft.com/v1.0/me/drive/root:${UrlPath}:/children?select=name,size,folder,@microsoft.graph.downloadUrl,lastModifiedDateTime,id`; 81 | const options = { 82 | method: "GET", 83 | url: url, 84 | headers: { 85 | "Authorization": `Bearer ${token.accessToken}`, 86 | "Content-Type": "application/x-www-form-urlencoded", 87 | }, 88 | }; 89 | 90 | 91 | return new Promise((resolve, reject) => { 92 | request(options, function (err, response, body) { 93 | if (!err) { 94 | let jsonData = JSON.parse(body); 95 | let ChildrenList: any[] = jsonData.value; 96 | // 只需要取 name、size、id、folder.childCount、lastModifiedDateTime 97 | let Children: any[] = []; 98 | if (ChildrenList?.length > 0) { 99 | ChildrenList.forEach(function (item) { 100 | let ext = null; 101 | if (!item.folder) { 102 | // 正则获取文件后缀 103 | let reg = /\.([^\.]+)$/; 104 | ext = reg.exec(item.name); 105 | if (ext) { 106 | ext = ext[1]; 107 | } else { 108 | ext = "folder"; 109 | } 110 | } 111 | let child = { 112 | file_fid: item.id, 113 | file_parent: path, 114 | file_name: item.name, 115 | file_type: ext || "folder", 116 | file_size: item.size, 117 | file_downloadUrl: item['@microsoft.graph.downloadUrl'] || "", 118 | childCount: item.folder || 0, 119 | lastModifiedDateTime: new Date(item.lastModifiedDateTime), 120 | file_up_time: GetNowTime(), 121 | } 122 | if (item.folder) { 123 | child.childCount = item.folder.childCount; 124 | } 125 | Children.push(child); 126 | }); 127 | 128 | Cache.set(path, Children, { 129 | // 1 小时 130 | ex: 60 * 60 * 1 131 | }) 132 | 133 | resolve(Children) 134 | } else { 135 | resolve(Children); 136 | } 137 | } else { 138 | // callback(err, null); 139 | reject(err); 140 | } 141 | }) 142 | 143 | }) 144 | } 145 | 146 | // 获取文件数据 147 | export async function GetFileData(file_id: string): Promise { 148 | // 调用官方API 149 | const url = `https://graph.microsoft.com/v1.0/me/drive/items/${file_id}/content`; 150 | const options = { 151 | method: "GET", 152 | url: url, 153 | headers: { 154 | "Authorization": `Bearer ${token.accessToken}`, 155 | "Content-Type": "application/x-www-form-urlencoded", 156 | }, 157 | }; 158 | 159 | return new Promise((resolve, reject) => { 160 | request(options, function (err, response, body) { 161 | if (!err) { 162 | // 写入缓存 163 | Cache.set(file_id, body); 164 | 165 | resolve(body) 166 | } else { 167 | reject(err); 168 | } 169 | }) 170 | }) 171 | 172 | 173 | } 174 | 175 | 176 | // // 获取OneDrive的Token 177 | // export function GetRefreshToken(code: string, callback: ICallback) { 178 | // const url = "https://login.microsoftonline.com/common/oauth2/token"; 179 | // const options = { 180 | // method: "POST", 181 | // url: url, 182 | // headers: { 183 | // "Content-Type": "application/x-www-form-urlencoded" 184 | // }, 185 | // form: { 186 | // grant_type: "authorization_code", 187 | // client_id: token.clientId, 188 | // client_secret: token.clientSecret, 189 | // code: code, 190 | // redirect_uri: token.redirectUri 191 | // } 192 | // }; 193 | 194 | // request(options, (err, res, body) => { 195 | // if (err) { 196 | // callback(err, null); 197 | // } else { 198 | // callback(null, JSON.parse(body)); 199 | // } 200 | // }); 201 | // } 202 | 203 | // // 刷新OneDrive的Token 204 | // export function RefreshToken(refreshToken: string, callback: ICallback) { 205 | // const url = "https://login.microsoftonline.com/common/oauth2/token"; 206 | // const options = { 207 | // method: "POST", 208 | // url: url, 209 | // headers: { 210 | // "Content-Type": "application/x-www-form-urlencoded" 211 | // }, 212 | // form: { 213 | // grant_type: "refresh_token", 214 | // client_id: token.clientId, 215 | // client_secret: token.clientSecret, 216 | // refresh_token: refreshToken, 217 | // redirect_uri: token.redirectUri, 218 | // } 219 | // }; 220 | 221 | // request(options, function (err, response, body) { 222 | // if (!err) { 223 | // let jsonData = JSON.parse(body); 224 | // token.accessToken = jsonData.access_token; 225 | // SaveToken(jsonData.access_token, jsonData.refresh_token, token.scopes, jsonData.expires_on, function (err2, result) { 226 | // // console.log("result", jsonData); 227 | // // callback(err2, [jsonData]); 228 | // callback(err2, [{ 229 | // accessToken: jsonData.access_token, 230 | // refreshToken: jsonData.refresh_token, 231 | // expiresOn: jsonData.expires_on, 232 | // }]) 233 | // }) 234 | // } else { 235 | // console.log(`RefreshToken:${err}`); 236 | // callback(err, null); 237 | // } 238 | // }) 239 | // } 240 | 241 | // // 列出 driveItem 的子项 242 | // export function GetChildren(path: string, callback: ICallback) { 243 | // // 将 path 转换为url编码 244 | // let UrlPath = encodeURIComponent(path); 245 | // // 调用官方API 246 | // const url = `https://graph.microsoft.com/v1.0/me/drive/root:${UrlPath}:/children?select=name,size,folder,@microsoft.graph.downloadUrl,lastModifiedDateTime,id`; 247 | // const options = { 248 | // method: "GET", 249 | // url: url, 250 | // headers: { 251 | // "Authorization": `Bearer ${token.accessToken}`, 252 | // "Content-Type": "application/x-www-form-urlencoded", 253 | // }, 254 | // }; 255 | // request(options, function (err, response, body) { 256 | // if (!err) { 257 | // let jsonData = JSON.parse(body); 258 | // let ChildrenList: any[] = jsonData.value; 259 | // // 只需要取 name、size、id、folder.childCount、lastModifiedDateTime 260 | // let Children: any[] = []; 261 | // if (ChildrenList?.length > 0) { 262 | // // console.log(ChildrenList); 263 | 264 | // ChildrenList.forEach(function (item) { 265 | // let ext = null; 266 | // if (!item.folder) { 267 | // // 正则获取文件后缀 268 | // let reg = /\.([^\.]+)$/; 269 | // ext = reg.exec(item.name); 270 | // if (ext) { 271 | // ext = ext[1]; 272 | // } else { 273 | // ext = "folder"; 274 | // } 275 | // } 276 | // let child = { 277 | // file_fid: item.id, 278 | // file_parent: path, 279 | // file_name: item.name, 280 | // file_type: ext || "folder", 281 | // file_size: item.size, 282 | // file_downloadUrl: item['@microsoft.graph.downloadUrl'] || "", 283 | // childCount: item.folder || 0, 284 | // lastModifiedDateTime: new Date(item.lastModifiedDateTime), 285 | // file_up_time: GetNowTime(), 286 | // } 287 | // if (item.folder) { 288 | // child.childCount = item.folder.childCount; 289 | // } 290 | // Children.push(child); 291 | // }); 292 | // // console.log(ChildrenList); 293 | // SaveFileList(Children, path, function (err, result) { 294 | // callback(err, Children); 295 | // }) 296 | // } else { 297 | // callback(jsonData, Children); 298 | // } 299 | // } else { 300 | // callback(err, null); 301 | // } 302 | // }) 303 | // } 304 | 305 | // // 获取文件数据 306 | // export function GetFileData(file_id: string, callback: ICallback) { 307 | // // 调用官方API 308 | // const url = `https://graph.microsoft.com/v1.0/me/drive/items/${file_id}/content`; 309 | // const options = { 310 | // method: "GET", 311 | // url: url, 312 | // headers: { 313 | // "Authorization": `Bearer ${token.accessToken}`, 314 | // "Content-Type": "application/x-www-form-urlencoded", 315 | // }, 316 | // }; 317 | // // console.log(`GetFileData请求:${url}`); 318 | // request(options, function (err, response, body) { 319 | // if (!err) { 320 | // SaveFileData(file_id, body, function (err, result) { 321 | // callback(err, body); 322 | // }) 323 | // } else { 324 | // callback(err, null); 325 | // } 326 | // }) 327 | // } 328 | -------------------------------------------------------------------------------- /server/api/GetItem.post.ts: -------------------------------------------------------------------------------- 1 | // /api/GetItem 2 | import { SeverConfig } from '@/model/SeverConfig' 3 | import { Cache } from '@/server/OneDrive/cache' 4 | import { GetChildren } from '@/server/OneDrive/onedrive' 5 | import { initialization } from '@/model/initialization' 6 | 7 | 8 | export default defineEventHandler(async (event: any) => { 9 | const { path } = await readBody(event) 10 | 11 | initialization() 12 | 13 | let { onedrive_root } = SeverConfig 14 | 15 | let parent = onedrive_root 16 | if (path.length > 0) { 17 | parent += "/" + path.join("/") 18 | } 19 | 20 | console.log("parent:", parent); 21 | 22 | let data: any = []; 23 | 24 | data = await Cache.get(parent) 25 | 26 | // let data = [] 27 | console.log('data:', data); 28 | 29 | if (!data) { 30 | console.log(`没有内容, 重新获取`); 31 | data = await GetChildren(parent) 32 | } 33 | 34 | // console.log(`data:`, data); 35 | 36 | return { 37 | code: '00', 38 | msg: "获取成功", 39 | items: data, 40 | } 41 | }) 42 | 43 | -------------------------------------------------------------------------------- /server/api/GetReadme.post.ts: -------------------------------------------------------------------------------- 1 | import { GetFileData } from '@/server/OneDrive/onedrive' 2 | import { Cache } from '@/server/OneDrive/cache' 3 | // import { kv } from "@vercel/kv"; 4 | 5 | export default defineEventHandler(async (event) => { 6 | 7 | const { fid } = await readBody(event) 8 | 9 | console.log("fid:", fid); 10 | 11 | 12 | let data = await Cache.get(fid); 13 | if (!data) { 14 | console.log(`没有内容, 重新获取`); 15 | data = await GetFileData(fid) 16 | } 17 | 18 | // console.log(`data:`, data); 19 | 20 | 21 | return { 22 | code: '00', 23 | msg: "获取成功", 24 | data: data 25 | } 26 | }) -------------------------------------------------------------------------------- /server/model/link.ts: -------------------------------------------------------------------------------- 1 | import fconnection from '@/server/model/mysql' 2 | import { ILink } from '@/model/Interfaces' 3 | 4 | 5 | // 获取短链列表 6 | export async function GetLinkList() { 7 | const connection = fconnection() 8 | return new Promise((resolve, reject) => { 9 | connection.query("select * from link", function (err, result) { 10 | if (err) { reject(err) } 11 | resolve(result) 12 | }) 13 | }) 14 | 15 | } 16 | 17 | // 通过短链获取长链 18 | export async function GetLink(short_link: string) { 19 | const connection = fconnection() 20 | return new Promise((resolve, reject) => { 21 | connection.query("select * from link where short_link = ?", [short_link], function (err, result) { 22 | if (err) { reject(err) } 23 | resolve(result) 24 | }) 25 | }) 26 | } 27 | 28 | // 添加短链 29 | export async function AddLink(short_link: string, long_link: string) { 30 | const connection = fconnection() 31 | return new Promise((resolve, reject) => { 32 | connection.query("insert into link(short_link, long_link, click_cot) values(?,?,?)", [short_link, long_link, 0], function (err, result) { 33 | if (err) { reject(err) } 34 | resolve(result) 35 | }) 36 | }) 37 | } 38 | 39 | // 编辑短链 40 | export async function EditLink(short_link: string, long_link: string, click_cot: number, id: number) { 41 | const connection = fconnection() 42 | return new Promise((resolve, reject) => { 43 | connection.query("update link set short_link = ?, long_link = ?, click_cot = ? where id = ?", [short_link, long_link, click_cot, id], function (err, result) { 44 | if (err) { reject(err) } 45 | resolve(result) 46 | }) 47 | }) 48 | } 49 | 50 | // 统计点击 51 | export async function ClickAdd(short_link: string) { 52 | const connection = fconnection() 53 | return new Promise((resolve, reject) => { 54 | connection.query("update link set click_cot = click_cot + 1 where short_link = ?", [short_link], function (err, result) { 55 | if (err) { reject(err) } 56 | resolve(result) 57 | }) 58 | }) 59 | } -------------------------------------------------------------------------------- /server/model/mysql.ts: -------------------------------------------------------------------------------- 1 | import mysql from 'mysql2' 2 | import { SeverConfig } from '@/model/SeverConfig' 3 | 4 | const { MySqlConfig, configName } = SeverConfig 5 | 6 | 7 | // 连接数据库 8 | const connection = mysql.createConnection(MySqlConfig[configName]) 9 | 10 | 11 | // export default (() => (connection)) 12 | export default (() => (connection)) -------------------------------------------------------------------------------- /server/routes/ForzaHorizon5/ForzaHorizon5/[...any].ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default defineEventHandler(async (event: any) => { 4 | 5 | return { 6 | code: '404', 7 | msg: '傻逼' 8 | } 9 | }) -------------------------------------------------------------------------------- /server/routes/Games/[...any].ts: -------------------------------------------------------------------------------- 1 | 2 | 3 | export default defineEventHandler(async (event: any) => { 4 | 5 | return { 6 | code: '404', 7 | msg: '傻逼' 8 | } 9 | }) -------------------------------------------------------------------------------- /stores/useConfig.ts: -------------------------------------------------------------------------------- 1 | 2 | export const useConfig = defineStore('config', { 3 | state: () => ({ 4 | MySqlConfig: { 5 | "pan": { 6 | host: 'localhost', // 数据库地址 7 | user: 'root', // 数据库用户名 8 | password: '123456', // 数据库密码 9 | database: 'pan', // 数据库名 10 | timezone: "08:00", // 时区 11 | }, 12 | "game": { 13 | host: 'localhost', // 数据库地址 14 | user: 'root', // 数据库用户名 15 | password: '123456', // 数据库密码 16 | database: 'game', // 数据库名 17 | timezone: "08:00", // 时区 18 | } 19 | }, 20 | token: { 21 | // scopes:'Files.ReadWrite.All profile openid email', // 请求的范围 22 | scopes: ['Files.ReadWrite.All', 'Files.Read.All', 'User.Read', 'offline_access'], // 请求的范围 23 | clientId: "9ed0b3ca-497d-43b4-a693-1506e19ee3ef", // 应用程序的 clientId 24 | clientSecret: "MCf8Q~460v2bWba2ge8qIjTSiziGYzOOyUib.aOk",// 应用程序的 clientSecret 25 | authority: "https://login.microsoftonline.com/common", // 应用程序的 authority 26 | redirectUri: "http://localhost:3001/init/redirect", // 应用程序的 redirectUri 27 | accessToken: "", // 应用程序的 accessToken 28 | }, 29 | configName: "pan" as 'pan' | 'game', 30 | onedrive_root: "/public" 31 | }) 32 | }) -------------------------------------------------------------------------------- /stores/useMain.ts: -------------------------------------------------------------------------------- 1 | export const useMain = defineStore('main', { 2 | state: () => ({ 3 | lazy_img: '/lazy_img.webp', 4 | wx: 'https://mod.3dmgame.com/static/upload/mod/202302/MOD63eb01f0e3e14.png@webp', 5 | zfb: 'https://mod.3dmgame.com/static/upload/mod/202302/MOD63eb01f0e2912.png@webp', 6 | logo: 'https://mod.3dmgame.com/static/upload/mod/202211/MOD6386ccf455c06.png@webp', 7 | leftMenu: null as boolean | null, 8 | leftMenuRail: false as boolean | null, 9 | 10 | }), 11 | 12 | getters: { 13 | // 相当于计算属性 14 | // double: (state) => state.n * 2, 15 | }, 16 | 17 | actions: { 18 | 19 | }, 20 | }) 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | // https://nuxt.com/docs/guide/concepts/typescript 3 | "extends": "./.nuxt/tsconfig.json" 4 | } 5 | --------------------------------------------------------------------------------