Skip to content

Commit

Permalink
tls internal and closes #14 php 8 required in composer json (#15)
Browse files Browse the repository at this point in the history
* tls internal and closes #14 php 8 required in composer json

* phpstan

* code sniffer fixes

* codesniffer
  • Loading branch information
mattvb91 authored Nov 1, 2023
1 parent 5fb4a46 commit a7b2f66
Show file tree
Hide file tree
Showing 71 changed files with 799 additions and 659 deletions.
6 changes: 4 additions & 2 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,10 @@ jobs:
run: |
docker network create -d bridge caddy-network
docker run --entrypoint caddy --name caddy -d --network="caddy-network" --hostname=caddy -v "$(pwd)/docker/caddy/autosave.json:/config/caddy/autosave.json" -v "$(pwd)/docker/caddy/files:/var/files" -p 80:80 -p 2019:2019 docker.pkg.github.com/mattvb91/caddy-php/caddy:head run --resume
docker run -v $(pwd):/app composer composer install
docker run --network="caddy-network" -e XDEBUG_MODE=coverage -v $(pwd):/app docker.pkg.github.com/mattvb91/${{ env.REPO_NAME }}/composer:head php ./vendor/bin/phpunit --testdox --coverage-clover=coverage.xml
docker run -v $(pwd):/app docker.pkg.github.com/mattvb91/${{ env.REPO_NAME }}/composer:head composer install
docker run -v $(pwd):/app docker.pkg.github.com/mattvb91/${{ env.REPO_NAME }}/composer:head composer phpstan
docker run -v $(pwd):/app docker.pkg.github.com/mattvb91/${{ env.REPO_NAME }}/composer:head composer codesniffer
docker run --network="caddy-network" -e XDEBUG_MODE=coverage -v $(pwd):/app docker.pkg.github.com/mattvb91/${{ env.REPO_NAME }}/composer:head composer phpunit
env:
REPO_NAME: ${{ github.event.repository.name }}
- uses: codecov/[email protected]
Expand Down
8 changes: 8 additions & 0 deletions codesniffer.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?xml version="1.0" ?>
<ruleset name="PHP-Caddy">
<description>PSR-12 with array indenting</description>
<rule ref="PSR12"/>
<rule ref="Generic.Arrays.ArrayIndent">
<exclude name="Generic.Arrays.ArrayIndent.CloseBraceNotNewLine"/>
</rule>
</ruleset>
11 changes: 10 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@
"description": "Control your Caddy instance through PHP",
"type": "package",
"license": "MIT",
"scripts": {
"phpunit": "phpunit --testdox --coverage-clover=coverage.xml",
"phpstan": "phpstan analyse",
"codesniffer": "phpcs ./src ./tests/**/*.php --standard=./codesniffer.xml -p",
"codefixer": "phpcbf ./src ./tests/**/*.php --standard=./codesniffer.xml"
},
"autoload": {
"psr-4": {
"mattvb91\\CaddyPhp\\": "src/"
Expand All @@ -17,10 +23,13 @@
}
},
"require": {
"php": "^8.1",
"guzzlehttp/guzzle": "^7.0"
},
"require-dev": {
"phpunit/phpunit": "^9.5",
"dms/phpunit-arraysubset-asserts": "^0.4.0"
"dms/phpunit-arraysubset-asserts": "^0.4.0",
"phpstan/phpstan": "^1.10",
"squizlabs/php_codesniffer": "^3.7"
}
}
8 changes: 4 additions & 4 deletions docker/composer/Dockerfile
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
FROM composer:2.3
FROM composer:2.6.5

RUN apk --update --no-cache add autoconf g++ make \
&& pecl install -f xdebug-3.1.4 \
RUN apk --update --no-cache add autoconf g++ make linux-headers \
&& pecl install -f xdebug-3.2.2 \
&& docker-php-ext-enable xdebug \
&& apk del --purge autoconf g++ make
&& apk del --purge autoconf g++ make linux-headers


EXPOSE 9000
7 changes: 7 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
parameters:
level: 9
paths:
- src
excludePaths:
- vendor
- tests
162 changes: 91 additions & 71 deletions src/Caddy.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@

use GuzzleHttp\Client;
use GuzzleHttp\Exception\ClientException;
use GuzzleHttp\Exception\GuzzleException;
use mattvb91\CaddyPhp\Config\Admin;
use mattvb91\CaddyPhp\Config\Apps\Http\Server\Routes\Match\Host;
use mattvb91\CaddyPhp\Config\Logging;
use mattvb91\CaddyPhp\Exceptions\CaddyClientException;
use mattvb91\caddyPhp\Interfaces\App;
use mattvb91\CaddyPhp\Interfaces\App;
use mattvb91\CaddyPhp\Interfaces\Arrayable;

