Skip to content

Commit aee42df

Browse files
committed
Implement some flow and cleanup API call for file manager
1 parent c3ef290 commit aee42df

File tree

9 files changed

+190
-122
lines changed

9 files changed

+190
-122
lines changed

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
"flowtype-errors/show-errors": 2,
77
"semi": "off",
88
"indent": ["error", 4],
9-
"comma-dangle": ["error", "always-multiline"]
9+
"comma-dangle": ["error", "always-multiline"],
10+
"no-unused-vars": "warn"
1011
}
1112
}

.flowconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[ignore]
2-
<PROJECT_ROOT>/vendor/.*
2+
<PROJECT_ROOT>/vendor/symfony/.*
33
<PROJECT_ROOT>/tests/.*
44
<PROJECT_ROOT>/storage/.*
55
<PROJECT_ROOT>/app/.*
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// @flow
2+
import axios from 'axios';
3+
import type { AxiosInstance } from 'axios';
4+
5+
// This token is set in the bootstrap.js file at the beginning of the request
6+
// and is carried through from there.
7+
// const token: string = '';
8+
9+
const http: AxiosInstance = axios.create({
10+
'X-Requested-With': 'XMLHttpRequest',
11+
'Accept': 'application/json',
12+
'Content-Type': 'application/json',
13+
});
14+
15+
// If we have a phpdebugbar instance registered at this point in time go
16+
// ahead and route the response data through to it so things show up.
17+
if (typeof window.phpdebugbar !== 'undefined') {
18+
http.interceptors.response.use(response => {
19+
window.phpdebugbar.ajaxHandler.handle(response.request);
20+
21+
return response;
22+
});
23+
}
24+
25+
export default http;
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// @flow
2+
import http from './../http';
3+
import filter from 'lodash/filter';
4+
import isObject from 'lodash/isObject';
5+
import route from '../../../../../vendor/tightenco/ziggy/src/js/route';
6+
7+
export interface DirectoryContentsResponse {
8+
files: Object,
9+
directories: Object,
10+
editable: Array<string>,
11+
}
12+
13+
/**
14+
* Get the contents of a specific directory for a given server.
15+
*
16+
* @param {String} server
17+
* @param {String} directory
18+
* @return {Promise<DirectoryContentsResponse>}
19+
*/
20+
export function getDirectoryContents(server: string, directory: string): Promise<DirectoryContentsResponse> {
21+
return new Promise((resolve, reject) => {
22+
http.get(route('server.files', { server, directory }))
23+
.then((response) => {
24+
return resolve({
25+
files: filter(response.data.contents, function (o) {
26+
return o.file;
27+
}),
28+
directories: filter(response.data.contents, function (o) {
29+
return o.directory;
30+
}),
31+
editable: response.data.editable,
32+
});
33+
})
34+
.catch(err => {
35+
if (err.response && err.response.status === 404) {
36+
return reject('The directory you requested could not be located on the server');
37+
}
38+
39+
if (err.response.data && isObject(err.response.data.errors)) {
40+
err.response.data.errors.forEach(error => {
41+
return reject(error.detail);
42+
});
43+
}
44+
45+
return reject(err);
46+
});
47+
});
48+
}
49+
50+
export default getDirectoryContents;

resources/assets/scripts/app.js

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @flow
12
import Vue from 'vue';
23
import Vuex from 'vuex';
34
import vuexI18n from 'vuex-i18n';
@@ -14,24 +15,26 @@ import { flash } from './mixins/flash';
1415
import store from './store/index.js';
1516
import router from './router';
1617

17-
window.events = new Vue;
18+
window.events = new Vue();
1819
window.Ziggy = Ziggy;
1920

2021
Vue.use(Vuex);
2122
Vue.use(VueRouter);
2223
Vue.use(vuexI18n.plugin, store);
2324
Vue.use(VeeValidate);
2425

26+
// $FlowFixMe: this is always going to be unhappy because we ignore the vendor dir.
2527
const route = require('./../../../vendor/tightenco/ziggy/src/js/route').default;
2628

27-
Vue.mixin({ methods: { route } });
29+
Vue.mixin({ methods: { route }});
2830
Vue.mixin(flash);
2931

