Skip to content

Commit

Permalink
Add folder deletion and bump itty-router-openapi
Browse files Browse the repository at this point in the history
  • Loading branch information
G4brym committed Dec 18, 2023
1 parent c5d5d34 commit 099dbb7
Show file tree
Hide file tree
Showing 8 changed files with 241 additions and 93 deletions.
78 changes: 78 additions & 0 deletions packages/dashboard-v2/src/appUtils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { api } from "boot/axios";
import { useMainStore } from "stores/main-store";

export const ROOT_FOLDER = "IA==" // IA== is a space

Expand Down Expand Up @@ -147,4 +148,81 @@ export const apiHandler = {
onUploadProgress: callback
})
},
fetchFile: async (bucket, prefix, delimiter = '/') => {
const mainStore = useMainStore();
let truncated = true
let cursor = null
let contentFiles = []
let contentFolders = []

while (truncated) {
const response = await api.get(`/buckets/${bucket}?include=customMetadata&include=httpMetadata`, {
params: {
delimiter: delimiter,
prefix: prefix && prefix !== '/' ? encode(prefix) : '',
cursor: cursor
}
})

truncated = response.data.truncated
cursor = response.data.cursor

if (response.data.objects) {
const files = response.data.objects.filter(function(obj) {
return !(obj.key.endsWith('/') && delimiter !== '') && obj.key !== prefix // Remove selected folder when delimiter is defined
}).map(function(obj) {
const date = new Date(obj.uploaded)

return {
...obj,
hash: encode(obj.key),
name: obj.key.replace(prefix, ''),
lastModified: timeSince(date),
timestamp: date.getTime(),
size: bytesToSize(obj.size),
sizeRaw: obj.size,
type: 'file',
icon: 'article',
color: 'grey',
}
}).filter(obj => {
// Remove hidden files
return !(mainStore.showHiddenFiles !== true && obj.name.startsWith('.'))
})

for (const f of files) {
contentFiles.push(f)
}
}

if (response.data.delimitedPrefixes) {
const folders = response.data.delimitedPrefixes.map(function (obj) {
return {
name: obj.replace(prefix, ''),
hash: encode(obj.key),
key: obj,
lastModified: '--',
timestamp: 0,
size: '--',
sizeRaw: 0,
type: 'folder',
icon: 'folder',
color: 'orange',
}
}).filter(obj => {
// Remove hidden files
return !(mainStore.showHiddenFiles !== true && obj.name.startsWith('.'))
})

for (const f of folders) {
contentFolders.push(f)
}
}
}

return [
...contentFolders,
...contentFiles
]
}
}
131 changes: 131 additions & 0 deletions packages/dashboard-v2/src/components/files/FileOptions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
<template>
<q-dialog v-model="deleteModal" @hide="deleteReset">
<q-card>
<q-card-section class="row column" v-if="deleteRow">
<q-avatar class="q-mb-md" icon="delete" color="red" text-color="white" />
<span v-if="deleteRow.type === 'folder'" class="q-ml-sm">Are you sure you want to delete the folder <code>{{deleteRow.name}}</code>, and
<code v-if="deleteFolderInnerFilesCount !== null">{{deleteFolderInnerFilesCount}}</code>
<code v-else><q-spinner color="primary"/></code>
files inside?</span>
<span v-else class="q-ml-sm">Are you sure you want to delete the file <code>{{deleteRow.name}}</code>?</span>
</q-card-section>

<q-card-actions align="right">
<q-btn flat label="Cancel" color="primary" v-close-popup />
<q-btn flat label="Delete" color="red" @click="deleteConfirm" />
</q-card-actions>
</q-card>
</q-dialog>
</template>

<script>
import { defineComponent } from "vue";
import { useMainStore } from "stores/main-store";
import { apiHandler, decode, encode, ROOT_FOLDER } from "src/appUtils";
import { useQuasar } from "quasar";
export default defineComponent({
name: 'FileOptions',
data: function () {
return {
deleteRow: null,
deleteFolderContents: [],
deleteModal: false,
deleteFolderInnerFilesCount: null,
newFolderName: ''
}
},
methods: {
deleteObject: async function(row) {
this.deleteModal = true
this.deleteRow = row
if (row.type === 'folder') {
this.deleteFolderContents = await apiHandler.fetchFile(this.selectedBucket, row.key, '')
this.deleteFolderInnerFilesCount = this.deleteFolderContents.length
}
},
deleteConfirm: async function() {
this.deleteModal = false
if (this.deleteRow.type === 'folder') {
const notif = this.q.notify({
group: false,
spinner: true,
message: 'Deleting files...',
caption: '0%',
timeout: 0
})
console.log(this.deleteFolderContents)
for (const [i, innerFile] of this.deleteFolderContents.entries()) {
if (innerFile.key) {
await apiHandler.deleteObject(innerFile.key, this.selectedBucket)
}
notif({
caption: `${parseInt(i*100/(this.deleteFolderInnerFilesCount+1))}%` // +1 because still needs to delete the folder
})
}
await apiHandler.deleteObject(this.deleteRow.key, this.selectedBucket)
notif({
icon: 'done', // we add an icon
spinner: false, // we reset the spinner setting so the icon can be displayed
caption: '100%',
message: 'Folder deleted!',
timeout: 2500 // we will timeout it in 2.5s
})
} else {
await apiHandler.deleteObject(this.deleteRow.key, this.selectedBucket)
this.q.notify({
group: false,
icon: 'done', // we add an icon
spinner: false, // we reset the spinner setting so the icon can be displayed
message: 'File deleted!',
timeout: 2500 // we will timeout it in 2.5s
})
}
this.$bus.emit('fetchFiles')
this.deleteReset()
},
deleteReset: function() {
this.deleteModal = false
this.deleteRow = null
this.deleteFolderInnerFilesCount = null
this.deleteFolderContents = []
},
onSubmit: async function() {
await apiHandler.createFolder(this.selectedFolder + this.newFolderName + '/', this.selectedBucket)
this.$bus.emit('fetchFiles')
this.modal = false
},
open: function() {
this.modal = true
}
},
computed: {
selectedBucket: function () {
return this.$route.params.bucket
},
selectedFolder: function () {
if (this.$route.params.folder && this.$route.params.folder !== ROOT_FOLDER) {
return decode(this.$route.params.folder)
}
return ''
},
},
setup() {
return {
mainStore: useMainStore(),
q: useQuasar()
};
},
})
</script>