class Caddy implements Arrayable
Expand All @@ -22,54 +24,57 @@ class Caddy implements Arrayable
*
* We need to build that path once based on the config and then cache it here. The format is
* [ host_identifier => ['path' => '/anything', 'host' => &$host]
* @var array<string, array{
* path: string,
* host: Host
* }>
*/
private array $_hostsCache = [];
private array $hostsCache = [];

private Client $_client;
private Client $client;

private Admin $_admin;
private Admin $admin;

private ?Logging $_logging;
private ?Logging $logging;

/** @var App[] */
private array $_apps;
private array $apps = [];

private string $_hostname;
private string $hostname;

private string $_cacheHostnameHeader;
private string $cacheHostnameHeader;

public function __construct(
?string $hostname = 'caddy',
?Admin $admin = new Admin(),
?Client $client = null,
?string $cacheHostnameHeader = 'localhost')
{
string $hostname = 'caddy',
Admin $admin = new Admin(),
Client $client = null,
string $cacheHostnameHeader = 'localhost'
) {
$this->setAdmin($admin);

$this->_hostname = $hostname;
$this->_cacheHostnameHeader = $cacheHostnameHeader;

$this->_client = $client ?? new Client([
'base_uri' => $hostname . $this->getAdmin()->getListen() . '/config',
'headers' => [
'Content-Type' => 'application/json',
],
]
);
$this->hostname = $hostname;
$this->cacheHostnameHeader = $cacheHostnameHeader;

$this->client = $client ?? new Client([
'base_uri' => $hostname . $this->getAdmin()->getListen() . '/config',
'headers' => [
'Content-Type' => 'application/json',
],
]);
}

/**
* If you are managing your hosts from an external source (for example db) and not directly in
* your config you should sync your hosts from the caddy config before making any changes for example trying to remove
* hosts
* your config you should sync your hosts from the caddy config before
* making any changes for example trying to remove hosts
*/
public function syncHosts(string $hostIdentifier)
public function syncHosts(string $hostIdentifier): void
{
$this->buildHostsCache($hostIdentifier);

$hosts = json_decode($this->_client->get($this->_hostsCache[$hostIdentifier]['path'])->getBody(), true);
/** @var string[] $hosts */
$hosts = json_decode($this->client->get($this->hostsCache[$hostIdentifier]['path'])->getBody(), true);

$this->_hostsCache[$hostIdentifier]['host']->setHosts($hosts);
$this->hostsCache[$hostIdentifier]['host']->setHosts($hosts);
}

/**
Expand All @@ -79,10 +84,12 @@ public function addHostname(string $hostIdentifier, string $hostname): bool
{
$this->buildHostsCache($hostIdentifier);

if ($this->_client->put($this->_hostsCache[$hostIdentifier]['path'] . '/0', [
if (
$this->client->put($this->hostsCache[$hostIdentifier]['path'] . '/0', [
'json' => $hostname,
])->getStatusCode() === 200) {
$this->_hostsCache[$hostIdentifier]['host']->addHost($hostname);
])->getStatusCode() === 200
) {
$this->hostsCache[$hostIdentifier]['host']->addHost($hostname);
return true;
}

Expand All @@ -96,13 +103,15 @@ public function removeHostname(string $hostIdentifier, string $hostname): bool
{
$this->buildHostsCache($hostIdentifier);

$path = $this->_hostsCache[$hostIdentifier]['path'];
$path = $path . '/' . array_search($hostname, $this->_hostsCache[$hostIdentifier]['host']->getHosts());
$path = $this->hostsCache[$hostIdentifier]['path'];
$path = $path . '/' . array_search($hostname, $this->hostsCache[$hostIdentifier]['host']->getHosts());

if ($this->_client->delete($path, [
if (
$this->client->delete($path, [
'json' => $hostname,
])->getStatusCode() === 200) {
$this->_hostsCache[$hostIdentifier]['host']->syncRemoveHost($hostname);
])->getStatusCode() === 200
) {
$this->hostsCache[$hostIdentifier]['host']->syncRemoveHost($hostname);
return true;
}

Expand All @@ -114,25 +123,31 @@ public function removeHostname(string $hostIdentifier, string $hostname): bool
*
* TODO we should be able to build our $caddy object back up from this.
* So instead of toArray we should be able to do fromArray() or something
*
* @throws \JsonException|\GuzzleHttp\Exception\GuzzleException
*/
public function getRemoteConfig(): object
{
return json_decode($this->_client->get('/config')->getBody(), false, 512, JSON_THROW_ON_ERROR);
/** @var object */
return json_decode($this->client->get('/config')->getBody(), false, 512, JSON_THROW_ON_ERROR);
}

/**
* This is responsible for flushing the individual caches of items on the caddy server.
*
* @param string[] $surrogates
* @throws GuzzleException
*/
public function flushSurrogates(array $surrogates): bool
{
//TODO this is missing the fact that you could customize your cache paths.

return $this->_client->request('PURGE', 'http://' . $this->_hostname . '/cache/souin', [
'headers' => [
'Surrogate-Key' => implode(', ', $surrogates),
'Host' => $this->_cacheHostnameHeader,
],
])->getStatusCode() === 204;
return $this->client->request('PURGE', 'http://' . $this->hostname . '/cache/souin', [
'headers' => [
'Surrogate-Key' => implode(', ', $surrogates),
'Host' => $this->cacheHostnameHeader,
],
])->getStatusCode() === 204;
}

/**
Expand All @@ -142,11 +157,13 @@ public function flushSurrogates(array $surrogates): bool
public function load(): bool
{
try {
return $this->_client->post('/load', [
'json' => $this->toArray(),
])->getStatusCode() === 200;
return $this->client->post('/load', [
'json' => $this->toArray(),
])->getStatusCode() === 200;
} catch (ClientException $e) {
throw new CaddyClientException($e->getResponse()->getBody() . PHP_EOL . json_encode($this->toArray(), JSON_PRETTY_PRINT));
throw new CaddyClientException(
$e->getResponse()->getBody() . PHP_EOL . json_encode($this->toArray(), JSON_PRETTY_PRINT)
);
}
}

Expand All @@ -155,37 +172,35 @@ public function load(): bool
*/
public function getClient(): Client
{
return $this->_client;
return $this->client;
}

public function getAdmin(): ?Admin
public function getAdmin(): Admin
{
return $this->_admin;
return $this->admin;
}

protected function setAdmin(Admin $admin): static
{
$this->_admin = $admin;
$this->admin = $admin;

return $this;
}

public function setLogging(Logging $logging)
public function setLogging(Logging $logging): static
{
$this->_logging = $logging;
$this->logging = $logging;

return $this;
}

public function addApp(App $app): static
{
$namespace = strtolower(substr(strrchr(get_class($app), '\\'), 1));
/** @var string $name */
$name = strrchr(get_class($app), '\\');
$namespace = strtolower(substr($name, 1));

if (!isset($this->_apps)) {
$this->_apps = [$namespace => $app];
} else {
$this->_apps[$namespace] = $app;
}
$this->apps[$namespace] = $app;

return $this;
}
Expand All @@ -194,34 +209,39 @@ public function toArray(): array
{
$config = [];

if (isset($this->_admin)) {
$config['admin'] = $this->_admin->toArray();
if (isset($this->admin)) {
$config['admin'] = $this->admin->toArray();
}

if (isset($this->_logging)) {
$config['logging'] = $this->_logging->toArray();
if (isset($this->logging)) {
$config['logging'] = $this->logging->toArray();
}

if (isset($this->_apps)) {
if (count($this->apps)) {
$apps = [];

array_map(static function (App $app, string $appNamespace) use (&$apps) {
$apps[$appNamespace] = $app->toArray();
}, $this->_apps, array_keys($this->_apps));
}, $this->apps, array_keys($this->apps));

$config['apps'] = $apps;
}

return $config;
}

/**
* @param string $hostIdentifier
* @return void
* @throws \Exception
*/
protected function buildHostsCache(string $hostIdentifier): void
{
if (!in_array($hostIdentifier, $this->_hostsCache, true)) {
if (!key_exists($hostIdentifier, $this->hostsCache)) {
//Find the host so we can get its path

$hostPath = '';
foreach ($this->_apps as $app) {
$hostPath = null;
foreach ($this->apps as $app) {
if ($found = findHost($app, $hostIdentifier)) {
$hostPath = $found;
break;
Expand All @@ -232,7 +252,7 @@ protected function buildHostsCache(string $hostIdentifier): void
throw new \Exception('Host does not exist. Check your host identified');
}

$this->_hostsCache[$hostIdentifier] = $hostPath;
$this->hostsCache[$hostIdentifier] = $hostPath;
}
}
}
}
5 changes: 3 additions & 2 deletions src/Config/Admin.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ class Admin implements Arrayable
{
/**
* If true, the admin endpoint will be completely disabled.
* Note that this makes any runtime changes to the config impossible, since the interface to do so is through the admin endpoint.
* Note that this makes any runtime changes to the config impossible,
* since the interface to do so is through the admin endpoint.
*/
private bool $disabled = false;

Expand Down Expand Up @@ -51,4 +52,4 @@ public function toArray(): array
'listen' => $this->getListen(),
];
}
}
}
Loading

0 comments on commit a7b2f66

Please sign in to comment.