Skip to content

Commit 32e9fb0

Browse files
committed
Add basic listing of server schedules
1 parent f9ec96c commit 32e9fb0

File tree

21 files changed

+508
-79
lines changed

21 files changed

+508
-79
lines changed
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<?php
2+
3+
namespace Pterodactyl\Http\Controllers\Api\Client\Servers;
4+
5+
use Illuminate\Http\Request;
6+
use Pterodactyl\Models\Server;
7+
use Pterodactyl\Transformers\Api\Client\ScheduleTransformer;
8+
use Pterodactyl\Http\Controllers\Api\Client\ClientApiController;
9+
10+
class ScheduleController extends ClientApiController
11+
{
12+
/**
13+
* Returns all of the schedules belonging to a given server.
14+
*
15+
* @param \Illuminate\Http\Request $request
16+
* @param \Pterodactyl\Models\Server $server
17+
* @return array
18+
*/
19+
public function index(Request $request, Server $server)
20+
{
21+
$schedules = $server->schedule;
22+
$schedules->loadMissing('tasks');
23+
24+
return $this->fractal->collection($schedules)
25+
->transformWith($this->getTransformer(ScheduleTransformer::class))
26+
->toArray();
27+
}
28+
}

app/Models/Schedule.php

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,29 @@
22

33
namespace Pterodactyl\Models;
44

