# Server Record Hooks

https://dev.wisecp.com/es/server-record-hooks

The eight hooks over server records, server groups and imports.

## Overview

Server records hold the details of the machines services are built on. Saving, updating, deleting and importing all live here.

? One distinction matters: **in the save filter the password is in the clear**, while in the events it is encrypted. The filter sits ahead of the encryption.

## Reference

### Changing the server data before it is saved

filterproduct.server_save_data

`AdminProducts` password IN THE CLEAR

Runs before a server record is written. This is the point **before** the access details are encrypted.

Parameters 3

$set_dataarrayby linkThe server data to be written: name, address, username, **password**, name servers, capacity and access key. ? The password is **in the clear** here (encryption happens after this filter). Writing the record as it stands into a log, an outside service or a table of your own leaks that password in plain text.

$typestringThe server module type.

$detailarrayThe existing record; empty on a new one.

Return 1

voidThe return is ignored; you write over the data.

Listener PHP

```php
Hook::add('filter:product.server_save_data', 10, function (&$set_data, $type, $detail) {
    // THE PASSWORD IS IN THE CLEAR HERE: do not write the record anywhere as it stands.
    $set_data['ns1'] = Acme::defaultNs(1);
    $set_data['ns2'] = Acme::defaultNs(2);
});
```

### Following a server being added

actionproduct.server_created

`AdminProducts` password encrypted

Runs after a new server record is created.

Parameters 3

$idintThe id of the new server.

$set_dataarrayThe saved data. Unlike in the filter the password here is **encrypted**: you cannot read and use it.

$typestringThe server module type.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:product.server_created', 10, function ($id, $set_data, $type) {
    // The password is encrypted here: give monitoring only the address.
    Acme::addMonitor($id, $set_data['ip'] ?? '');
});
```

### Following a server update

actionproduct.server_updated

`AdminProducts` before and after in hand

Runs after a server record is updated.

Parameters 4

$idintThe server id.

$set_dataarrayThe new data; the password is encrypted.

$typestringThe new module type.

$detailarrayThe record from **before** the update, there to measure the difference.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:product.server_updated', 10, function ($id, $set_data, $type, $detail) {
    // Move monitoring when the address changed.
    if (($detail['ip'] ?? '') !== ($set_data['ip'] ?? ''))
        Acme::moveMonitor($id, $set_data['ip'] ?? '');
});
```

### Stopping a server deletion

gateproduct.server_delete

`AdminProducts` before the write

Runs before a server record is deleted. The record goes; **the machine and the accounts on it stay**.

Parameters 2

$idintThe id of the server to be deleted.

$detailarrayThe server record: name, address and type.

Return 1

string|null**A non-empty text blocks the operation** and is shown to the administrator as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:product.server_delete', 10, function ($id, $detail) {
    // The record goes but the accounts on the machine remain.
    if (Acme::serverHasServices($id)) return 'Live services still run on this server.';

    return null;
});
```

### Following a server being deleted

actionproduct.server_deleted

`AdminProducts` after deletion

Runs after the server record is deleted.

Parameters 2

$iintThe id of the deleted server.

$detailarrayThe record as it stood at deletion.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:product.server_deleted', 10, function ($i, $detail) {
    Acme::dropMonitor($i);
});
```

### Following an import from a server

actionproduct.server_imported

`AdminProducts` a list of summary text

Runs after the accounts on a server are brought in as services.

Parameters 2

$serverarrayThe server record the import ran against.

$importedarrayA summary of the services created. Each entry is **text**, not a record: it holds the name and id together. If you need the id, query the service rather than parsing the text.

Return 1

voidThe return is ignored.

Listener PHP

```php
Hook::add('action:product.server_imported', 10, function ($server, $imported) {
    // The entries are TEXT, not records.
    Acme::notifyOps(count($imported) . ' services imported from ' . ($server['name'] ?? ''));
});
```

### Changing server group data

filterproduct.server_group_save_data

`AdminProducts` servers as a text list

Runs before a server group is saved.

Parameters 2

$dataarrayby linkThe group data to be written: name, fill type and its servers. The server list is **comma-separated text, not an array**: split it before treating it as one.

$detailarrayThe existing record; empty on a new one.

Return 1

voidThe return is ignored; you write over the data.

Listener PHP

```php
Hook::add('filter:product.server_group_save_data', 10, function (&$data, $detail) {
    // The server list is COMMA TEXT, not an array.
    $ids = array_filter(explode(',', (string) ($data['servers'] ?? '')));
    $data['servers'] = implode(',', Acme::onlyHealthy($ids));
});
```

### Stopping a server group deletion

gateproduct.server_group_delete

`AdminProducts` before the write

Runs before a server group is deleted.

Parameters 2

$idintThe id of the group to be deleted.

$detailarrayThe group record.

Return 1

string|null**A non-empty text blocks the operation** and is shown to the administrator as the error. An empty return lets it carry on.

Listener PHP

```php
Hook::add('gate:product.server_group_delete', 10, function ($id, $detail) {
    if (Acme::groupBoundToProducts($id)) return 'Products are still bound to this group.';

    return null;
});
```

## Pitfalls

> **In the save filter the password is in the clear**
> 
> The server save filter runs **before the encryption**: the password field holds plain text. Writing that array to a log, sending it to an outside service or copying it into a table of your own leaks the server password in the clear. In the events the same field is encrypted.

> **Deleting the record does not empty the machine**
> 
> The deletion removes only the **record**. The accounts, domains and data on the server stay where they are; the system stops knowing about them. The delete gate is your last chance to notice.

## Related Articles

- Product Hooks
- [Service Hooks](https://dev.wisecp.com/en/service-lifecycle-hooks)
- [How Hooks Work](https://dev.wisecp.com/en/how-hooks-work)
