Support saving existing files
This commit is contained in:
parent
a8f523e2aa
commit
bfdc1f766b
6 changed files with 95 additions and 2 deletions
|
@ -18,6 +18,7 @@ use Pterodactyl\Http\Requests\Api\Client\Servers\Files\RenameFileRequest;
|
||||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest;
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\CreateFolderRequest;
|
||||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest;
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\DownloadFileRequest;
|
||||||
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest;
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\GetFileContentsRequest;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest;
|
||||||
|
|
||||||
class FileController extends ClientApiController
|
class FileController extends ClientApiController
|
||||||
{
|
{
|
||||||
|
@ -83,6 +84,22 @@ class FileController extends ClientApiController
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes the contents of the specified file to the server.
|
||||||
|
*
|
||||||
|
* @param \Pterodactyl\Http\Requests\Api\Client\Servers\Files\WriteFileContentRequest $request
|
||||||
|
* @return \Illuminate\Http\Response
|
||||||
|
*/
|
||||||
|
public function writeFileContents(WriteFileContentRequest $request): Response
|
||||||
|
{
|
||||||
|
$this->fileRepository->setServer($request->getModel(Server::class))->putContent(
|
||||||
|
$request->get('file'),
|
||||||
|
$request->getContent()
|
||||||
|
);
|
||||||
|
|
||||||
|
return Response::create('', Response::HTTP_NO_CONTENT);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new folder on the server.
|
* Creates a new folder on the server.
|
||||||
*
|
*
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Pterodactyl\Http\Requests\Api\Client\Servers\Files;
|
||||||
|
|
||||||
|
use Pterodactyl\Contracts\Http\ClientPermissionsRequest;
|
||||||
|
use Pterodactyl\Http\Requests\Api\Client\ClientApiRequest;
|
||||||
|
|
||||||
|
class WriteFileContentRequest extends ClientApiRequest implements ClientPermissionsRequest
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Returns the permissions string indicating which permission should be used to
|
||||||
|
* validate that the authenticated user has permission to perform this action aganist
|
||||||
|
* the given resource (server).
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function permission(): string
|
||||||
|
{
|
||||||
|
return 'save-files';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is no rule here for the file contents since we just use the body content
|
||||||
|
* on the request to set the file contents. If nothing is passed that is fine since
|
||||||
|
* it just means we want to set the file to be empty.
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function rules(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'file' => 'required|string',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
import axios, {AxiosInstance} from 'axios';
|
import axios, {AxiosError, AxiosInstance} from 'axios';
|
||||||
import {ServerApplicationCredentials} from "@/store/types";
|
import {ServerApplicationCredentials} from "@/store/types";
|
||||||
|
|
||||||
// This token is set in the bootstrap.js file at the beginning of the request
|
// This token is set in the bootstrap.js file at the beginning of the request
|
||||||
|
@ -38,3 +38,18 @@ export function withCredentials(server: string, credentials: ServerApplicationCr
|
||||||
|
|
||||||
return http;
|
return http;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts an error into a human readable response. Mostly just a generic helper to
|
||||||
|
* make sure we display the message from the server back to the user if we can.
|
||||||
|
*/
|
||||||
|
export function httpErrorToHuman(error: any): string {
|
||||||
|
if (error.response && error.response.data) {
|
||||||
|
const { data } = error.response;
|
||||||
|
if (data.errors && data.errors[0] && data.errors[0].detail) {
|
||||||
|
return data.errors[0].detail;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error.message;
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,14 @@
|
||||||
|
import http from "@/api/http";
|
||||||
|
|
||||||
|
export default (server: string, file: string, content: string): Promise<void> => {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
http.post(`/api/client/servers/${server}/files/write`, content, {
|
||||||
|
params: { file },
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain; charset=utf-8',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.then(() => resolve())
|
||||||
|
.catch(reject);
|
||||||
|
});
|
||||||
|
}
|
|
@ -19,7 +19,7 @@
|
||||||
<button class="btn btn-secondary btn-sm" v-on:click="closeModal">
|
<button class="btn btn-secondary btn-sm" v-on:click="closeModal">
|
||||||
Cancel
|
Cancel
|
||||||
</button>
|
</button>
|
||||||
<button class="ml-2 btn btn-primary btn-sm">
|
<button class="ml-2 btn btn-primary btn-sm" v-on:click="submit">
|
||||||
Save
|
Save
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -39,6 +39,8 @@
|
||||||
import {DirectoryContentObject} from "@/api/server/types";
|
import {DirectoryContentObject} from "@/api/server/types";
|
||||||
import getFileContents from '@/api/server/files/getFileContents';
|
import getFileContents from '@/api/server/files/getFileContents';
|
||||||
import SpinnerModal from "@/components/core/SpinnerModal.vue";
|
import SpinnerModal from "@/components/core/SpinnerModal.vue";
|
||||||
|
import writeFileContents from '@/api/server/files/writeFileContents';
|
||||||
|
import {httpErrorToHuman} from '@/api/http';
|
||||||
|
|
||||||
interface Data {
|
interface Data {
|
||||||
file?: DirectoryContentObject,
|
file?: DirectoryContentObject,
|
||||||
|
@ -120,7 +122,16 @@
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
submit: function () {
|
submit: function () {
|
||||||
|
this.isLoading = true;
|
||||||
|
const content = this.editor!.getValue();
|
||||||
|
|
||||||
|
writeFileContents(this.serverUuid!, join(this.fm!.currentDirectory, this.file!.name), content)
|
||||||
|
.then(() => this.error = null)
|
||||||
|
.catch(error => {
|
||||||
|
console.log(error);
|
||||||
|
this.error = httpErrorToHuman(error);
|
||||||
|
})
|
||||||
|
.then(() => this.isLoading = false);
|
||||||
},
|
},
|
||||||
|
|
||||||
loadFileContent: function (): Promise<void> {
|
loadFileContent: function (): Promise<void> {
|
||||||
|
|
|
@ -46,6 +46,7 @@ Route::group(['prefix' => '/servers/{server}', 'middleware' => [AuthenticateServ
|
||||||
Route::get('/contents', 'Servers\FileController@getFileContents')->name('api.client.servers.files.contents');
|
Route::get('/contents', 'Servers\FileController@getFileContents')->name('api.client.servers.files.contents');
|
||||||
Route::put('/rename', 'Servers\FileController@renameFile')->name('api.client.servers.files.rename');
|
Route::put('/rename', 'Servers\FileController@renameFile')->name('api.client.servers.files.rename');
|
||||||
Route::post('/copy', 'Servers\FileController@copyFile')->name('api.client.servers.files.copy');
|
Route::post('/copy', 'Servers\FileController@copyFile')->name('api.client.servers.files.copy');
|
||||||
|
Route::post('/write', 'Servers\FileController@writeFileContents')->name('api.client.servers.files.write');
|
||||||
Route::post('/delete', 'Servers\FileController@delete')->name('api.client.servers.files.delete');
|
Route::post('/delete', 'Servers\FileController@delete')->name('api.client.servers.files.delete');
|
||||||
Route::post('/create-folder', 'Servers\FileController@createFolder')->name('api.client.servers.files.create-folder');
|
Route::post('/create-folder', 'Servers\FileController@createFolder')->name('api.client.servers.files.create-folder');
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue