Implement some flow and cleanup API call for file manager

This commit is contained in:
Dane Everitt 2018-09-23 16:06:23 -07:00
parent c3ef290145
commit aee42df3ad
No known key found for this signature in database
GPG key ID: EEA66103B3D71F53
9 changed files with 186 additions and 118 deletions

View file

@ -6,6 +6,7 @@
"flowtype-errors/show-errors": 2, "flowtype-errors/show-errors": 2,
"semi": "off", "semi": "off",
"indent": ["error", 4], "indent": ["error", 4],
"comma-dangle": ["error", "always-multiline"] "comma-dangle": ["error", "always-multiline"],
"no-unused-vars": "warn"
} }
} }

View file

@ -1,5 +1,5 @@
[ignore] [ignore]
<PROJECT_ROOT>/vendor/.* <PROJECT_ROOT>/vendor/symfony/.*
<PROJECT_ROOT>/tests/.* <PROJECT_ROOT>/tests/.*
<PROJECT_ROOT>/storage/.* <PROJECT_ROOT>/storage/.*
<PROJECT_ROOT>/app/.* <PROJECT_ROOT>/app/.*

View file

@ -0,0 +1,25 @@
// @flow
import axios from 'axios';
import type { AxiosInstance } from 'axios';
// This token is set in the bootstrap.js file at the beginning of the request
// and is carried through from there.
// const token: string = '';
const http: AxiosInstance = axios.create({
'X-Requested-With': 'XMLHttpRequest',
'Accept': 'application/json',
'Content-Type': 'application/json',
});
// If we have a phpdebugbar instance registered at this point in time go
// ahead and route the response data through to it so things show up.
if (typeof window.phpdebugbar !== 'undefined') {
http.interceptors.response.use(response => {
window.phpdebugbar.ajaxHandler.handle(response.request);
return response;
});
}
export default http;

View file

@ -0,0 +1,50 @@
// @flow
import http from './../http';
import filter from 'lodash/filter';
import isObject from 'lodash/isObject';
import route from '../../../../../vendor/tightenco/ziggy/src/js/route';
export interface DirectoryContentsResponse {
files: Object,
directories: Object,
editable: Array<string>,
}
/**
* Get the contents of a specific directory for a given server.
*
* @param {String} server
* @param {String} directory
* @return {Promise<DirectoryContentsResponse>}
*/
export function getDirectoryContents(server: string, directory: string): Promise<DirectoryContentsResponse> {
return new Promise((resolve, reject) => {
http.get(route('server.files', { server, directory }))
.then((response) => {
return resolve({
files: filter(response.data.contents, function (o) {
return o.file;
}),
directories: filter(response.data.contents, function (o) {
return o.directory;
}),
editable: response.data.editable,
});
})
.catch(err => {
if (err.response && err.response.status === 404) {
return reject('The directory you requested could not be located on the server');
}
if (err.response.data && isObject(err.response.data.errors)) {
err.response.data.errors.forEach(error => {
return reject(error.detail);
});
}
return reject(err);
});
});
}
export default getDirectoryContents;

View file

@ -1,3 +1,4 @@
// @flow
import Vue from 'vue'; import Vue from 'vue';
import Vuex from 'vuex'; import Vuex from 'vuex';
import vuexI18n from 'vuex-i18n'; import vuexI18n from 'vuex-i18n';
@ -14,7 +15,7 @@ import { flash } from './mixins/flash';
import store from './store/index.js'; import store from './store/index.js';
import router from './router'; import router from './router';
window.events = new Vue; window.events = new Vue();
window.Ziggy = Ziggy; window.Ziggy = Ziggy;
Vue.use(Vuex); Vue.use(Vuex);
@ -22,6 +23,7 @@ Vue.use(VueRouter);
Vue.use(vuexI18n.plugin, store); Vue.use(vuexI18n.plugin, store);
Vue.use(VeeValidate); Vue.use(VeeValidate);
// $FlowFixMe: this is always going to be unhappy because we ignore the vendor dir.
const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default; const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default;
Vue.mixin({ methods: { route }}); Vue.mixin({ methods: { route }});
@ -30,8 +32,9 @@ Vue.mixin(flash);
Vue.i18n.add('en', Locales.en); Vue.i18n.add('en', Locales.en);
Vue.i18n.set('en'); Vue.i18n.set('en');
// $FlowFixMe
if (module.hot) { if (module.hot) {
module.hot.accept(); module.hot.accept();
} }
const app = new Vue({ store, router }).$mount('#pterodactyl'); new Vue({ store, router }).$mount('#pterodactyl');

View file

