Skip to content

Commit a8d9ecc

Browse files
committed
Support pagination of server backups, closes pterodactyl#2787
1 parent 8a97b73 commit a8d9ecc

File tree

6 files changed

+53
-22
lines changed

6 files changed

+53
-22
lines changed

app/Http/Controllers/Api/Client/Servers/BackupController.php

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,8 @@ public function __construct(
6060
*/
6161
public function index(GetBackupsRequest $request, Server $server)
6262
{
63-
return $this->fractal->collection($server->backups()->paginate(20))
63+
$limit = min($request->query('per_page') ?? 20, 50);
64+
return $this->fractal->collection($server->backups()->paginate($limit))
6465
->transformWith($this->getTransformer(BackupTransformer::class))
6566
->toArray();
6667
}

resources/scripts/api/swr/getServerBackups.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,17 @@ import http, { getPaginationSet, PaginatedResult } from '@/api/http';
33
import { ServerBackup } from '@/api/server/types';
44
import { rawDataToServerBackup } from '@/api/transformers';
55
import { ServerContext } from '@/state/server';
6+
import { createContext, useContext } from 'react';
67

7-
export default (page?: number | string) => {
8+
interface ctx {
9+
page: number;
10+
setPage: (value: number | ((s: number) => number)) => void;
11+
}
12+
13+
export const Context = createContext<ctx>({ page: 1, setPage: () => 1 });
14+
15+
export default () => {
16+
const { page } = useContext(Context);
817
const uuid = ServerContext.useStoreState(state => state.server.data!.uuid);
918

1019
return useSWR<PaginatedResult<ServerBackup>>([ 'server:backups', uuid, page ], async () => {

resources/scripts/components/dashboard/ApiKeyModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ const ApiKeyModal = ({ apiKey }: Props) => {
1313

1414
return (
1515
<>
16-
<h3 css={tw`mb-6`}>Your API Key</h3>
16+
<h3 css={tw`mb-6 text-2xl`}>Your API Key</h3>
1717
<p css={tw`text-sm mb-6`}>
1818
The API key you have requested is shown below. Please store this in a safe location, it will not be
1919
shown again.

resources/scripts/components/server/backups/BackupContainer.tsx

Lines changed: 38 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,18 @@
1-
import React, { useEffect } from 'react';
1+
import React, { useContext, useEffect, useState } from 'react';
22
import Spinner from '@/components/elements/Spinner';
33
import useFlash from '@/plugins/useFlash';
44
import Can from '@/components/elements/Can';
55
import CreateBackupButton from '@/components/server/backups/CreateBackupButton';
66
import FlashMessageRender from '@/components/FlashMessageRender';
77
import BackupRow from '@/components/server/backups/BackupRow';
88
import tw from 'twin.macro';
9-
import getServerBackups from '@/api/swr/getServerBackups';
9+
import getServerBackups, { Context as ServerBackupContext } from '@/api/swr/getServerBackups';
1010
import { ServerContext } from '@/state/server';
1111
import ServerContentBlock from '@/components/elements/ServerContentBlock';
12+
import Pagination from '@/components/elements/Pagination';
1213

13-
export default () => {
14+
const BackupContainer = () => {
15+
const { page, setPage } = useContext(ServerBackupContext);
1416
const { clearFlashes, clearAndAddHttpError } = useFlash();
1517
const { data: backups, error, isValidating } = getServerBackups();
1618

@@ -33,19 +35,29 @@ export default () => {
3335
return (
3436
<ServerContentBlock title={'Backups'}>
3537
<FlashMessageRender byKey={'backups'} css={tw`mb-4`}/>
36-
{!backups.items.length ?
37-
<p css={tw`text-center text-sm text-neutral-300`}>
38-
There are no backups stored for this server.
39-
</p>
40-
:
41-
<div>
42-
{backups.items.map((backup, index) => <BackupRow
43-
key={backup.uuid}
44-
backup={backup}
45-
css={index > 0 ? tw`mt-2` : undefined}
46-
/>)}
47-
</div>
48-
}
38+
<Pagination data={backups} onPageSelect={setPage}>
39+
{({ items }) => (
40+
!items.length ?
41+
// Don't show any error messages if the server has no backups and the user cannot
42+
// create additional ones for the server.
43+
!backupLimit ?
44+
null
45+
:
46+
<p css={tw`text-center text-sm text-neutral-300`}>
47+
{page > 1 ?
48+
'Looks like we\'ve run out of backups to show you, try going back a page.'
49+
:
50+
'It looks like there are no backups currently stored for this server.'
51+
}
52+
</p>
53+
:
54+
items.map((backup, index) => <BackupRow
55+
key={backup.uuid}
56+
backup={backup}
57+
css={index > 0 ? tw`mt-2` : undefined}
58+
/>)
59+
)}
60+
</Pagination>
4961
{backupLimit === 0 &&
5062
<p css={tw`text-center text-sm text-neutral-300`}>
5163
Backups cannot be created for this server.
@@ -59,10 +71,19 @@ export default () => {
5971
</p>
6072
}
6173
{backupLimit > 0 && backupLimit !== backups.items.length &&
62-
<CreateBackupButton css={tw`w-full sm:w-auto`}/>
74+
<CreateBackupButton css={tw`w-full sm:w-auto`}/>
6375
}
6476
</div>
6577
</Can>
6678
</ServerContentBlock>
6779
);
6880
};
81+
82+
export default () => {
83+
const [ page, setPage ] = useState<number>(1);
84+
return (
85+
<ServerBackupContext.Provider value={{ page, setPage }}>
86+
<BackupContainer/>
87+
</ServerBackupContext.Provider>
88+
);
89+
};

resources/scripts/components/server/backups/ChecksumModal.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import tw from 'twin.macro';
44

55
const ChecksumModal = ({ checksum, ...props }: RequiredModalProps & { checksum: string }) => (
66
<Modal {...props}>
7-
<h3 css={tw`mb-6`}>Verify file checksum</h3>
7+
<h3 css={tw`mb-6 text-2xl`}>Verify file checksum</h3>
88
<p css={tw`text-sm`}>
99
The checksum of this file is:
1010
</p>

resources/scripts/components/server/databases/DatabaseRow.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ export default ({ database, className }: Props) => {
111111
</Formik>
112112
<Modal visible={connectionVisible} onDismissed={() => setConnectionVisible(false)}>
113113
<FlashMessageRender byKey={'database-connection-modal'} css={tw`mb-6`}/>
114-
<h3 css={tw`mb-6`}>Database connection details</h3>
114+
<h3 css={tw`mb-6 text-2xl`}>Database connection details</h3>
115115
<div>
116116
<Label>Endpoint</Label>
117117
<CopyOnClick text={database.connectionString}><Input type={'text'} readOnly value={database.connectionString} /></CopyOnClick>

0 commit comments

Comments
 (0)