Pass one at unfucking the stupid file encoding issues
This commit is contained in:
parent
24417ac516
commit
3e65a2d055
10 changed files with 33 additions and 19 deletions
|
@ -72,7 +72,7 @@ class FileController extends ClientApiController
|
||||||
{
|
{
|
||||||
$contents = $this->fileRepository
|
$contents = $this->fileRepository
|
||||||
->setServer($server)
|
->setServer($server)
|
||||||
->getDirectory($this->encode($request->get('directory') ?? '/'));
|
->getDirectory(rawurldecode($request->get('directory') ?? '/'));
|
||||||
|
|
||||||
return $this->fractal->collection($contents)
|
return $this->fractal->collection($contents)
|
||||||
->transformWith($this->getTransformer(FileObjectTransformer::class))
|
->transformWith($this->getTransformer(FileObjectTransformer::class))
|
||||||
|
@ -93,7 +93,7 @@ class FileController extends ClientApiController
|
||||||
{
|
{
|
||||||
return new Response(
|
return new Response(
|
||||||
$this->fileRepository->setServer($server)->getContent(
|
$this->fileRepository->setServer($server)->getContent(
|
||||||
$this->encode($request->get('file')), config('pterodactyl.files.max_edit_size')
|
rawurldecode($request->get('file')), config('pterodactyl.files.max_edit_size')
|
||||||
),
|
),
|
||||||
Response::HTTP_OK,
|
Response::HTTP_OK,
|
||||||
['Content-Type' => 'text/plain']
|
['Content-Type' => 'text/plain']
|
||||||
|
|
|
@ -23,7 +23,7 @@ class FileObjectTransformer extends BaseDaemonTransformer
|
||||||
public function transform(array $item)
|
public function transform(array $item)
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => Arr::get($item, 'name'),
|
'name' => rawurlencode(Arr::get($item, 'name')),
|
||||||
'mode' => Arr::get($item, 'mode'),
|
'mode' => Arr::get($item, 'mode'),
|
||||||
'mode_bits' => Arr::get($item, 'mode_bits'),
|
'mode_bits' => Arr::get($item, 'mode_bits'),
|
||||||
'size' => Arr::get($item, 'size'),
|
'size' => Arr::get($item, 'size'),
|
||||||
|
|
|
@ -3,7 +3,7 @@ import http from '@/api/http';
|
||||||
export default (server: string, file: string): Promise<string> => {
|
export default (server: string, file: string): Promise<string> => {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
http.get(`/api/client/servers/${server}/files/contents`, {
|
http.get(`/api/client/servers/${server}/files/contents`, {
|
||||||
params: { file: encodeURI(decodeURI(file)) },
|
params: { file },
|
||||||
transformResponse: res => res,
|
transformResponse: res => res,
|
||||||
responseType: 'text',
|
responseType: 'text',
|
||||||
})
|
})
|
||||||
|
|
|
@ -18,7 +18,9 @@ export interface FileObject {
|
||||||
|
|
||||||
export default async (uuid: string, directory?: string): Promise<FileObject[]> => {
|
export default async (uuid: string, directory?: string): Promise<FileObject[]> => {
|
||||||
const { data } = await http.get(`/api/client/servers/${uuid}/files/list`, {
|
const { data } = await http.get(`/api/client/servers/${uuid}/files/list`, {
|
||||||
params: { directory: encodeURI(directory ?? '/') },
|
// At this point the directory is still encoded so we need to decode it since axios
|
||||||
|
// will automatically re-encode this value before sending it along in the request.
|
||||||
|
params: { directory: decodeURI(directory ?? '/') },
|
||||||
});
|
});
|
||||||
|
|
||||||
return (data.data || []).map(rawDataToFileObject);
|
return (data.data || []).map(rawDataToFileObject);
|
||||||
|
|
|
@ -2,7 +2,7 @@ import http from '@/api/http';
|
||||||
|
|
||||||
export default async (uuid: string, file: string, content: string): Promise<void> => {
|
export default async (uuid: string, file: string, content: string): Promise<void> => {
|
||||||
await http.post(`/api/client/servers/${uuid}/files/write`, content, {
|
await http.post(`/api/client/servers/${uuid}/files/write`, content, {
|
||||||
params: { file: encodeURI(decodeURI(file)) },
|
params: { file },
|
||||||
headers: {
|
headers: {
|
||||||
'Content-Type': 'text/plain',
|
'Content-Type': 'text/plain',
|
||||||
},
|
},
|
||||||
|
|
|
@ -61,7 +61,7 @@ export default () => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
clearFlashes('files:view');
|
clearFlashes('files:view');
|
||||||
fetchFileContent()
|
fetchFileContent()
|
||||||
.then(content => saveFileContents(uuid, name || hash.replace(/^#/, ''), content))
|
.then(content => saveFileContents(uuid, name || decodeURI(hash.replace(/^#/, '')), content))
|
||||||
.then(() => {
|
.then(() => {
|
||||||
if (name) {
|
if (name) {
|
||||||
history.push(`/server/${id}/files/edit#/${name}`);
|
history.push(`/server/${id}/files/edit#/${name}`);
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
import React, { useEffect, useState } from 'react';
|
import React, { useEffect, useState } from 'react';
|
||||||
import { ServerContext } from '@/state/server';
|
import { ServerContext } from '@/state/server';
|
||||||
import { NavLink } from 'react-router-dom';
|
import { NavLink, useLocation } from 'react-router-dom';
|
||||||
import { cleanDirectoryPath } from '@/helpers';
|
import { cleanDirectoryPath } from '@/helpers';
|
||||||
import tw from 'twin.macro';
|
import tw from 'twin.macro';
|
||||||
|
|
||||||
|
@ -14,14 +14,28 @@ export default ({ renderLeft, withinFileEditor, isNewFile }: Props) => {
|
||||||
const [ file, setFile ] = useState<string | null>(null);
|
const [ file, setFile ] = useState<string | null>(null);
|
||||||
const id = ServerContext.useStoreState(state => state.server.data!.id);
|
const id = ServerContext.useStoreState(state => state.server.data!.id);
|
||||||
const directory = ServerContext.useStoreState(state => state.files.directory);
|
const directory = ServerContext.useStoreState(state => state.files.directory);
|
||||||
|
const { hash } = useLocation();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const parts = cleanDirectoryPath(window.location.hash).split('/');
|
let pathHash = cleanDirectoryPath(hash);
|
||||||
|
try {
|
||||||
|
pathHash = decodeURI(pathHash);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error decoding URL parts in hash:', e);
|
||||||
|
}
|
||||||
|
|
||||||
if (withinFileEditor && !isNewFile) {
|
if (withinFileEditor && !isNewFile) {
|
||||||
setFile(parts.pop() || null);
|
let name = pathHash.split('/').pop() || null;
|
||||||
|
if (name) {
|
||||||
|
try {
|
||||||
|
name = decodeURIComponent(name);
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Error decoding filename:', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setFile(name);
|
||||||
}
|
}
|
||||||
}, [ withinFileEditor, isNewFile ]);
|
}, [ withinFileEditor, isNewFile, hash ]);
|
||||||
|
|
||||||
const breadcrumbs = (): { name: string; path?: string }[] => directory.split('/')
|
const breadcrumbs = (): { name: string; path?: string }[] => directory.split('/')
|
||||||
.filter(directory => !!directory)
|
.filter(directory => !!directory)
|
||||||
|
@ -51,16 +65,16 @@ export default ({ renderLeft, withinFileEditor, isNewFile }: Props) => {
|
||||||
to={`/server/${id}/files#${crumb.path}`}
|
to={`/server/${id}/files#${crumb.path}`}
|
||||||
css={tw`px-1 text-neutral-200 no-underline hover:text-neutral-100`}
|
css={tw`px-1 text-neutral-200 no-underline hover:text-neutral-100`}
|
||||||
>
|
>
|
||||||
{crumb.name}
|
{decodeURIComponent(crumb.name)}
|
||||||
</NavLink>/
|
</NavLink>/
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
:
|
:
|
||||||
<span key={index} css={tw`px-1 text-neutral-300`}>{crumb.name}</span>
|
<span key={index} css={tw`px-1 text-neutral-300`}>{decodeURIComponent(crumb.name)}</span>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
{file &&
|
{file &&
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<span css={tw`px-1 text-neutral-300`}>{decodeURI(file)}</span>
|
<span css={tw`px-1 text-neutral-300`}>{file}</span>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -39,7 +39,7 @@ export default () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
clearFlashes('files');
|
clearFlashes('files');
|
||||||
setSelectedFiles([]);
|
setSelectedFiles([]);
|
||||||
setDirectory(hash.length > 0 ? decodeURI(hash) : '/');
|
setDirectory(hash.length > 0 ? hash : '/');
|
||||||
}, [ hash ]);
|
}, [ hash ]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
|
@ -24,7 +24,7 @@ const Clickable: React.FC<{ file: FileObject }> = memo(({ file, children }) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const match = useRouteMatch();
|
const match = useRouteMatch();
|
||||||
|
|
||||||
const destination = cleanDirectoryPath(`${directory}/${file.name}`).split('/').map(v => encodeURI(v)).join('/');
|
const destination = cleanDirectoryPath(`${directory}/${file.name}`).split('/').join('/');
|
||||||
|
|
||||||
const onRowClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
const onRowClick = (e: React.MouseEvent<HTMLAnchorElement, MouseEvent>) => {
|
||||||
// Don't rely on the onClick to work with the generated URL. Because of the way this
|
// Don't rely on the onClick to work with the generated URL. Because of the way this
|
||||||
|
@ -72,7 +72,7 @@ const FileObjectRow = ({ file }: { file: FileObject }) => (
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div css={tw`flex-1 truncate`}>
|
<div css={tw`flex-1 truncate`}>
|
||||||
{file.name}
|
{decodeURIComponent(file.name)}
|
||||||
</div>
|
</div>
|
||||||
{file.isFile &&
|
{file.isFile &&
|
||||||
<div css={tw`w-1/6 text-right mr-4 hidden sm:block`}>
|
<div css={tw`w-1/6 text-right mr-4 hidden sm:block`}>
|
||||||
|
|
|
@ -7,8 +7,6 @@ export default () => {
|
||||||
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
|
||||||
const directory = ServerContext.useStoreState(state => state.files.directory);
|
const directory = ServerContext.useStoreState(state => state.files.directory);
|
||||||
|
|
||||||
console.log('firing');
|
|
||||||
|
|
||||||
return useSWR<FileObject[]>(
|
return useSWR<FileObject[]>(
|
||||||
`${uuid}:files:${directory}`,
|
`${uuid}:files:${directory}`,
|
||||||
() => loadDirectory(uuid, cleanDirectoryPath(directory)),
|
() => loadDirectory(uuid, cleanDirectoryPath(directory)),
|
||||||
|
|
Loading…
Reference in a new issue