Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 47 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,26 +29,53 @@
* 管理页面支持分页加载图片

### 使用教程
* 1.fork项目到自己的github
* 2.注册CloudFlare并开通R2服务
![Upload](https://oss.tuqu.me/roim/blog/cf/r2.png)
* 3.找到Pages选项并且创建项目
![Upload](https://oss.tuqu.me/roim/blog/cf/pages1.png)
* 4.选择项目创建方式
![Upload](https://oss.tuqu.me/roim/blog/cf/pages2.png)
* 4.链接Github或GitLab并选需要构建的项目
![Upload](https://oss.tuqu.me/roim/blog/cf/pages3.png)
![Upload](https://oss.tuqu.me/roim/blog/cf/pages4.png)
* 5.设置环境变量
> 因为cloudflare默认的node版本较低需要手动指定版本,否在会导致构建失败.
![Upload](https://oss.tuqu.me/roim/blog/cf/pages5.png)
* 6.设置项目的函数信息绑定R2和KV服务
![Upload](https://oss.tuqu.me/roim/blog/cf/pages6.png)
![Upload](https://oss.tuqu.me/roim/blog/cf/pages7.png)
* 7.构建项目,提示成功即可访问
![Upload](https://oss.tuqu.me/roim/blog/cf/pages8.png)

> 注意:Pages的函数变量名称需要于项目的变量名称一致,如果需要修改functions里面的Env名空间,对应的文件是`[[path]].ts`
1. fork项目到自己的github

2. 注册CloudFlare并开通R2服务
![Upload](https://oss.tuqu.me/roim/blog/cf/r2.png)

3. 找到Pages选项并且创建项目
![Upload](https://oss.tuqu.me/roim/blog/cf/pages1.png)

4. 选择项目创建方式
![Upload](https://oss.tuqu.me/roim/blog/cf/pages2.png)

5. 链接Github或GitLab并选需要构建的项目
![Upload](https://oss.tuqu.me/roim/blog/cf/pages3.png)
![Upload](https://oss.tuqu.me/roim/blog/cf/pages4.png)

链接到仓库后,,选择`Vue框架预设`,,保存并部署

<img src="https://s2.loli.net/2024/06/25/WZKh6XdIOvSHMLD.png" alt="image-20240625125904324" style="zoom:25%;" />

6. 设置环境变量`BASE_URL`

<img src="https://s2.loli.net/2024/06/25/b7nU62XQANzuYjG.png" alt="image-20240625125627118" style="zoom:25%;" />

7. 设置项目的函数信息,绑定R2和KV服务
<img src="https://s2.loli.net/2024/06/25/iZIyafXTYenpmtw.png" alt="image-20240625125529675" style="zoom:25%;" />

```typescript
// 请确保变量名与`functions/rest/[[path]].ts`中一致
// ./functions/rest/[[path]].ts

export interface Env {
BASE_URL: string
XK: KVNamespace
PICX: R2Bucket
}
```

> 注意:Pages的函数变量名称需要于项目的变量名称一致,如果需要修改functions里面的Env名空间,对应的文件是`[[path]].ts`

8. 设置`PICX_AUTH_TOKEN`

前往绑定的KV中,添加`PICX_AUTH_TOKEN`条目,填写Token

<img src="https://s2.loli.net/2024/06/25/q6VGMuQxsZ7gojA.png" alt="image-20240625130106257" style="zoom: 25%;" />

9. 重试部署,提示成功即可使用


### 图床截图
![Upload](https://oss.tuqu.me/roim/blog/5.png)
Expand Down
1 change: 1 addition & 0 deletions functions/rest/[[path]].ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export const onRequest: PagesFunction<Env> = async (context : EventContext) => {
try {
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const response: Response | undefined = await router.handle(context.request, context.env);
response?.headers.set('Access-Control-Allow-Origin', '*');
return response ?? error(404, 'not found');
} catch (err) {
return error(500, (err as Error).message);
Expand Down
103 changes: 96 additions & 7 deletions functions/rest/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@ const auth = async (request : Request, env : Env) => {
return json(Fail("system not auth setting"))
}
if (authKey != token) {
return json(FailCode("auth fail", StatusCode.NotAuth))
if(!request.url.includes("list")){
return json(FailCode("auth fail"+request.url, StatusCode.NotAuth))
}
}
// return new Response('Not Authenticated', { status: 401 })
}
Expand All @@ -46,7 +48,73 @@ router.post('/checkToken', async (req : Request, env : Env) => {

// list image
router.post('/list', auth, async (req : Request, env : Env) => {
const data = await req.json() as ImgReq
var data;

try{
data= await req.json() as ImgReq

}catch(e){
let url=new URL(req.url)
data={
limit:url.searchParams.get("limit"),
delimiter:url.searchParams.get("delimiter"),
}
}

if (!data.limit) {
data.limit = 10
}
if (data.limit > 100) {
data.limit = 100
}
if (!data.delimiter) {
data.delimiter = "/"
}
let include = undefined
if (data.delimiter != "/") {
include = data.delimiter
}
// console.log(include)
const options = <R2ListOptions>{
limit: data.limit,
cursor: data.cursor,
delimiter: data.delimiter,
prefix: include
}
const list = await env.PICX.list(options)
console.log(list)
const truncated = list.truncated ? list.truncated : false
const cursor = list.cursor
const objs = list.objects
const urls = objs.map(it => {
return <ImgItem> {
url: `${env.BASE_URL}/rest/${it.key}`,
key: it.key,
size: it.size
}
})
return json(Ok(<ImgList>{
list: urls,
next: truncated,
cursor: cursor,
prefixes: list.delimitedPrefixes
}))
})
// list image
router.post('/listdir', auth, async (req : Request, env : Env) => {
var data;

try{
data= await req.json() as ImgReq

}catch(e){
let url=new URL(req.url)
data={
limit:url.searchParams.get("limit"),
delimiter:url.searchParams.get("delimiter"),
}
}

if (!data.limit) {
data.limit = 10
}
Expand All @@ -68,7 +136,7 @@ router.post('/list', auth, async (req : Request, env : Env) => {
prefix: include
}
const list = await env.PICX.list(options)
// console.log(list)
console.log(list)
const truncated = list.truncated ? list.truncated : false
const cursor = list.cursor
const objs = list.objects
Expand All @@ -91,6 +159,9 @@ router.post('/list', auth, async (req : Request, env : Env) => {
router.post('/upload', auth, async (req: Request, env : Env) => {
const files = await req.formData()
const images = files.getAll("files")
let q = files.get("prefix")
const prefix= q?q:""
console.log(prefix)
const errs = []
const urls = Array<ImgItem>()
for (let item of images) {
Expand All @@ -104,21 +175,23 @@ router.post('/upload', auth, async (req: Request, env : Env) => {
const header = new Headers()
header.set("content-type", fileType)
header.set("content-length", `${item.size}`)
const object = await env.PICX.put(filename, item.stream(), {
const object = await env.PICX.put(prefix+filename, item.stream(), {
httpMetadata: header,
}) as R2Object
if (object || object.key) {
urls.push({
key: object.key,
key: "123"+object.key,
size: object.size,
url: `${env.BASE_URL}/rest/${object.key}`,
filename: item.name
filename: "456"+item.name
})
}
}
return json(Build(urls, errs.toString()))
})



// 创建目录
router.post("/folder", auth, async (req: Request, env: Env) => {
try {
Expand All @@ -127,9 +200,25 @@ router.post("/folder", auth, async (req: Request, env: Env) => {
if (!regx.test(data.name)) {
return json(Fail("Folder name error"))
}
await env.PICX.put(data.name + '/', null)

let file=data.prehold;


const header = new Headers()
header.set("content-type", file.type)
header.set("content-length", `${file.size}`)

console.log(file)
return json(file)

console.log("ragbshfnj")
await env.PICX.put((data.name + '/').replace("//","/")+file.name, file.stream(), {
httpMetadata: header,
})

return json(Ok("Success"))
} catch (e) {
console.log(e)
return json(Fail("Create folder fail"))
}
})
Expand Down
3 changes: 2 additions & 1 deletion functions/rest/type.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export interface ImgReq {

// 文件夹名称
export interface Folder {
name: string
name: string,
prehold:File
}

export function NotAuth() : Result {
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"postcss": "^8.4.5",
"tailwindcss": "^3.2.4",
"typescript": "^4.4.4",
"vite": "^3.2.5",
"vite": "^3.2.10",
"wrangler": "^2.9.0"
}
}
Binary file added src/assets/prehold.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions src/plugins/router.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,10 @@ const router = createRouter({
{
path: '/auth',
component: () => import('../views/auth.vue')
// component: () => import('../views/UploadImages.vue')
// component: () => import('../views/ManageImages.vue')


},
{
path: '/:path(.*)',
Expand Down
1 change: 1 addition & 0 deletions src/utils/request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ request.interceptors.response.use(
)

export const requestListImages = (data : ImgReq): Promise<ImgList> => request.post('/rest/list', data)
export const requestListDir = (data : ImgReq): Promise<ImgList> => request.post('/rest/listdir', 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 checkToken = (data: AuthToken) => request.post('/rest/checkToken', data)
Expand Down
3 changes: 2 additions & 1 deletion src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,5 +40,6 @@ export interface ImgReq {
limit: Number
}
export interface Folder {
name: string
name: string,
prehold:File
}
47 changes: 34 additions & 13 deletions src/views/ManageImages.vue
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
已上传 {{ uploadedImages.length }} 张图片,共 {{ formatBytes(imagesTotalSize) }}
</div>
</div>
<div class="flex items-center justify-start">
<font-awesome-icon :icon="faFolderPlus" class="text-xl cursor-pointer text-3xl text-amber-300 mr-2" @click="addFolder" />
<div class="flex items-center justify-start" >
<font-awesome-icon :icon="faFolderPlus" v-if="0" class="text-xl cursor-pointer text-3xl text-amber-300 mr-2" @click="addFolder" />
<font-awesome-icon
:icon="faRedoAlt"
class="text-xl cursor-pointer text-indigo-400"
Expand Down Expand Up @@ -68,6 +68,7 @@ const changeFolder = (path : string) => {
delimiter.value = path
listImages()
}

const addFolder = () => {
ElMessageBox.prompt('请输入目录名称,仅支持英文名称', '新增目录', {
confirmButtonText: '创建',
Expand All @@ -76,17 +77,37 @@ const addFolder = () => {
inputErrorMessage: '无效的目录名称',
}).then(({ value }) => {
loading.value = true
createFolder(<Folder> {
name: value
}).then((res) => {
console.log(res)
ElMessage.success('文件见创建成功')
listImages()
}).catch(() => {
ElMessage.error('文件见创建失败')
}).finally(() => {
loading.value = false
})

const imagePath = new URL('../assets/prehold.png', import.meta.url).href;


const xhr = new XMLHttpRequest();
xhr.open('GET', imagePath, true);
xhr.responseType = 'blob';

xhr.onload = () => {
if (xhr.status === 200) {
const blob = xhr.response;
let file = new File([blob], 'yourimage.jpg', { type: blob.type });

console.log(file)
createFolder(<Folder> {
name: value,
prehold:file
}).then((res) => {
console.log(res)
ElMessage.success('文件夹创建成功')
listImages()
}).catch(() => {
ElMessage.error('文件夹创建失败')
}).finally(() => {
loading.value = false
})
}
};
xhr.send();


}).catch(() => {})
}
const listImages = () => {
Expand Down
Loading