1.add auth token.
This commit is contained in:
parent
3086303ebc
commit
d952110e47
|
@ -1,12 +1,51 @@
|
||||||
import { router } from '../router';
|
import { router } from '../router';
|
||||||
import { Env } from '../[[path]]'
|
import { Env } from '../[[path]]'
|
||||||
import { json } from 'itty-router-extras';
|
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 { checkFileType, getFileName, parseRange } from '../utils'
|
||||||
import { R2ListOptions } from "@cloudflare/workers-types";
|
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
|
// 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
|
const data = await req.json() as ImgReq
|
||||||
if (!data.limit) {
|
if (!data.limit) {
|
||||||
data.limit = 10
|
data.limit = 10
|
||||||
|
@ -49,7 +88,7 @@ router.post('/list', async (req : Request, env : Env) => {
|
||||||
})
|
})
|
||||||
|
|
||||||
// batch upload file
|
// 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 files = await req.formData()
|
||||||
const images = files.getAll("files")
|
const images = files.getAll("files")
|
||||||
const errs = []
|
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 {
|
try {
|
||||||
const data = await req.json() as Folder
|
const data = await req.json() as Folder
|
||||||
const regx = /^[A-Za-z_]+$/
|
const regx = /^[A-Za-z_]+$/
|
||||||
if (!regx.test(data.name)) {
|
if (!regx.test(data.name)) {
|
||||||
return json(Fail("Folder name error"))
|
return json(Fail("Folder name error"))
|
||||||
}
|
}
|
||||||
await env.PICX.put(data.name + '/', "")
|
await env.PICX.put(data.name + '/', null)
|
||||||
return json(Ok("Success"))
|
return json(Ok("Success"))
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return json(Fail("Create folder fail"))
|
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
|
// delete image
|
||||||
router.delete("/", async (req : Request, env: Env) => {
|
router.delete("/", auth, async (req : Request, env: Env) => {
|
||||||
const params = await req.json()
|
const params = await req.json()
|
||||||
// console.log(params)
|
// console.log(params)
|
||||||
const keys = params.keys;
|
const keys = params.keys;
|
||||||
|
|
|
@ -29,6 +29,22 @@ export interface Folder {
|
||||||
name: string
|
name: string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function NotAuth() : Result {
|
||||||
|
return <Result> {
|
||||||
|
code: StatusCode.NotAuth,
|
||||||
|
msg: "Not Authorization",
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function FailCode(msg : string, code: number) : Result {
|
||||||
|
return <Result> {
|
||||||
|
code: code,
|
||||||
|
msg: msg,
|
||||||
|
data: null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export function Fail(msg : string) : Result {
|
export function Fail(msg : string) : Result {
|
||||||
return <Result> {
|
return <Result> {
|
||||||
code: StatusCode.ERROR,
|
code: StatusCode.ERROR,
|
||||||
|
@ -52,6 +68,11 @@ export function Build(data : any, msg: string) : Result {
|
||||||
}
|
}
|
||||||
const StatusCode = {
|
const StatusCode = {
|
||||||
OK: 200,
|
OK: 200,
|
||||||
ERROR: 500
|
ERROR: 500,
|
||||||
|
NotAuth: 401
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface AuthToken {
|
||||||
|
token: string
|
||||||
}
|
}
|
||||||
export default StatusCode
|
export default StatusCode
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
|
import './permission'
|
||||||
import router from './plugins/router'
|
import router from './plugins/router'
|
||||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'
|
||||||
import './app.css'
|
import './app.css'
|
||||||
|
|
|
@ -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()
|
||||||
|
})
|
|
@ -11,6 +11,10 @@ const router = createRouter({
|
||||||
path: '/up',
|
path: '/up',
|
||||||
component: () => import('../views/UploadImages.vue')
|
component: () => import('../views/UploadImages.vue')
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: '/auth',
|
||||||
|
component: () => import('../views/auth.vue')
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/:path(.*)',
|
path: '/:path(.*)',
|
||||||
redirect: '/'
|
redirect: '/'
|
||||||
|
|
|
@ -1,14 +1,44 @@
|
||||||
import axios from 'axios'
|
import axios from 'axios'
|
||||||
import { ElNotification as elNotify } from 'element-plus'
|
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({
|
const request = axios.create({
|
||||||
baseURL: import.meta.env.VITE_APP_API_URL,
|
baseURL: import.meta.env.VITE_APP_API_URL,
|
||||||
timeout: 200000
|
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(
|
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) => {
|
(error) => {
|
||||||
elNotify({
|
elNotify({
|
||||||
message: error?.response?.data || String(error),
|
message: error?.response?.data || String(error),
|
||||||
|
@ -22,4 +52,5 @@ request.interceptors.response.use(
|
||||||
export const requestListImages = (data : ImgReq): Promise<ImgList> => request.post('/rest/list', data)
|
export const requestListImages = (data : ImgReq): Promise<ImgList> => request.post('/rest/list', data)
|
||||||
export const requestUploadImages = (data: FormData) : Promise<ImgItem[]> => request.post('/rest/upload', data)
|
export const requestUploadImages = (data: FormData) : Promise<ImgItem[]> => request.post('/rest/upload', data)
|
||||||
export const createFolder = (data: Folder) => request.post('/rest/folder', 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 })
|
export const requestDeleteImage = (data: ImgDel) => request.delete('/rest', { data })
|
||||||
|
|
|
@ -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
|
|
@ -11,6 +11,9 @@ export type UploadedImage = {
|
||||||
expiresAt: number
|
expiresAt: number
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface AuthToken {
|
||||||
|
token: string
|
||||||
|
}
|
||||||
export interface ImgItem {
|
export interface ImgItem {
|
||||||
key : string
|
key : string
|
||||||
url : string
|
url : string
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
<span v-else class="pl-2 text-gray-600"> {{ it }}</span>
|
<span v-else class="pl-2 text-gray-600"> {{ it }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="grid gap-4 lg:grid-cols-4 grid-cols-2">
|
<div class="grid gap-2 lg:gap-4 lg:grid-cols-4 grid-cols-2">
|
||||||
<transition-group name="el-fade-in-linear">
|
<transition-group name="el-fade-in-linear">
|
||||||
<div
|
<div
|
||||||
class="col-span-1 md:col-span-1"
|
class="col-span-1 md:col-span-1"
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
<template>
|
||||||
|
<div class="mx-auto max-w-6xl my-4 px-4 relative">
|
||||||
|
<div class="mx-auto max-w-2xl mt-10">
|
||||||
|
<el-input v-model="token" size="large" class="h-12" placeholder="请输入认证Token" />
|
||||||
|
<el-button :loading="loading" class="mt-4 w-full lg:w-6xl" size="large" type="primary" @click="saveToken">保存</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ElInput, ElButton, ElMessage } from 'element-plus'
|
||||||
|
import storage from '../utils/storage'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
import { useRouter } from 'vue-router'
|
||||||
|
import { checkToken } from '../utils/request'
|
||||||
|
const token = ref('')
|
||||||
|
const loading = ref(false)
|
||||||
|
const router = useRouter()
|
||||||
|
const saveToken = () => {
|
||||||
|
loading.value = true
|
||||||
|
if (!token.value) {
|
||||||
|
loading.value = false
|
||||||
|
ElMessage.error('请输入token')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
checkToken({
|
||||||
|
token: token.value
|
||||||
|
}).then(res => {
|
||||||
|
if (res) {
|
||||||
|
// 检测token是否有效
|
||||||
|
storage.local.set('auth-token', token.value)
|
||||||
|
router.push('/')
|
||||||
|
} else {
|
||||||
|
ElMessage.error('Token无效')
|
||||||
|
}
|
||||||
|
}).finally(() => {
|
||||||
|
loading.value = false
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
|
||||||
|
</style>
|
Loading…
Reference in New Issue