3032
Vue.i18n.add('en', Locales.en);
3133
Vue.i18n.set('en');
3234

35+
// $FlowFixMe
3336
if (module.hot) {
3437
module.hot.accept();
3538
}
3639

37-
const app = new Vue({ store, router }).$mount('#pterodactyl');
40+
new Vue({ store, router }).$mount('#pterodactyl');

resources/assets/scripts/components/server/subpages/FileManager.vue

Lines changed: 99 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -42,126 +42,114 @@
4242
</template>
4343

4444
<script>
45-
import map from 'lodash/map';
46-
import filter from 'lodash/filter';
47-
import isObject from 'lodash/isObject';
48-
import { mapState } from 'vuex';
49-
import FileManagerFileRow from '../components/filemanager/FileManagerFileRow';
50-
import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow';
51-
52-
export default {
53-
name: 'file-manager-page',
54-
components: {FileManagerFolderRow, FileManagerFileRow},
55-
56-
computed: {
57-
...mapState('server', ['server', 'credentials']),
58-
...mapState('socket', ['connected']),
45+
// @flow
46+
import map from 'lodash/map';
47+
import { mapState } from 'vuex';
48+
import type { Route } from 'vue-router';
49+
import FileManagerFileRow from '../components/filemanager/FileManagerFileRow';
50+
import FileManagerFolderRow from '../components/filemanager/FileManagerFolderRow';
51+
import { getDirectoryContents, DirectoryContentsResponse } from '../../../api/server/getDirectoryContents';
52+
53+
export default {
54+
name: 'file-manager-page',
55+
components: { FileManagerFolderRow, FileManagerFileRow },
56+
57+
computed: {
58+
...mapState('server', ['server', 'credentials']),
59+
...mapState('socket', ['connected']),
60+
61+
/**
62+
* Configure the breadcrumbs that display on the filemanager based on the directory that the
63+
* user is currently in.
64+
*/
65+
breadcrumbs: function (): Array<Object> {
66+
const directories: Array<string> = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
67+
if (directories.length < 1 || !directories[0]) {
68+
return [];
69+
}
5970
60-
/**
61-
* Configure the breadcrumbs that display on the filemanager based on the directory that the
62-
* user is currently in.
63-
*/
64-
breadcrumbs: function () {
65-
const directories = this.currentDirectory.replace(/^\/|\/$/, '').split('/');
66-
if (directories.length < 1 || !directories[0]) {
67-
return [];
71+
return map(directories, function (value: string, key: number) {
72+
if (key === directories.length - 1) {
73+
return { directoryName: value };
6874
}
6975
70-
return map(directories, function (value, key) {
71-
if (key === directories.length - 1) {
72-
return {directoryName: value};
73-
}
74-
75-
return {
76-
directoryName: value,
77-
path: directories.slice(0, key + 1).join('/'),
78-
};
79-
});
80-
}
76+
return {
77+
directoryName: value,
78+
path: directories.slice(0, key + 1).join('/'),
79+
};
80+
});
81+
},
82+
},
83+
84+
watch: {
85+
/**
86+
* When the route changes reload the directory.
87+
*/
88+
'$route': function (to: Route) {
89+
this.currentDirectory = to.params.path || '/';
8190
},
8291
83-
watch: {
84-
/**
85-
* When the route changes reload the directory.
86-
*/
87-
'$route': function (to) {
88-
this.currentDirectory = to.params.path || '/';
89-
},
92+
/**
93+
* Watch the current directory setting and when it changes update the file listing.
94+
*/
95+
currentDirectory: function (): void {
96+
this.listDirectory();
97+
},
9098
91-
/**
92-
* Watch the current directory setting and when it changes update the file listing.
93-
*/
94-
currentDirectory: function () {
99+
/**
100+
* When we reconnect to the Daemon make sure we grab a listing of all of the files
101+
* so that the error message disappears and we then load in a fresh listing.
102+
*/
103+
connected: function (): void {
104+
if (this.connected) {
95105
this.listDirectory();
96-
},
97-
98-
/**
99-
* When we reconnect to the Daemon make sure we grab a listing of all of the files
100-
* so that the error message disappears and we then load in a fresh listing.
101-
*/
102-
connected: function () {
103-
if (this.connected) {
104-
this.listDirectory();
105-
}
106106
}
107107
},
108+
},
109+
110+
data: function () {
111+
return {
112+
currentDirectory: this.$route.params.path || '/',
113+
loading: true,
114+
errorMessage: null,
115+
116+
directories: [],
117+
editableFiles: [],
118+
files: [],
119+
};
120+
},
121+
122+
mounted: function () {
123+
this.listDirectory();
124+
},
125+
126+
methods: {
127+
/**
128+
* List the contents of a directory.
129+
*/
130+
listDirectory: function (): void {
131+
this.loading = true;
132+
133+
const directory: string = encodeURI(this.currentDirectory.replace(/^\/|\/$/, ''));
134+
getDirectoryContents(this.$route.params.id, directory)
135+
.then((response: DirectoryContentsResponse) => {
136+
this.files = response.files;
137+
this.directories = response.directories;
138+
this.editableFiles = response.editable;
139+
this.errorMessage = null;
140+
})
141+
.catch((err: string|Object) => {
142+
if (err instanceof String) {
143+
this.errorMessage = err;
144+
return;
145+
}
108146
109-
data: function () {
110-
return {
111-
currentDirectory: this.$route.params.path || '/',
112-
loading: true,
113-
errorMessage: null,
114-
115-
directories: [],
116-
editableFiles: [],
117-
files: [],
118-
};
119-
},
120-
121-
mounted: function () {
122-
this.listDirectory();
147+
console.error('An error was encountered while processing this request.', { err });
148+
})
149+
.then(() => {
150+
this.loading = false;
151+
});
123152
},
124-
125-
methods: {
126-
/**
127-
* List the contents of a directory.
128-
*/
129-
listDirectory: function () {
130-
this.loading = true;
131-
132-
window.axios.get(this.route('server.files', {
133-
server: this.$route.params.id,
134-
directory: encodeURI(this.currentDirectory.replace(/^\/|\/$/, '')),
135-
}))
136-
.then((response) => {
137-
this.files = filter(response.data.contents, function (o) {
138-
return o.file;
139-
});
140-
141-
this.directories = filter(response.data.contents, function (o) {
142-
return o.directory;
143-
});
144-
145-
this.editableFiles = response.data.editable;
146-
this.errorMessage = null;
147-
})
148-
.catch(err => {
149-
console.error({err});
150-
if (err.response.status === 404) {
151-
this.errorMessage = 'The directory you requested could not be located on the server.';
152-
return;
153-
}
154-
155-
if (err.response.data && isObject(err.response.data.errors)) {
156-
err.response.data.errors.forEach(error => {
157-
this.errorMessage = error.detail;
158-
});
159-
}
160-
})
161-
.finally(() => {
162-
this.loading = false;
163-
});
164-
},
165-
}
166-
};
153+
},
154+
};
167155
</script>

resources/assets/scripts/helpers/index.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
// @flow
12
import format from 'date-fns/format';
23

34
/**
@@ -7,13 +8,13 @@ import format from 'date-fns/format';
78
* @param {Number} bytes
89
* @return {String}
910
*/
10-
export function readableSize (bytes) {
11+
export function readableSize (bytes: number): string {
1112
if (Math.abs(bytes) < 1024) {
1213
return `${bytes} Bytes`;
1314
}
1415

15-
let u = -1;
16-
const units = ['KiB', 'MiB', 'GiB', 'TiB'];
16+
let u: number = -1;
17+
const units: Array<string> = ['KiB', 'MiB', 'GiB', 'TiB'];
1718

1819
do {
1920
bytes /= 1024;
@@ -29,6 +30,6 @@ export function readableSize (bytes) {
2930
* @param {String} date
3031
* @return {String}
3132
*/
32-
export function formatDate (date) {
33+
export function formatDate (date: string): string {
3334
return format(date, 'MMM D, YYYY [at] HH:MM');
3435
}

0 commit comments

Comments
 (0)