Skip to content

Commit 671162e

Browse files
fix!: better utilize internal service handlers #634, #635
1 parent 562652a commit 671162e

File tree

3 files changed

+47
-46
lines changed

3 files changed

+47
-46
lines changed

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/Endpoint.inc

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1147,8 +1147,10 @@ class Endpoint {
11471147
* Creates a new object for the assigned Model using the data submitted in a POST request.
11481148
*/
11491149
protected function post(): Model|ModelSet {
1150-
# POST request cannot include an ID, strip the ID if present
1151-
unset($this->request_data['id']);
1150+
# POST requests cannot include an ID unless auto_create_id is off
1151+
if ($this->model->auto_create_id) {
1152+
unset($this->request_data['id']);
1153+
}
11521154

11531155
# Construct the model from representation using the client's request data
11541156
$this->model->from_representation(data: $this->request_data);

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Core/Model.inc

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,15 @@ class Model {
8989
*/
9090
public string $id_type = 'integer';
9191

92+
/**
93+
* @var bool $auto_create_id
94+
* Enables or disables automatic creation of an ID for this Model object when the `create()` method is called. If
95+
* set to `true`, the ID will be automatically generated. If set to `false`, an ID must be provided before the
96+
* `create()` method is called. This applies exclusively to Models with $many enabled and is usually only
97+
* relevant to Model classes that use an internal callable method.
98+
*/
99+
public bool $auto_create_id = true;
100+
92101
/**
93102
* @var mixed $parent_id
94103
* For Models acting as children to a different Model class, this property will contain the ID of the parent model
@@ -2036,7 +2045,7 @@ class Model {
20362045
*/
20372046
final public function create(bool $apply = false): Model {
20382047
# Ensure all object Fields and validations succeed for proceeding.
2039-
if ($this->validate(create_id: true)) {
2048+
if ($this->validate(requires_id: !$this->auto_create_id, create_id: $this->auto_create_id)) {
20402049
# Check if creating this object would put the total number of objects over the $many_maximum
20412050
$this->check_many_maximum();
20422051

pfSense-pkg-RESTAPI/files/usr/local/pkg/RESTAPI/Models/Service.inc

Lines changed: 33 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -20,20 +20,17 @@ class Service extends Model {
2020
public function __construct(mixed $id = null, mixed $parent_id = null, mixed $data = [], mixed ...$options) {
2121
# Set Model attributes
2222
$this->internal_callable = 'get_services';
23+
$this->auto_create_id = false; // We don't create services with create(), we just control existing ones
2324
$this->many = true;
2425

2526
# Set Model Fields
26-
$this->name = new StringField(
27-
required: true,
28-
choices_callable: 'get_service_name_choices',
29-
help_text: 'The internal name of the service.',
30-
);
3127
$this->action = new StringField(
3228
required: true,
3329
choices: ['start', 'stop', 'restart'],
3430
write_only: true,
3531
help_text: 'The action to perform against this service.',
3632
);
33+
$this->name = new StringField(read_only: true, help_text: 'The internal name of the service.');
3734
$this->description = new StringField(read_only: true, help_text: 'The full descriptive name of the service.');
3835
$this->enabled = new BooleanField(
3936
read_only: true,
@@ -57,59 +54,52 @@ class Service extends Model {
5754
* @return array An array of available pfSense services and their current statuses.
5855
*/
5956
public static function get_services(): array {
60-
# Variables
61-
$services = get_services();
62-
63-
# Loop through each service and set the proper values
64-
foreach ($services as $id => $service) {
65-
# Ensure the $enabled and $status values are always present
66-
$services[$id]['enabled'] = is_service_enabled($service['name']);
67-
$services[$id]['status'] = is_service_running($service['name']);
68-
}
69-
70-
return $services;
71-
}
72-
73-
/**
74-
* Obtains all internal service choices for the `name` field.
75-
* @return array An array of available service names.
76-
*/
77-
public function get_service_name_choices(): array {
78-
$choices = [];
79-
foreach ($this->get_services() as $service) {
80-
$choices[] = $service['name'];
81-
}
82-
return $choices;
83-
}
84-
85-
/**
86-
* Gets the representation ID for a given service name.
87-
* @param string $name The internal service to obtain the ID for.
88-
* @return int The representation ID of the service with this name.
89-
*/
90-
public function get_id_by_name(string $name): int {
91-
return $this->query(['name' => $name])->first()->id;
57+
return get_services();
9258
}
9359

9460
/**
9561
* Starts or stops a specified service.
9662
*/
97-
public function _create() {
63+
public function _create(): void {
64+
# Obtain the extras for this service. Provides additional info required for some services to be controlled.
65+
$extras = $this->_format_extra_data($this->get_services()[$this->id]);
66+
9867
# Trigger the requested service action
9968
switch ($this->action->value) {
10069
case 'start':
101-
service_control_start(name: $this->name->value, extras: []);
70+
service_control_start(name: $this->name->value, extras: $extras);
10271
break;
10372
case 'stop':
104-
service_control_stop(name: $this->name->value, extras: []);
73+
service_control_stop(name: $this->name->value, extras: $extras);
10574
break;
10675
case 'restart':
107-
service_control_restart(name: $this->name->value, extras: []);
76+
service_control_restart(name: $this->name->value, extras: $extras);
10877
break;
10978
}
11079

111-
# Populate the current status of the service with this name so this object is up-to-date.
112-
$this->id = $this->get_id_by_name($this->name->value);
80+
# Recheck the service status after the action has been performed to ensure the correct status is returned
11381
$this->from_internal();
11482
}
83+
84+
/**
85+
* Formats the 'extra' service data required for some services to be restarted to the weird format pfSense
86+
* sometimes expects. In the webConfigurator, this process is obfuscated by AJAX calls and JavaScript so there
87+
* is no direct function to format this data. This function is a workaround to provide the correct data format.
88+
*
89+
* @param array $service The service data to format.
90+
* @return array The formatted service data suitable for the service control functions 'extras' parameter.
91+
*/
92+
public function _format_extra_data(array $service): array {
93+
# Format OpenVPN service extra data for service control
94+
if ($service['name'] === 'openvpn') {
95+
# 'mode' changes to 'vpnmode'
96+
$service['vpnmode'] = $service['mode'];
97+
unset($service['mode']);
98+
99+
# 'vpnid' changes to just 'id'
100+
$service['id'] = $service['vpnid'];
101+
}
102+
103+
return $service;
104+
}
115105
}

0 commit comments

Comments
 (0)