@ -42,12 +42,13 @@
</template> </template>
<script> <script>
// @flow
import map from 'lodash/map'; import map from 'lodash/map';
import filter from 'lodash/filter';
import isObject from 'lodash/isObject';
import { mapState } from 'vuex'; import { mapState } from 'vuex';
import type { Route } from 'vue-router';
import FileManagerFileRow from '../components/filemanager/FileManagerFileRow'; import FileManagerFileRow from '../components/filemanager/FileManagerFileRow';
import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow'; import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow';
import { getDirectoryContents, DirectoryContentsResponse } from '../../../api/server/getDirectoryContents';
export default { export default {
name: 'file-manager-page', name: 'file-manager-page',
@ -61,13 +62,13 @@
* Configure the breadcrumbs that display on the filemanager based on the directory that the * Configure the breadcrumbs that display on the filemanager based on the directory that the
* user is currently in. * user is currently in.
*/ */
breadcrumbs: function () { breadcrumbs: function (): Array<Object> {
const directories = this.currentDirectory.replace(/^\/|\/$/, '').split('/'); const directories: Array<string> = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
if (directories.length < 1 || !directories[0]) { if (directories.length < 1 || !directories[0]) {
return []; return [];
} }
return map(directories, function (value, key) { return map(directories, function (value: string, key: number) {
if (key === directories.length - 1) { if (key === directories.length - 1) {
return { directoryName: value }; return { directoryName: value };
} }
@ -77,21 +78,21 @@
path: directories.slice(0, key + 1).join('/'), path: directories.slice(0, key + 1).join('/'),
}; };
}); });
} },
}, },
watch: { watch: {
/** /**
* When the route changes reload the directory. * When the route changes reload the directory.
*/ */
'$route': function (to) { '$route': function (to: Route) {
this.currentDirectory = to.params.path || '/'; this.currentDirectory = to.params.path || '/';
}, },
/** /**
* Watch the current directory setting and when it changes update the file listing. * Watch the current directory setting and when it changes update the file listing.
*/ */
currentDirectory: function () { currentDirectory: function (): void {
this.listDirectory(); this.listDirectory();
}, },
@ -99,11 +100,11 @@
* When we reconnect to the Daemon make sure we grab a listing of all of the files * When we reconnect to the Daemon make sure we grab a listing of all of the files
* so that the error message disappears and we then load in a fresh listing. * so that the error message disappears and we then load in a fresh listing.
*/ */
connected: function () { connected: function (): void {
if (this.connected) { if (this.connected) {
this.listDirectory(); this.listDirectory();
} }
} },
}, },
data: function () { data: function () {
@ -126,42 +127,29 @@
/** /**
* List the contents of a directory. * List the contents of a directory.
*/ */
listDirectory: function () { listDirectory: function (): void {
this.loading = true; this.loading = true;
window.axios.get(this.route('server.files', { const directory: string = encodeURI(this.currentDirectory.replace(/^\/|\/$/, ''));
server: this.$route.params.id, getDirectoryContents(this.$route.params.id, directory)
directory: encodeURI(this.currentDirectory.replace(/^\/|\/$/, '')), .then((response: DirectoryContentsResponse) => {
})) this.files = response.files;
.then((response) => { this.directories = response.directories;
this.files = filter(response.data.contents, function (o) { this.editableFiles = response.editable;
return o.file;
});
this.directories = filter(response.data.contents, function (o) {
return o.directory;
});
this.editableFiles = response.data.editable;
this.errorMessage = null; this.errorMessage = null;
}) })
.catch(err => { .catch((err: string|Object) => {
console.error({err}); if (err instanceof String) {
if (err.response.status === 404) { this.errorMessage = err;
this.errorMessage = 'The directory you requested could not be located on the server.';
return; return;
} }
if (err.response.data && isObject(err.response.data.errors)) { console.error('An error was encountered while processing this request.', { err });
err.response.data.errors.forEach(error => {
this.errorMessage = error.detail;
});
}
}) })
.finally(() => { .then(() => {
this.loading = false; this.loading = false;
}); });
}, },
} },
}; };
</script> </script>

View file

@ -1,3 +1,4 @@
// @flow
import format from 'date-fns/format'; import format from 'date-fns/format';
/** /**
@ -7,13 +8,13 @@ import format from 'date-fns/format';
* @param {Number} bytes * @param {Number} bytes
* @return {String} * @return {String}
*/ */
export function readableSize (bytes) { export function readableSize (bytes: number): string {
if (Math.abs(bytes) < 1024) { if (Math.abs(bytes) < 1024) {
return `${bytes} Bytes`; return `${bytes} Bytes`;
} }
let u = -1; let u: number = -1;
const units = ['KiB', 'MiB', 'GiB', 'TiB']; const units: Array<string> = ['KiB', 'MiB', 'GiB', 'TiB'];
do { do {
bytes /= 1024; bytes /= 1024;
@ -29,6 +30,6 @@ export function readableSize (bytes) {
* @param {String} date * @param {String} date
* @return {String} * @return {String}
*/ */
export function formatDate (date) { export function formatDate (date: string): string {
return format(date, 'MMM D, YYYY [at] HH:MM'); return format(date, 'MMM D, YYYY [at] HH:MM');
} }

File diff suppressed because one or more lines are too long

View file

@ -15,7 +15,7 @@
} }
&.clickable { &.clickable {
@apply .cursor-pointer .no-underline; @apply .no-underline;
} }
&.active-selection, &.clickable:hover { &.active-selection, &.clickable:hover {