Implement some flow and cleanup API call for file manager
This commit is contained in:
parent
c3ef290145
commit
aee42df3ad
9 changed files with 186 additions and 118 deletions
|
@ -6,6 +6,7 @@
|
|||
"flowtype-errors/show-errors": 2,
|
||||
"semi": "off",
|
||||
"indent": ["error", 4],
|
||||
"comma-dangle": ["error", "always-multiline"]
|
||||
"comma-dangle": ["error", "always-multiline"],
|
||||
"no-unused-vars": "warn"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[ignore]
|
||||
<PROJECT_ROOT>/vendor/.*
|
||||
<PROJECT_ROOT>/vendor/symfony/.*
|
||||
<PROJECT_ROOT>/tests/.*
|
||||
<PROJECT_ROOT>/storage/.*
|
||||
<PROJECT_ROOT>/app/.*
|
||||
|
|
25
resources/assets/scripts/api/http.js
Normal file
25
resources/assets/scripts/api/http.js
Normal 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;
|
50
resources/assets/scripts/api/server/getDirectoryContents.js
Normal file
50
resources/assets/scripts/api/server/getDirectoryContents.js
Normal 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;
|
|
@ -1,3 +1,4 @@
|
|||
// @flow
|
||||
import Vue from 'vue';
|
||||
import Vuex from 'vuex';
|
||||
import vuexI18n from 'vuex-i18n';
|
||||
|
@ -14,7 +15,7 @@ import { flash } from './mixins/flash';
|
|||
import store from './store/index.js';
|
||||
import router from './router';
|
||||
|
||||
window.events = new Vue;
|
||||
window.events = new Vue();
|
||||
window.Ziggy = Ziggy;
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
@ -22,16 +23,18 @@ Vue.use(VueRouter);
|
|||
Vue.use(vuexI18n.plugin, store);
|
||||
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;
|
||||
|
||||
Vue.mixin({ methods: { route } });
|
||||
Vue.mixin({ methods: { route }});
|
||||
Vue.mixin(flash);
|
||||
|
||||
Vue.i18n.add('en', Locales.en);
|
||||
Vue.i18n.set('en');
|
||||
|
||||
// $FlowFixMe
|
||||
if (module.hot) {
|
||||
module.hot.accept();
|
||||
}
|
||||
|
||||
const app = new Vue({ store, router }).$mount('#pterodactyl');
|
||||
new Vue({ store, router }).$mount('#pterodactyl');
|
||||
|
|
|
@ -42,126 +42,114 @@
|
|||
</template>
|
||||
|
||||
<script>
|
||||
import map from 'lodash/map';
|
||||
import filter from 'lodash/filter';
|
||||
import isObject from 'lodash/isObject';
|
||||
import { mapState } from 'vuex';
|
||||
import FileManagerFileRow from '../components/filemanager/FileManagerFileRow';
|
||||
import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow';
|
||||
// @flow
|
||||
import map from 'lodash/map';
|
||||
import { mapState } from 'vuex';
|
||||
import type { Route } from 'vue-router';
|
||||
import FileManagerFileRow from '../components/filemanager/FileManagerFileRow';
|
||||
import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow';
|
||||
import { getDirectoryContents, DirectoryContentsResponse } from '../../../api/server/getDirectoryContents';
|
||||
|
||||
export default {
|
||||
name: 'file-manager-page',
|
||||
components: {FileManagerFolderRow, FileManagerFileRow},
|
||||
export default {
|
||||
name: 'file-manager-page',
|
||||
components: { FileManagerFolderRow, FileManagerFileRow },
|
||||
|
||||
computed: {
|
||||
...mapState('server', ['server', 'credentials']),
|
||||
...mapState('socket', ['connected']),
|
||||
computed: {
|
||||
...mapState('server', ['server', 'credentials']),
|
||||
...mapState('socket', ['connected']),
|
||||
|
||||
/**
|
||||
* Configure the breadcrumbs that display on the filemanager based on the directory that the
|
||||
* user is currently in.
|
||||
*/
|
||||
breadcrumbs: function () {
|
||||
const directories = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
|
||||
if (directories.length < 1 || !directories[0]) {
|
||||
return [];
|
||||
/**
|
||||
* Configure the breadcrumbs that display on the filemanager based on the directory that the
|
||||
* user is currently in.
|
||||
*/
|
||||
breadcrumbs: function (): Array<Object> {
|
||||
const directories: Array<string> = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
|
||||
if (directories.length < 1 || !directories[0]) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return map(directories, function (value: string, key: number) {
|
||||
if (key === directories.length - 1) {
|
||||
return { directoryName: value };
|
||||
}
|
||||
|
||||
return map(directories, function (value, key) {
|
||||
if (key === directories.length - 1) {
|
||||
return {directoryName: value};
|
||||
}
|
||||
return {
|
||||
directoryName: value,
|
||||
path: directories.slice(0, key + 1).join('/'),
|
||||
};
|
||||
});
|
||||
},
|
||||
},
|
||||
|
||||
return {
|
||||
directoryName: value,
|
||||
path: directories.slice(0, key + 1).join('/'),
|
||||
};
|
||||
});
|
||||
}
|
||||
watch: {
|
||||
/**
|
||||
* When the route changes reload the directory.
|
||||
*/
|
||||
'$route': function (to: Route) {
|
||||
this.currentDirectory = to.params.path || '/';
|
||||
},
|
||||
|
||||
watch: {
|
||||
/**
|
||||
* When the route changes reload the directory.
|
||||
*/
|
||||
'$route': function (to) {
|
||||
this.currentDirectory = to.params.path || '/';
|
||||
},
|
||||
|
||||
/**
|
||||
* Watch the current directory setting and when it changes update the file listing.
|
||||
*/
|
||||
currentDirectory: function () {
|
||||
this.listDirectory();
|
||||
},
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
connected: function () {
|
||||
if (this.connected) {
|
||||
this.listDirectory();
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
data: function () {
|
||||
return {
|
||||
currentDirectory: this.$route.params.path || '/',
|
||||
loading: true,
|
||||
errorMessage: null,
|
||||
|
||||
directories: [],
|
||||
editableFiles: [],
|
||||
files: [],
|
||||
};
|
||||
},
|
||||
|
||||
mounted: function () {
|
||||
/**
|
||||
* Watch the current directory setting and when it changes update the file listing.
|
||||
*/
|
||||
currentDirectory: function (): void {
|
||||
this.listDirectory();
|
||||
},
|
||||
|
||||
methods: {
|
||||
/**
|
||||
* List the contents of a directory.
|
||||
*/
|
||||
listDirectory: function () {
|
||||
this.loading = true;
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
connected: function (): void {
|
||||
if (this.connected) {
|
||||
this.listDirectory();
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
window.axios.get(this.route('server.files', {
|
||||
server: this.$route.params.id,
|
||||
directory: encodeURI(this.currentDirectory.replace(/^\/|\/$/, '')),
|
||||
}))
|
||||
.then((response) => {
|
||||
this.files = filter(response.data.contents, function (o) {
|
||||
return o.file;
|
||||
});
|
||||
data: function () {
|
||||
return {
|
||||
currentDirectory: this.$route.params.path || '/',
|
||||
loading: true,
|
||||
errorMessage: null,
|
||||
|
||||
this.directories = filter(response.data.contents, function (o) {
|
||||
return o.directory;
|
||||
});
|
||||
directories: [],
|
||||
editableFiles: [],
|
||||
files: [],
|
||||
};
|
||||
},
|
||||
|
||||
this.editableFiles = response.data.editable;
|
||||
this.errorMessage = null;
|
||||
})
|
||||
.catch(err => {
|
||||
console.error({err});
|
||||
if (err.response.status === 404) {
|
||||
this.errorMessage = 'The directory you requested could not be located on the server.';
|
||||
return;
|
||||
}
|
||||
mounted: function () {
|
||||
this.listDirectory();
|
||||
},
|
||||
|
||||
if (err.response.data && isObject(err.response.data.errors)) {
|
||||
err.response.data.errors.forEach(error => {
|
||||
this.errorMessage = error.detail;
|
||||
});
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
}
|
||||
};
|
||||
methods: {
|
||||
/**
|
||||
* List the contents of a directory.
|
||||
*/
|
||||
listDirectory: function (): void {
|
||||
this.loading = true;
|
||||
|
||||
const directory: string = encodeURI(this.currentDirectory.replace(/^\/|\/$/, ''));
|
||||
getDirectoryContents(this.$route.params.id, directory)
|
||||
.then((response: DirectoryContentsResponse) => {
|
||||
this.files = response.files;
|
||||
this.directories = response.directories;
|
||||
this.editableFiles = response.editable;
|
||||
this.errorMessage = null;
|
||||
})
|
||||
.catch((err: string|Object) => {
|
||||
if (err instanceof String) {
|
||||
this.errorMessage = err;
|
||||
return;
|
||||
}
|
||||
|
||||
console.error('An error was encountered while processing this request.', { err });
|
||||
})
|
||||
.then(() => {
|
||||
this.loading = false;
|
||||
});
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
// @flow
|
||||
import format from 'date-fns/format';
|
||||
|
||||
/**
|
||||
|
@ -7,13 +8,13 @@ import format from 'date-fns/format';
|
|||
* @param {Number} bytes
|
||||
* @return {String}
|
||||
*/
|
||||
export function readableSize (bytes) {
|
||||
export function readableSize (bytes: number): string {
|
||||
if (Math.abs(bytes) < 1024) {
|
||||
return `${bytes} Bytes`;
|
||||
}
|
||||
|
||||
let u = -1;
|
||||
const units = ['KiB', 'MiB', 'GiB', 'TiB'];
|
||||
let u: number = -1;
|
||||
const units: Array<string> = ['KiB', 'MiB', 'GiB', 'TiB'];
|
||||
|
||||
do {
|
||||
bytes /= 1024;
|
||||
|
@ -29,6 +30,6 @@ export function readableSize (bytes) {
|
|||
* @param {String} date
|
||||
* @return {String}
|
||||
*/
|
||||
export function formatDate (date) {
|
||||
export function formatDate (date: string): string {
|
||||
return format(date, 'MMM D, YYYY [at] HH:MM');
|
||||
}
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -15,7 +15,7 @@
|
|||
}
|
||||
|
||||
&.clickable {
|
||||
@apply .cursor-pointer .no-underline;
|
||||
@apply .no-underline;
|
||||
}
|
||||
|
||||
&.active-selection, &.clickable:hover {
|
||||
|
|
Loading…
Reference in a new issue