mirror of
https://github.com/ApfelTeeSaft/R2-Explorer.git
synced 2026-09-02 03:53:25 +00:00
Fix small issues
This commit is contained in:
@@ -54,6 +54,7 @@ wrangler publish
|
||||
- Image thumbnail's using Cloudflare workers
|
||||
- Tooltip when hovering a file with absolute time in "x days time ago" format
|
||||
- Automatically load more files, when the bottom is reached (current limit is 1000 files)
|
||||
- bundle bootstrap icons instead of importing
|
||||
|
||||
## Known issues
|
||||
|
||||
|
||||
@@ -51,7 +51,7 @@ const apiHandler = {
|
||||
)
|
||||
},
|
||||
renameObject: (oldName, newName) => {
|
||||
return axios.post(`/api/buckets/${store.state.activeBucket}/rename`, {
|
||||
return axios.post(`/api/buckets/${store.state.activeBucket}/move`, {
|
||||
oldKey: encodeKey(oldName, store.state.currentFolder),
|
||||
newKey: encodeKey(newName, store.state.currentFolder),
|
||||
})
|
||||
@@ -59,7 +59,10 @@ const apiHandler = {
|
||||
multipartCreate: (file, folder) => {
|
||||
return axios.post(`/api/buckets/${store.state.activeBucket}/multipart/create`, null, {
|
||||
params: {
|
||||
key: encodeKey(file.name, folder)
|
||||
key: encodeKey(file.name, folder),
|
||||
httpMetadata: encodeKey(JSON.stringify({
|
||||
contentType: file.type
|
||||
}))
|
||||
}
|
||||
})
|
||||
},
|
||||
@@ -86,8 +89,16 @@ const apiHandler = {
|
||||
},
|
||||
uploadObjects: (file, folder, callback) => {
|
||||
folder = folder || store.state.currentFolder
|
||||
console.log(file)
|
||||
console.log(file.type)
|
||||
|
||||
return axios.post(`/api/buckets/${store.state.activeBucket}/upload?key=${encodeKey(file.name, folder)}`, file, {
|
||||
return axios.post(`/api/buckets/${store.state.activeBucket}/upload`, file, {
|
||||
params: {
|
||||
key: encodeKey(file.name, folder),
|
||||
httpMetadata: encodeKey(JSON.stringify({
|
||||
contentType: file.type
|
||||
}))
|
||||
},
|
||||
headers: {
|
||||
'Content-Type': 'multipart/form-data',
|
||||
},
|
||||
@@ -97,11 +108,10 @@ const apiHandler = {
|
||||
listObjects: async () => {
|
||||
const prefix = getCurrentFolder()
|
||||
|
||||
const response = await axios.get(`/api/buckets/${store.state.activeBucket}`, {
|
||||
const response = await axios.get(`/api/buckets/${store.state.activeBucket}?include=customMetadata&include=httpMetadata`, {
|
||||
params: {
|
||||
delimiter: '/',
|
||||
prefix: encodeKey(prefix),
|
||||
include: 'customMetadata'
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@@ -44,6 +44,14 @@
|
||||
<div v-html="fileData.replaceAll('\n', '<br>')"></div>
|
||||
</template>
|
||||
|
||||
<template v-else-if="type === 'json'">
|
||||
<pre v-html="JSON.stringify(fileData, null, 2)"></pre>
|
||||
</template>
|
||||
|
||||
<template v-else-if="type === 'html'">
|
||||
<pre v-html="fileData"></pre>
|
||||
</template>
|
||||
|
||||
<template v-else-if="type === 'markdown'">
|
||||
<div class="markdown" v-html="markdownParser(fileData)"></div>
|
||||
</template>
|
||||
|
||||
@@ -96,8 +96,9 @@ export default {
|
||||
for (const file of this.$store.state.files) {
|
||||
if (file.hash === this.$route.params.file) {
|
||||
apiHandler.downloadFile(file).then(response => {
|
||||
const filename = file.key.split('.json')[0]
|
||||
for (const att of response.data.attachments) {
|
||||
att.downloadUrl = `${self.$store.state.serverUrl}/api/buckets/${self.$store.state.activeBucket}/${btoa(unescape(encodeURIComponent(`${file.key}/${att.filename}`)))}`
|
||||
att.downloadUrl = `${self.$store.state.serverUrl}/api/buckets/${self.$store.state.activeBucket}/${btoa(unescape(encodeURIComponent(`${filename}/${att.filename}`)))}`
|
||||
}
|
||||
self.file = response.data
|
||||
})
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<div class="text-center">
|
||||
<p class="fs-3">This bucket don't have any emails yet!</p>
|
||||
<p class="fs-5">Learn how to setup the Email Explorer in the official documentation</p>
|
||||
<a href="https://r2explorer.dev/guides/setup-email-explorer/" class="fs-5">https://r2explorer.dev</a>
|
||||
<a target="_blank" href="https://r2explorer.dev/guides/setup-email-explorer/" class="fs-5">https://r2explorer.dev</a>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
@@ -70,7 +70,6 @@ export default {
|
||||
this.$watch(
|
||||
() => this.$route.params.folder,
|
||||
(newFolder) => {
|
||||
console.log(newFolder)
|
||||
if (this.$store.state.activeTab === 'email') {
|
||||
this.$store.dispatch('refreshObjects')
|
||||
}
|
||||
|
||||
@@ -18,7 +18,7 @@ require('bootstrap/js/dist/modal')
|
||||
|
||||
let url = window.location.origin
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
axios.defaults.baseURL = 'https://my-r2-explorer.g4brym.workers.dev'
|
||||
axios.defaults.baseURL = 'http://localhost:8787'
|
||||
}
|
||||
|
||||
store.commit('setServerUrl', url)
|
||||
|
||||
@@ -33,6 +33,16 @@ const PreviewConfigs = [
|
||||
extensions: ['csv'],
|
||||
type: 'csv',
|
||||
downloadType: 'text'
|
||||
},
|
||||
{
|
||||
extensions: ['json'],
|
||||
type: 'json',
|
||||
downloadType: 'text'
|
||||
},
|
||||
{
|
||||
extensions: ['html'],
|
||||
type: 'html',
|
||||
downloadType: 'text'
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@@ -60,10 +60,7 @@ export default createStore({
|
||||
}
|
||||
},
|
||||
loadServerConfigs (state, data) {
|
||||
state.user = {
|
||||
username: "[email protected]"
|
||||
}
|
||||
// state.user = data.user
|
||||
state.user = data.user
|
||||
state.config = data.config
|
||||
state.serverVersion = data.version
|
||||
},
|
||||
|
||||
@@ -3,11 +3,11 @@ import {Context} from "../../interfaces";
|
||||
import {OpenAPIRouteSchema} from "@cloudflare/itty-router-openapi/dist/src/types";
|
||||
import {z} from "zod";
|
||||
|
||||
export class RenameObject extends OpenAPIRoute {
|
||||
export class MoveObject extends OpenAPIRoute {
|
||||
static schema: OpenAPIRouteSchema = {
|
||||
operationId: 'post-bucket-rename-object',
|
||||
operationId: 'post-bucket-move-object',
|
||||
tags: ['Buckets'],
|
||||
summary: 'Rename object',
|
||||
summary: 'Move object',
|
||||
parameters: {
|
||||
bucket: Path(String),
|
||||
},
|
||||
@@ -30,7 +30,7 @@ export class RenameObject extends OpenAPIRoute {
|
||||
const newKey = decodeURIComponent(escape(atob(data.body.newKey)))
|
||||
|
||||
const object = await bucket.get(oldKey)
|
||||
const resp = await bucket.put(newKey, object.body, {customMetadata: object.customMetadata})
|
||||
const resp = await bucket.put(newKey, object.body, {customMetadata: object.customMetadata, httpMetadata: object.httpMetadata})
|
||||
|
||||
await bucket.delete(oldKey)
|
||||
|
||||
@@ -12,6 +12,7 @@ export class CreateUpload extends OpenAPIRoute {
|
||||
bucket: Path(String),
|
||||
key: Query(z.string().optional().describe('base64 encoded file key')),
|
||||
customMetadata: Query(z.string().optional().describe('base64 encoded json string')),
|
||||
httpMetadata: Query(z.string().optional().describe('base64 encoded json string')),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,12 +27,18 @@ export class CreateUpload extends OpenAPIRoute {
|
||||
const bucket = env[data.params.bucket]
|
||||
|
||||
const key = decodeURIComponent(escape(atob(data.query.key)))
|
||||
|
||||
let customMetadata = undefined
|
||||
if (data.query.customMetadata) {
|
||||
customMetadata = decodeURIComponent(escape(atob(data.query.key)))
|
||||
customMetadata = JSON.parse(decodeURIComponent(escape(atob(data.query.customMetadata))))
|
||||
}
|
||||
|
||||
const multipartUpload = await bucket.createMultipartUpload(key, {customMetadata: customMetadata});
|
||||
let httpMetadata = undefined
|
||||
if (data.query.httpMetadata) {
|
||||
httpMetadata = JSON.parse(decodeURIComponent(escape(atob(data.query.httpMetadata))))
|
||||
}
|
||||
|
||||
const multipartUpload = await bucket.createMultipartUpload(key, {customMetadata: customMetadata, httpMetadata: httpMetadata});
|
||||
|
||||
return {
|
||||
uploadId: multipartUpload.uploadId,
|
||||
|
||||
@@ -12,6 +12,7 @@ export class PutObject extends OpenAPIRoute {
|
||||
bucket: Path(String),
|
||||
key: Query(z.string().optional().describe('base64 encoded file key')),
|
||||
customMetadata: Query(z.string().optional().describe('base64 encoded json string')),
|
||||
httpMetadata: Query(z.string().optional().describe('base64 encoded json string')),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -26,11 +27,17 @@ export class PutObject extends OpenAPIRoute {
|
||||
const bucket = env[data.params.bucket]
|
||||
|
||||
let key = decodeURIComponent(escape(atob(data.query.key)))
|
||||
|
||||
let customMetadata = undefined
|
||||
if (data.query.customMetadata) {
|
||||
customMetadata = decodeURIComponent(escape(atob(data.query.key)))
|
||||
customMetadata = JSON.parse(decodeURIComponent(escape(atob(data.query.customMetadata))))
|
||||
}
|
||||
|
||||
return await bucket.put(key, request.body, {customMetadata: customMetadata})
|
||||
let httpMetadata = undefined
|
||||
if (data.query.httpMetadata) {
|
||||
httpMetadata = JSON.parse(decodeURIComponent(escape(atob(data.query.httpMetadata))))
|
||||
}
|
||||
|
||||
return await bucket.put(key, request.body, {customMetadata: customMetadata, httpMetadata: httpMetadata})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import {config} from "../settings";
|
||||
import {ListBuckets} from "./api/listBuckets";
|
||||
import {ListObjects} from "./api/listObjects";
|
||||
import {RenameObject} from "./api/renameObject";
|
||||
import {MoveObject} from "./api/moveObject";
|
||||
import {CreateFolder} from "./api/createFolder";
|
||||
import {PutObject} from "./api/putObject";
|
||||
import {DeleteObject} from "./api/deleteObject";
|
||||
@@ -22,7 +22,7 @@ export const bucketsRouter = OpenAPIRouter({
|
||||
|
||||
bucketsRouter.get('', ListBuckets)
|
||||
bucketsRouter.get('/:bucket', ListObjects)
|
||||
bucketsRouter.post('/:bucket/rename', RenameObject)
|
||||
bucketsRouter.post('/:bucket/move', MoveObject)
|
||||
bucketsRouter.post('/:bucket/folder', CreateFolder)
|
||||
bucketsRouter.post('/:bucket/upload', PutObject)
|
||||
bucketsRouter.post('/:bucket/multipart/create', CreateUpload)
|
||||
|
||||
+12
-6
@@ -1,4 +1,6 @@
|
||||
export async function dashboardProxy(request: any, env: any, context: any) {
|
||||
import {Context} from "./interfaces";
|
||||
|
||||
export async function dashboardProxy(request: any, env: any, context: Context) {
|
||||
// Initialize the default cache
|
||||
//@ts-ignore
|
||||
const cache = caches.default
|
||||
@@ -10,10 +12,14 @@ export async function dashboardProxy(request: any, env: any, context: any) {
|
||||
path = "/"
|
||||
}
|
||||
|
||||
// use .match() to see if we have a cache hit, if so return the caches response early
|
||||
let result = await cache.match(request)
|
||||
if (result) {
|
||||
return result
|
||||
let result
|
||||
|
||||
if (context.config.cacheAssets !== false) {
|
||||
// use .match() to see if we have a cache hit, if so return the caches response early
|
||||
result = await cache.match(request)
|
||||
if (result) {
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
let dashboardUrl = 'https://demo.r2explorer.dev'
|
||||
@@ -41,7 +47,7 @@ export async function dashboardProxy(request: any, env: any, context: any) {
|
||||
},
|
||||
})
|
||||
|
||||
if (response.status !== 200) {
|
||||
if (response.status === 200 && context.config.cacheAssets !== false) {
|
||||
// before returning the response we put a clone of our response object into the cache so it can be resolved later
|
||||
context.executionContext.waitUntil(cache.put(request, result.clone()))
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ export interface R2ExplorerConfig {
|
||||
targetBucket: string
|
||||
},
|
||||
showHiddenFiles?: string
|
||||
cacheAssets?: boolean
|
||||
// basicAuth?: BasicAuth | BasicAuth[] // TODO
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user