<style scoped>
code {
background-color: #e9e9e9;
padding: 0.25em;
}
</style>
99 changes: 19 additions & 80 deletions packages/dashboard-v2/src/pages/files/FilesFolderPage.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@
:hide-pagination="true"
:rows-per-page-options="[0]"
column-sort-order="da"
:flat="true"
@rowClick="rowClick">
:flat="true">

<template v-slot:body-cell-name="prop">
<td class="flex" style="align-items: center">
Expand All @@ -27,14 +26,17 @@
</td>
</template>

<template v-slot:body-cell-options="">
<template v-slot:body-cell-options="prop">
<td class="text-right">
<q-btn round flat icon="more_vert" size="sm">
<q-menu>
<q-list style="min-width: 100px">
<q-item clickable v-close-popup> <!-- @click="rowClick(prop.row)" -->
<q-item clickable v-close-popup @click="openObject(prop.row)">
<q-item-section>Open</q-item-section>
</q-item>
<q-item clickable v-close-popup @click="$refs.options.deleteObject(prop.row)">
<q-item-section>Delete</q-item-section>
</q-item>
<q-item clickable v-close-popup>
<q-item-section>New incognito tab</q-item-section>
</q-item>
Expand All @@ -52,19 +54,21 @@
</q-page>

<file-preview ref="preview"/>
<file-options ref="options" />
</template>

<script>
import { defineComponent } from "vue";
import { api } from "boot/axios";
import { useMainStore } from "stores/main-store";
import { bytesToSize, decode, encode, ROOT_FOLDER, timeSince } from "../../appUtils";
import { apiHandler, bytesToSize, decode, encode, ROOT_FOLDER, timeSince } from "../../appUtils";
import FilePreview from "components/preview/FilePreview.vue";
import DragAndDrop from "components/utils/DragAndDrop.vue";
import FileOptions from "components/files/FileOptions.vue";
export default defineComponent({
name: 'FilesIndexPage',
components: { DragAndDrop, FilePreview },
components: { FileOptions, DragAndDrop, FilePreview },
data: function () {
return {
loading: false,
Expand Down Expand Up @@ -177,83 +181,18 @@ export default defineComponent({
this.$refs.preview.openFile(row)
}
},
openObject: function(row) {
if (row.type === 'folder') {
this.$router.push({ name: `files-folder`, params: { bucket: this.selectedBucket, folder: encode(row.key) }})
} else {
// console.log(row)
this.$refs.preview.openFile(row)
}
},
fetchFiles: async function () {
const self = this
this.loading = true
let truncated = true
let cursor = null
let contentFiles = []
let contentFolders = []
while (truncated) {
const response = await api.get(`/buckets/${this.selectedBucket}?include=customMetadata&include=httpMetadata`, {
params: {
delimiter: '/',
prefix: this.selectedFolder && this.selectedFolder !== '/' ? encode(this.selectedFolder) : '',
cursor: cursor
}
})
truncated = response.data.truncated
cursor = response.data.cursor
if (response.data.objects) {
const files = response.data.objects.filter(function(obj) {
return !obj.key.endsWith('/') // Remove selected folder
}).map(function(obj) {
const date = new Date(obj.uploaded)
return {
...obj,
hash: encode(obj.key),
name: obj.key.replace(self.selectedFolder, ''),
lastModified: timeSince(date),
timestamp: date.getTime(),
size: bytesToSize(obj.size),
sizeRaw: obj.size,
type: 'file',
icon: 'article',
color: 'grey',
}
}).filter(obj => {
// Remove hidden files
return !(this.mainStore.showHiddenFiles !== true && obj.name.startsWith('.'))
})
for (const f of files) {
contentFiles.push(f)
}
}
if (response.data.delimitedPrefixes) {
const folders = response.data.delimitedPrefixes.map(function (obj) {
return {
name: obj.replace(self.selectedFolder, ''),
hash: encode(obj.key),
key: obj,
lastModified: '--',
timestamp: 0,
size: '--',
sizeRaw: 0,
type: 'folder',
icon: 'folder',
color: 'orange',
}
}).filter(obj => {
// Remove hidden files
return !(this.mainStore.showHiddenFiles !== true && obj.name.startsWith('.'))
})
for (const f of folders) {
contentFolders.push(f)
}
}
}
this.rows = [
...contentFolders,
...contentFiles
]
this.rows = await apiHandler.fetchFile(this.selectedBucket, this.selectedFolder, '/')
this.loading = false
}
},
Expand Down
8 changes: 4 additions & 4 deletions worker/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion worker/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"wrangler": "^3.6.0"
},
"dependencies": {
"@cloudflare/itty-router-openapi": "^1.0.3",
"@cloudflare/itty-router-openapi": "^1.0.6",
"postal-mime": "^1.0.16"
},
"bin": {
Expand Down
Loading

0 comments on commit 099dbb7

Please sign in to comment.