5+
use Illuminate\Container\Container;
6+
use Pterodactyl\Contracts\Extensions\HashidsInterface;
7+
8+
/**
9+
* @property int $id
10+
* @property int $server_id
11+
* @property string $name
12+
* @property string $cron_day_of_week
13+
* @property string $cron_day_of_month
14+
* @property string $cron_hour
15+
* @property string $cron_minute
16+
* @property bool $is_active
17+
* @property bool $is_processing
18+
* @property \Carbon\Carbon|null $last_run_at
19+
* @property \Carbon\Carbon|null $next_run_at
20+
* @property \Carbon\Carbon $created_at
21+
* @property \Carbon\Carbon $updated_at
22+
*
23+
* @property string $hashid
24+
*
25+
* @property \Pterodactyl\Models\Server $server
26+
* @property \Pterodactyl\Models\Task[]|\Illuminate\Support\Collection $tasks
27+
*/
528
class Schedule extends Validable
629
{
730
/**
@@ -51,8 +74,6 @@ class Schedule extends Validable
5174
* @var array
5275
*/
5376
protected $dates = [
54-
self::CREATED_AT,
55-
self::UPDATED_AT,
5677
'last_run_at',
5778
'next_run_at',
5879
];
@@ -93,7 +114,7 @@ class Schedule extends Validable
93114
*/
94115
public function getHashidAttribute()
95116
{
96-
return app()->make('hashids')->encode($this->id);
117+
return Container::getInstance()->make(HashidsInterface::class)->encode($this->id);
97118
}
98119

99120
/**

app/Models/Task.php

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,26 @@
22

33
namespace Pterodactyl\Models;
44

5+
use Illuminate\Container\Container;
56
use Znck\Eloquent\Traits\BelongsToThrough;
7+
use Pterodactyl\Contracts\Extensions\HashidsInterface;
68

9+
/**
10+
* @property int $id
11+
* @property int $schedule_id
12+
* @property int $sequence_id
13+
* @property string $action
14+
* @property string $payload
15+
* @property int $time_offset
16+
* @property bool $is_queued
17+
* @property \Carbon\Carbon $created_at
18+
* @property \Carbon\Carbon $updated_at
19+
*
20+
* @property string $hashid
21+
*
22+
* @property \Pterodactyl\Models\Schedule $schedule
23+
* @property \Pterodactyl\Models\Server $server
24+
*/
725
class Task extends Validable
826
{
927
use BelongsToThrough;
@@ -83,7 +101,7 @@ class Task extends Validable
83101
*/
84102
public function getHashidAttribute()
85103
{
86-
return app()->make('hashids')->encode($this->id);
104+
return Container::getInstance()->make(HashidsInterface::class)->encode($this->id);
87105
}
88106

89107
/**
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace Pterodactyl\Transformers\Api\Client;
4+
5+
use Pterodactyl\Models\Task;
6+
use Pterodactyl\Models\Schedule;
7+
use Illuminate\Database\Eloquent\Model;
8+
9+
class ScheduleTransformer extends BaseClientTransformer
10+
{
11+
/**
12+
* @var array
13+
*/
14+
protected $availableIncludes = ['tasks'];
15+
16+
/**
17+
* {@inheritdoc}
18+
*/
19+
public function getResourceName(): string
20+
{
21+
return Schedule::RESOURCE_NAME;
22+
}
23+
24+
/**
25+
* Returns a transformed schedule model such that a client can view the information.
26+
*
27+
* @param \Pterodactyl\Models\Schedule $model
28+
* @return array
29+
*/
30+
public function transform(Schedule $model)
31+
{
32+
return [
33+
'id' => $model->id,
34+
'name' => $model->name,
35+
'cron' => [
36+
'day_of_week' => $model->cron_day_of_week,
37+
'day_of_month' => $model->cron_day_of_month,
38+
'hour' => $model->cron_hour,
39+
'minute' => $model->cron_minute,
40+
],
41+
'is_active' => $model->is_active,
42+
'is_processing' => $model->is_processing,
43+
'last_run_at' => $model->last_run_at ? $model->last_run_at->toIso8601String() : null,
44+
'next_run_at' => $model->next_run_at ? $model->next_run_at->toIso8601String() : null,
45+
'created_at' => $model->created_at->toIso8601String(),
46+
'updated_at' => $model->updated_at->toIso8601String(),
47+
];
48+
}
49+
50+
/**
51+
* Allows attaching the tasks specific to the schedule in the response.
52+
*
53+
* @param \Pterodactyl\Models\Schedule $model
54+
* @return \League\Fractal\Resource\Collection
55+
*
56+
* @throws \Pterodactyl\Exceptions\Transformer\InvalidTransformerLevelException
57+
*/
58+
public function includeTasks(Schedule $model)
59+
{
60+
return $this->collection(
61+
$model->tasks, $this->makeTransformer(TaskTransformer::class), Task::RESOURCE_NAME
62+
);
63+
}
64+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<?php
2+
3+
namespace Pterodactyl\Transformers\Api\Client;
4+
5+
use Pterodactyl\Models\Task;
6+
7+
class TaskTransformer extends BaseClientTransformer
8+
{
9+
/**
10+
* {@inheritdoc}
11+
*/
12+
public function getResourceName(): string
13+
{
14+
return Task::RESOURCE_NAME;
15+
}
16+
17+
/**
18+
* Transforms a schedule's task into a client viewable format.
19+
*
20+
* @param \Pterodactyl\Models\Task $model
21+
* @return array
22+
*/
23+
public function transform(Task $model)
24+
{
25+
return [
26+
'id' => $model->id,
27+
'sequence_id' => $model->sequence_id,
28+
'action' => $model->action,
29+
'payload' => $model->payload,
30+
'time_offset' => $model->time_offset,
31+
'is_queued' => $model->is_queued,
32+
'created_at' => $model->created_at->toIso8601String(),
33+
'updated_at' => $model->updated_at->toIso8601String(),
34+
];
35+
}
36+
}

package.json

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
"@babel/core": "^7.7.5",
4545
"@babel/plugin-proposal-class-properties": "^7.7.4",
4646
"@babel/plugin-proposal-object-rest-spread": "^7.7.4",
47+
"@babel/plugin-proposal-optional-chaining": "^7.8.3",
4748
"@babel/plugin-syntax-dynamic-import": "^7.7.4",
4849
"@babel/plugin-transform-runtime": "^7.7.5",
4950
"@babel/preset-env": "^7.7.5",
@@ -68,8 +69,8 @@
6869
"@types/uuid": "^3.4.5",
6970
"@types/webpack-env": "^1.13.6",
7071
"@types/yup": "^0.26.17",
71-
"@typescript-eslint/eslint-plugin": "^1.10.1",
72-
"@typescript-eslint/parser": "^1.10.1",
72+
"@typescript-eslint/eslint-plugin": "^2.19.0",
73+
"@typescript-eslint/parser": "^2.19.0",
7374
"babel-loader": "^8.0.6",
7475
"babel-plugin-styled-components": "^1.10.6",
7576
"babel-plugin-tailwind-components": "^0.5.10",
@@ -98,8 +99,8 @@
9899
"style-loader": "^0.23.1",
99100
"tailwindcss": "^0.7.4",
100101
"terser-webpack-plugin": "^1.3.0",
101-
"ts-loader": "^5.3.3",
102-
"typescript": "^3.6.3",
102+
"ts-loader": "^6.2.1",
103+
"typescript": "^3.7.5",
103104
"webpack": "^4.41.2",
104105
"webpack-assets-manifest": "^3.1.1",
105106
"webpack-cli": "^3.3.10",

resources/scripts/.eslintrc.yml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,7 @@ rules:
2323
- always-multiline
2424
"react-hooks/rules-of-hooks":
2525
- error
26-
"react-hooks/exhaustive-deps":
27-
- warn
26+
"react-hooks/exhaustive-deps": 0
2827
"@typescript-eslint/explicit-function-return-type": 0
2928
"@typescript-eslint/explicit-member-accessibility": 0
3029
"@typescript-eslint/no-unused-vars": 0
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import http from '@/api/http';
2+
3+
export interface Schedule {
4+
id: number;
5+
name: string;
6+
cron: {
7+
dayOfWeek: string;
8+
dayOfMonth: string;
9+
hour: string;
10+
minute: string;
11+
};
12+
isActive: boolean;
13+
isProcessing: boolean;
14+
lastRunAt: Date | null;
15+
nextRunAt: Date | null;
16+
createdAt: Date;
17+
updatedAt: Date;
18+
19+
tasks: Task[];
20+
}
21+
22+
export interface Task {
23+
id: number;
24+
sequenceId: number;
25+
action: string;
26+
payload: string;
27+
timeOffset: number;
28+
isQueued: boolean;
29+
createdAt: Date;
30+
updatedAt: Date;
31+
}
32+
33+
export const rawDataToServerTask = (data: any): Task => ({
34+
id: data.id,
35+
sequenceId: data.sequence_id,
36+
action: data.action,
37+
payload: data.payload,
38+
timeOffset: data.time_offset,
39+
isQueued: data.is_queued,
40+
createdAt: new Date(data.created_at),
41+
updatedAt: new Date(data.updated_at),
42+
});
43+
44+
export const rawDataToServerSchedule = (data: any): Schedule => ({
45+
id: data.id,
46+
name: data.name,
47+
cron: {
48+
dayOfWeek: data.cron.day_of_week,
49+
dayOfMonth: data.cron.day_of_month,
50+
hour: data.cron.hour,
51+
minute: data.cron.minute,
52+
},
53+
isActive: data.is_active,
54+
isProcessing: data.is_processing,
55+
lastRunAt: data.last_run_at ? new Date(data.last_run_at) : null,
56+
nextRunAt: data.next_run_at ? new Date(data.next_run_at) : null,
57+
createdAt: new Date(data.created_at),
58+
updatedAt: new Date(data.updated_at),
59+
60+
tasks: (data.relationships?.tasks?.data || []).map((row: any) => rawDataToServerTask(row.attributes)),
61+
});
62+
63+
export default (uuid: string): Promise<Schedule[]> => {
64+
return new Promise((resolve, reject) => {
65+
http.get(`/api/client/servers/${uuid}/schedules`, {
66+
params: {
67+
include: ['tasks'],
68+
},
69+
})
70+
.then(({ data }) => resolve((data.data || []).map((row: any) => rawDataToServerSchedule(row.attributes))))
71+
.catch(reject);
72+
});
73+
};

resources/scripts/components/elements/ContentBox.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ type Props = Readonly<React.DetailedHTMLProps<React.HTMLAttributes<HTMLDivElemen
88
showFlashes?: string | boolean;
99
}>;
1010

11-
export default ({ title, borderColor, showFlashes, children, ...props }: Props) => (
11+
const ContentBox = ({ title, borderColor, showFlashes, children, ...props }: Props) => (
1212
<div {...props}>
1313
{title && <h2 className={'text-neutral-300 mb-4 px-4'}>{title}</h2>}
1414
{showFlashes &&
@@ -24,3 +24,5 @@ export default ({ title, borderColor, showFlashes, children, ...props }: Props)
2424
</div>
2525
</div>
2626
);
27+
28+
export default ContentBox;

resources/scripts/components/elements/Field.tsx

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import React from 'react';
2-
import { Field, FieldProps } from 'formik';
2+
import { Field as FormikField, FieldProps } from 'formik';
33
import classNames from 'classnames';
44

55
interface OwnProps {
@@ -11,8 +11,8 @@ interface OwnProps {
1111

1212
type Props = OwnProps & Omit<React.InputHTMLAttributes<HTMLInputElement>, 'name'>;
1313

14-
export default ({ id, name, label, description, validate, className, ...props }: Props) => (
15-
<Field name={name} validate={validate}>
14+
const Field = ({ id, name, label, description, validate, className, ...props }: Props) => (
15+
<FormikField name={name} validate={validate}>
1616
{
1717
({ field, form: { errors, touched } }: FieldProps) => (
1818
<React.Fragment>
@@ -37,5 +37,7 @@ export default ({ id, name, label, description, validate, className, ...props }:
3737
</React.Fragment>
3838
)
3939
}
40-
</Field>
40+
</FormikField>
4141
);
42+
43+
export default Field;

0 commit comments

Comments
 (0)