From d952110e477e29f7e3a890e9039f8497f7e1e746 Mon Sep 17 00:00:00 2001 From: atom Date: Fri, 24 Mar 2023 15:23:15 +0800 Subject: [PATCH] 1.add auth token. --- functions/rest/routes/index.ts | 65 ++++++++++++++++++++++++++++++---- functions/rest/type.ts | 23 +++++++++++- src/main.ts | 1 + src/permission.ts | 13 +++++++ src/plugins/router.ts | 4 +++ src/utils/request.ts | 37 +++++++++++++++++-- src/utils/storage.ts | 37 +++++++++++++++++++ src/utils/types.ts | 3 ++ src/views/ManageImages.vue | 2 +- src/views/auth.vue | 45 +++++++++++++++++++++++ 10 files changed, 219 insertions(+), 11 deletions(-) create mode 100644 src/permission.ts create mode 100644 src/utils/storage.ts create mode 100644 src/views/auth.vue diff --git a/functions/rest/routes/index.ts b/functions/rest/routes/index.ts index 117751f..9cec42f 100644 --- a/functions/rest/routes/index.ts +++ b/functions/rest/routes/index.ts @@ -1,12 +1,51 @@ import { router } from '../router'; import { Env } from '../[[path]]' import { json } from 'itty-router-extras'; -import { Ok, Fail, Build, ImgItem, ImgList, ImgReq, Folder } from "../type"; +import StatusCode, { Ok, Fail, Build, ImgItem, ImgList, ImgReq, Folder, AuthToken, FailCode, NotAuth } from "../type"; import { checkFileType, getFileName, parseRange } from '../utils' import { R2ListOptions } from "@cloudflare/workers-types"; +const auth = async (request : Request, env : Env) => { + const method = request.method; + // console.log(method) + if (method == "GET" || method == "OPTIONS") { + return + } + // get user token + const token = request.headers.get('Authorization') + if (!token) { + return json(NotAuth()) + } + // with kv equal + const authKey = await env.XK.get('PICX_AUTH_TOKEN') + if (!authKey) { + return json(Fail("system not auth setting")) + } + if (authKey != token) { + return json(FailCode("auth fail", StatusCode.NotAuth)) + } + // return new Response('Not Authenticated', { status: 401 }) +} + +// 检测token是否有效 +router.post('/checkToken', async (req : Request, env : Env) => { + const data = await req.json() as AuthToken + const token = data.token + if (!token) { + return json(Ok(false)) + } + const authKey = await env.XK.get('PICX_AUTH_TOKEN') + if (!authKey) { + return json(Ok(false)) + } + if (authKey != token) { + return json(Ok(false)) + } + return json(Ok(true)) +}) + // list image -router.post('/list', async (req : Request, env : Env) => { +router.post('/list', auth, async (req : Request, env : Env) => { const data = await req.json() as ImgReq if (!data.limit) { data.limit = 10 @@ -49,7 +88,7 @@ router.post('/list', async (req : Request, env : Env) => { }) // batch upload file -router.post('/upload', async (req: Request, env : Env) => { +router.post('/upload', auth, async (req: Request, env : Env) => { const files = await req.formData() const images = files.getAll("files") const errs = [] @@ -81,22 +120,36 @@ router.post('/upload', async (req: Request, env : Env) => { }) // 创建目录 -router.post("/folder", async (req: Request, env: Env) => { +router.post("/folder", auth, async (req: Request, env: Env) => { try { const data = await req.json() as Folder const regx = /^[A-Za-z_]+$/ if (!regx.test(data.name)) { return json(Fail("Folder name error")) } - await env.PICX.put(data.name + '/', "") + await env.PICX.put(data.name + '/', null) return json(Ok("Success")) } catch (e) { return json(Fail("Create folder fail")) } }) +// 删除key +router.get('/del/:id+', async (req : Request, env: Env) => { + const key = req.params.id + if (!key) { + return json(Fail("not delete key")) + } + try { + await env.PICX.delete(key) + } catch (e) { + console.log(`img delete error:${e.message}`,) + } + return json(Ok(key)) +}) + // delete image -router.delete("/", async (req : Request, env: Env) => { +router.delete("/", auth, async (req : Request, env: Env) => { const params = await req.json() // console.log(params) const keys = params.keys; diff --git a/functions/rest/type.ts b/functions/rest/type.ts index bd8d991..2428dc8 100644 --- a/functions/rest/type.ts +++ b/functions/rest/type.ts @@ -29,6 +29,22 @@ export interface Folder { name: string } +export function NotAuth() : Result { + return { + code: StatusCode.NotAuth, + msg: "Not Authorization", + data: null + } +} + +export function FailCode(msg : string, code: number) : Result { + return { + code: code, + msg: msg, + data: null + } +} + export function Fail(msg : string) : Result { return { code: StatusCode.ERROR, @@ -52,6 +68,11 @@ export function Build(data : any, msg: string) : Result { } const StatusCode = { OK: 200, - ERROR: 500 + ERROR: 500, + NotAuth: 401 +} + +export interface AuthToken { + token: string } export default StatusCode diff --git a/src/main.ts b/src/main.ts index 534fcb9..b7e33c3 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,5 +1,6 @@ import { createApp } from 'vue' import App from './App.vue' +import './permission' import router from './plugins/router' import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome' import './app.css' diff --git a/src/permission.ts b/src/permission.ts new file mode 100644 index 0000000..d7d15f0 --- /dev/null +++ b/src/permission.ts @@ -0,0 +1,13 @@ +import router from "./plugins/router" +import storage from "./utils/storage"; + +router.beforeEach((to, from, next) => { + // console.log(to.path) + const path = to.path + const token = storage.local.get('auth-token') + if (path != '/auth' && !token) { + router.push('/auth') + return + } + next() +}) diff --git a/src/plugins/router.ts b/src/plugins/router.ts index 901ce16..104f19a 100644 --- a/src/plugins/router.ts +++ b/src/plugins/router.ts @@ -11,6 +11,10 @@ const router = createRouter({ path: '/up', component: () => import('../views/UploadImages.vue') }, + { + path: '/auth', + component: () => import('../views/auth.vue') + }, { path: '/:path(.*)', redirect: '/' diff --git a/src/utils/request.ts b/src/utils/request.ts index 4c80776..260c8c0 100644 --- a/src/utils/request.ts +++ b/src/utils/request.ts @@ -1,14 +1,44 @@ import axios from 'axios' import { ElNotification as elNotify } from 'element-plus' -import {ImgList, ImgDel, ImgReq, Folder, ImgItem} from "./types"; - +import {ImgList, ImgDel, ImgReq, Folder, ImgItem, AuthToken} from "./types" +import storage from "./storage" const request = axios.create({ baseURL: import.meta.env.VITE_APP_API_URL, timeout: 200000 }) +request.interceptors.request.use( + (config) => { + const token = storage.local.get('auth-token') + if (token) { + // @ts-ignore + config.headers['Authorization'] = token + } + return config + } +) + request.interceptors.response.use( - (response) => response.data.data, + (response) => { + const data = response.data; + // console.log(data) + if (data.code === 401) { + // 跳转到登陆页面 + // 移除token + storage.local.remove('auth-token') + window.location.href = '/auth' + return Promise.reject(data.msg) + } + if (data.code !== 200) { + elNotify({ + message: data.msg, + duration: 0, + type: 'error' + }) + return Promise.reject(data.msg) + } + return data.data + }, (error) => { elNotify({ message: error?.response?.data || String(error), @@ -22,4 +52,5 @@ request.interceptors.response.use( export const requestListImages = (data : ImgReq): Promise => request.post('/rest/list', data) export const requestUploadImages = (data: FormData) : Promise => request.post('/rest/upload', data) export const createFolder = (data: Folder) => request.post('/rest/folder', data) +export const checkToken = (data: AuthToken) => request.post('/rest/checkToken', data) export const requestDeleteImage = (data: ImgDel) => request.delete('/rest', { data }) diff --git a/src/utils/storage.ts b/src/utils/storage.ts new file mode 100644 index 0000000..7560022 --- /dev/null +++ b/src/utils/storage.ts @@ -0,0 +1,37 @@ +const globalObj = {} +const KEY_PEX = 'ROIM-' +class WebStorage { + storage: Storage; + constructor(storage: Storage) { + this.storage = storage + } + get(key : string) { + const valueStr = this.storage.getItem(KEY_PEX + key) + if (!valueStr) { + return '' + } + if (!valueStr) { + return '' + } + try { + return JSON.parse(valueStr) + } catch (e) { + return valueStr + } + } + set(key : string, value : any) { + const valueStr = JSON.stringify(value) + return this.storage.setItem(KEY_PEX + key, valueStr) + } + remove(key : string) { + this.storage.removeItem(KEY_PEX + key) + } + removeAll() { + this.storage.clear() + } +} +const storage = { + local: new WebStorage(window.localStorage), + session: new WebStorage(window.sessionStorage) +} +export default storage diff --git a/src/utils/types.ts b/src/utils/types.ts index cc34dfc..6deaa77 100644 --- a/src/utils/types.ts +++ b/src/utils/types.ts @@ -11,6 +11,9 @@ export type UploadedImage = { expiresAt: number } +export interface AuthToken { + token: string +} export interface ImgItem { key : string url : string diff --git a/src/views/ManageImages.vue b/src/views/ManageImages.vue index 1dabd61..fff7e35 100644 --- a/src/views/ManageImages.vue +++ b/src/views/ManageImages.vue @@ -25,7 +25,7 @@ {{ it }} -
+
+
+
+ + 保存 +
+
+ + + + +