Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
mrfrase3 committed Dec 6, 2018
2 parents 75f8462 + 62eb08b commit 15e25c8
Show file tree
Hide file tree
Showing 15 changed files with 1,118 additions and 86 deletions.
96 changes: 69 additions & 27 deletions frontend/content.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,21 @@ import Vue from 'vue';
import store from '@/store';
import md5 from 'crypto-js/md5';
import typedArraysLib from 'crypto-js/lib-typedarrays';
import supportedFiles from '../supportedFiles';

const contentLib = {
async _get(id) {
const current = store.getters['content/current'];
const get = await store.dispatch('content/get', id);
store.commit('content/setCurrent', current._id);
return get;
},
async _patch(id, data) {
const current = store.getters['content/current'];
const get = await store.dispatch('content/patch', [id, data]);
store.commit('content/setCurrent', current._id);
return get;
},
_urltoFile(url, filename, type) {
type = type || (url.match(/^data:([^;]+);/) || '')[1];
return (fetch(url)
Expand All @@ -13,18 +26,18 @@ const contentLib = {
},
_fileMD5(file) {
return new Promise((resolve) => {
const reader = FileReader();
reader.addEventListener('load', () => {
const wordArray = typedArraysLib.WordArray.create(this.result);
resolve(md5(wordArray));
});
const reader = new FileReader();
reader.onload = (evt) => {
const wordArray = typedArraysLib.init(evt.target.result);
resolve(md5(wordArray).toString());
};
reader.readAsArrayBuffer(file);
});
},
async _findFile(name, opts = { createIfNotFound: false }) {
const { Content } = Vue.$FeathersVuex;
if (name instanceof Content) return name;
const currGroup = store.getters['groups/current']();
const currGroup = store.getters['groups/current'];
if (!currGroup) throw new Error('Not operating in the context of any group.');
let query = { groupId: currGroup._id, name };
if (/^[0-9abcdef]{24}$/.test(name)) query = { groupId: currGroup._id, _id: name };
Expand All @@ -33,111 +46,140 @@ const contentLib = {
if (res.length === 1) return new Content(res[0]);
if (res.length) throw new Error('File description given is too vuage.');
// check the remote store
res = await store.dispatch('content/find', { query }).data;
res = await store.dispatch('content/find', { query });
if (res.length === 1) return new Content(res[0]);
if (res.length) throw new Error('File description given is too vuage.');
// not found, so let's create
if (!opts.createIfNotFound || query._id) throw new Error('Specified file was not found.');
const current = store.getters['content/current'];
let cont = new Content({
name,
groupId: currGroup._id,
perms: opts.perms || [],
type: opts.type || undefined,
});
cont = await cont.save();
store.commit('content/setCurrent', current._id);
return cont;
},
async _readReq(content) {
const { name, presign } = content;
const filename = name.split('/').pop();
const { filename, presign } = content;
const res = await fetch(presign.url);
return new File([await res.blob()], filename, res.headers['Content-Type']);
},
async _writeReq(content, payload) {
// convert payload to file
const { name, presign, type } = content;
const filename = name.split('/').pop();
const { presign, type } = content;
const filename = content.key.split('/').pop();// name.split('/').pop();
let file = null;
if (typeof payload === 'string') {
if (/^((data:)|(https{0,1}:\/\/)|(s{0,1}ftp:\/\/))/.test(payload)) {
payload = await this._urltoFile(payload, filename, type);
} else payload = new Blob([payload], { type });
}
if (payload instanceof File) file = payload;
else if (payload instanceof File) {
else if (payload instanceof Blob) {
file = new File([payload], filename, { lastModified: Date.now() });
}
if (!file) throw new Error('Unknown payload type.');
// gen md5
const filemd5 = this._fileMD5(file);
const filemd5 = await this._fileMD5(file);

// build request
const fd = new FormData();
presign.fields.forEach((v, i) => fd.append(i, v));
Object.keys(presign.fields).forEach(i => fd.append(i, presign.fields[i]));
fd.append('file', file);

// send request
await fetch(presign.url, {
method: 'POST',
method: presign.method,
headers: {
'Content-Type': 'multipart/form-data',
// 'Content-Type': 'multipart/form-data',
'Content-MD5': filemd5,
},
body: fd,
});
await store.dispatch('content/patch', [content._id, { md5: filemd5 }]);
return file;
},
async _getPresign(content, method) {
const { Content } = Vue.$FeathersVuex;
if (method === 'read') {
if (content.presign && content.presign.expires > Date.now() && !content.presign.fields) {
if (content.presign && content.presign.expires > Date.now() && content.presign.method === 'GET') {
return content;
}
return new Content(await store.dispatch('content/get', [content._id]));
return new Content(await this._get(content._id));
} else if (method === 'write') {
if (content.presign && content.presign.expires > Date.now() && content.presign.fields) {
if (content.presign && content.presign.expires > Date.now() && content.presign.method !== 'GET') {
return content;
}
return new Content(await store.dispatch('content/patch', [content._id, {}]));
return new Content(await this._patch(content._id, {}));
}
throw new Error('Unknown Method');
},
async readFile(name) {
store.commit('content/setOperationPending');
let content = await this._findFile(name);
content = await this._getPresign(content, 'read');
const file = await this._readReq(content);
store.commit('content/unsetOperationPending');
return file;
},
async createFile(name, payload, type, perms) {
store.commit('content/setOperationPending');
if (payload instanceof File) type = payload.type; // eslint-disable-line prefer-destructuring
if (!type) throw new Error('Mime-Type must be specified when creating a file, either as an arg or passing a File as the payload.');
let content = await this._findFile(name, { createIfNotFound: true, type, perms });
content = await this._getPresign(content, 'read');
const file = await this._writeReq(content, payload);
let file = null;
if (type !== 'text/x-directory') {
content = await this._getPresign(content, 'write');
file = await this._writeReq(content, payload);
}
store.commit('content/unsetOperationPending');
return file;
},
async writeFile(name, payload) {
store.commit('content/setOperationPending');
let content = await this._findFile(name);
content = await this._getPresign(content, 'read');
content = await this._getPresign(content, 'write');
const file = await this._writeReq(content, payload);
store.commit('content/unsetOperationPending');
return file;
},
async renameFile(name, newName) {
store.commit('content/setOperationPending');
const { Content } = Vue.$FeathersVuex;
const content = await this._findFile(name);
return new Content(await store.dispatch('content/patch', [content._id, { name: newName }]));
let content = await this._findFile(name);
content = await this._patch(content._id, { name: newName });
store.commit('content/unsetOperationPending');
return new Content(content);
},
async changeFileType(name, type) {
store.commit('content/setOperationPending');
const { Content } = Vue.$FeathersVuex;
let content = await this._findFile(name);
const filename = `${content.filename.replace(/\.[\w-]+$/, '')}.${supportedFiles[type].ext[0]}`;
const path = content.path ? `${content.path}/` : '';
const newName = `${content.groupId}/${path}${filename}`;
content = await this._patch(content._id, { name: newName, type });
store.commit('content/unsetOperationPending');
return new Content(content);
},
async deleteFile(name) {
store.commit('content/setOperationPending');
let content = await this._findFile(name);
content = await content.remove();
store.commit('content/unsetOperationPending');
return content;
},
async copyFile(name, copyName) {
store.commit('content/setOperationPending');
const content = await this._findFile(name);
content.copy = content._id;
delete content._id;
content.name = copyName;
content.save();
await content.save();
store.commit('content/unsetOperationPending');
return content;
},
};

Expand Down
74 changes: 74 additions & 0 deletions frontend/explore.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<template>
<v-container fluid grid-list-md style="padding:0;"><v-layout>
<v-flex class="md4 lg3" v-if="$vuetify.breakpoint.mdAndUp" @click="log">
<tree />
</v-flex>
<v-flex class="xs12 md8 lg9">
<viewFile v-if="typeof $route.query.edit === 'undefined'" />
<editFile v-else />
</v-flex>
<new-file v-if="typeof $route.query.new !== 'undefined'"/>
</v-layout></v-container>
</template>

<script>
import { mapGetters } from 'vuex';
import store from '@/store';
import tree from './explorer/tree.vue';
import viewFile from './explorer/view.vue';
import editFile from './explorer/edit.vue';
import newFile from './explorer/new.vue';
const isDirectory = (path, parent = '', groupId) => {
const name = `${groupId}${parent ? `/${parent}` : ''}${path ? `/${path.replace(/\/$/, '')}` : ''}/.directory`;
const contents = store.getters['content/find']({ query: { name } }).data;
return contents.length ? contents[0]._id : null;
};
const isFile = (path, parent = '', groupId) => {
const name = `${groupId}${parent ? `/${parent}` : ''}${path ? `/${path.replace(/\/$/, '')}` : ''}`;
const contents = store.getters['content/find']({ query: { name } }).data;
return contents.length ? contents[0]._id : null;
};
const loadPath = (to, from, next) => {
const group = store.getters['groups/current'];
// console.log(to, store.getters['content/find']());
const { path } = to.params;
const { parent } = to.meta;
let pathId = isDirectory(path, parent, group._id);
if (!pathId) pathId = isFile(path, parent, group._id);
if (!pathId) return next((to.meta.route.path || '').replace('{groupId}', group.slugs ? group.slugs[0] : group._id).replace(':path', '') || '/');
store.commit('content/setCurrent', pathId);
return next();
};
export default {
components: {
tree,
viewFile,
editFile,
newFile,
},
methods: {
log() { console.log(this.$route.query); },
},
data() {
return {};
},
computed: {
...mapGetters('groups', { currentGroup: 'current' }),
...mapGetters('content', { currentContent: 'current' }),
},
beforeRouteEnter: async (to, from, next) => {
const group = store.getters['groups/current'];
await store.dispatch('content/find', { query: { groupId: group._id } });
loadPath(to, from, next);
},
beforeRouteUpdate: async (to, from, next) => {
const group = store.getters['groups/current'];
await store.dispatch('content/find', { query: { groupId: group._id } });
loadPath(to, from, next);
},
beforeRouteLeave: (to, from, next) => { store.commit('content/clearCurrent'); next(); },
};
</script>
34 changes: 34 additions & 0 deletions frontend/explorer/edit.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
<template>
<component v-if="displayData.edit" :is="displayData.edit" :opts="displayData.opts || {}" />
</template>

<script>
import { mapGetters } from 'vuex';
import supportedFiles from '../../supportedFiles';
import markdown from './edit/markdown.vue';
import textEdit from './edit/text.vue';
export default {
components: {
markdown,
textEdit,
},
data() {
return {};
},
computed: {
...mapGetters('content', { currentContent: 'current' }),
displayData() { return supportedFiles[this.currentContent.type]; },
},
updated() {
if (!supportedFiles[this.currentContent.type].edit) {
this.$route.replace({ ...this.$route, query: {} });
}
},
mounted() {
if (!supportedFiles[this.currentContent.type].edit) {
this.$route.replace({ ...this.$route, query: {} });
}
},
};
</script>
Loading

1 comment on commit 15e25c8

@mrfrase3
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mostly ref #2

Please sign in to comment.