Compare commits

...

36 Commits

Author SHA1 Message Date
tracer f04c306f91 added chekcs for cron job 2024-05-01 18:57:37 +02:00
tracer 5d2e95ac3d added chekcs for cron job 2024-05-01 18:41:24 +02:00
tracer 4e056f6831 added chekcs for cron job 2024-05-01 18:39:55 +02:00
tracer 753e96ed85 added self for nameservers for openApi defaults. 2024-04-30 19:33:07 +02:00
tracer d31ee8bdec added self for nameservers for openApi defaults. 2024-04-30 17:27:31 +02:00
tracer 26b0b6de6b modified version update 2024-04-30 17:22:43 +02:00
tracer efb069eb5a modified version update 2024-04-30 14:11:54 +02:00
tracer 514c77de55 modified version update 2024-04-30 14:08:59 +02:00
tracer 7b40624218 modified version update 2024-04-30 13:49:59 +02:00
tracer 06df37ed3c modified version update 2024-04-30 13:35:21 +02:00
tracer c6ece08a0b changed config table 2024-04-30 13:21:01 +02:00
tracer 0f13e29fe9 changed config table 2024-04-30 13:14:05 +02:00
tracer b06128e819 changed config table 2024-04-30 13:11:54 +02:00
tracer f25e90f292 added self for nameservers for openApi defaults. 2024-04-30 11:13:24 +02:00
tracer d0224f6746 new migration 2024-04-27 15:43:37 +02:00
tracer 71a275198f added version to api 2024-04-26 19:24:04 +02:00
tracer 578f76426e added version to api 2024-04-26 19:22:44 +02:00
tracer 96689879c4 try to fix cors issues 2024-04-25 21:03:35 +02:00
tracer e9b14a11d7 try to fix cors issues 2024-04-25 20:58:01 +02:00
tracer a312ad9095 move log out of root 2024-04-23 19:53:57 +02:00
tracer 1518deee87 added a more verbose error if API key is wrong 2024-04-22 20:19:34 +02:00
tracer c9757ead13 fixed cron issues 2024-04-22 18:57:49 +02:00
tracer 3e591eee9c updated readme 2024-04-21 13:52:57 +02:00
tracer 94334694d4 added systemd service & timer 2024-04-21 13:49:02 +02:00
tracer 6fc85b8692 added semaphore fo zone creation 2024-04-21 13:04:38 +02:00
tracer 8784acbed9 made check:panel more verbose 2024-04-19 18:11:21 +02:00
tracer cc4bbbecb4 fixed change path for BindApi class 2024-04-19 16:36:00 +02:00
tracer 730ae25d63 fixed change path for BindApi class 2024-04-19 16:19:17 +02:00
tracer e4f5a4a07e modified apikeys table 2024-04-19 15:50:12 +02:00
tracer 4f52e99a92 added version handling 2024-04-19 15:11:21 +02:00
tracer 176bd9dc83 renamed doains:refresh to domains:update as former update has been dropped 2024-04-18 18:04:25 +02:00
tracer 65ea85da39 updated domain refresh 2024-04-18 17:33:07 +02:00
tracer 2c65f743e7 fixed marking oown panel bug 2024-04-18 14:20:46 +02:00
tracer eaa137291b made name mandatory for apikeys 2024-04-17 20:28:51 +02:00
tracer 17535d825d fixed .htaccess 2024-04-17 15:27:21 +02:00
tracer 93e12b9949 fixed .htaccess 2024-04-17 15:25:53 +02:00
41 changed files with 1703 additions and 730 deletions

3
.gitignore vendored
View File

@ -14,3 +14,6 @@
/config.json.prod
keys.txt
/.phpunit.cache
/var/log/*
/public/openapi/bindapi.json
/public/openapi/bootstrap.php

View File

@ -6,9 +6,6 @@
6. [DynDNS](#6-dyndns)
7. [Conclusion](#7-conclusion)
Don't use this code right now.
You can try 1.0.2, but it's not well tested.
NOTICE: This documentation is not current as of September 2022.
After I finished the refactoring I'll upgrade it.

5
TODO
View File

@ -1,3 +1,6 @@
b4 cron job: check panel, check nameserver1
check log file location
API Endpoint cleanup
check keytype of panel/bindApi
more UNIT tests
DB migrations

148
bindapi.json Normal file
View File

@ -0,0 +1,148 @@
{
"openapi": "3.0.0",
"info": {
"title": "bindAPI",
"version": "1.0.9"
},
"servers": [
{
"url": "{schema}://{hostname}/api",
"description": "The bindAPI URL.",
"variables": {
"schema": {
"enum": [
"http",
"https"
],
"default": "https"
},
"hostname": {
"enum": [
"ns1.24unix.net",
"ns2.24unix.net"
],
"default": "ns2.24unix.net"
}
}
}
],
"paths": {
"/ping": {
"get": {
"tags": [
"Server"
],
"description": "Checks for connectivity and valid APIkey",
"operationId": "ping",
"responses": {
"200": {
"description": "OK"
},
"401": {
"description": "API key is missing or invalid."
}
},
"security": [
{
"Authorization": []
}
]
}
},
"/version": {
"get": {
"tags": [
"Server"
],
"description": "Check the API version of the nameserver.",
"operationId": "version",
"responses": {
"200": {
"description": "x.y.z, aka major, minor, patch"
},
"401": {
"description": "API key is missing or invalid."
}
},
"security": [
{
"Authorization": []
}
]
}
},
"/domains": {
"get": {
"tags": [
"Domains"
],
"summary": "List all domains.",
"description": "Returns a list of all domains on this server.",
"operationId": "getAllDomains",
"responses": {
"200": {
"description": "OK"
},
"401": {
"description": "API key is missing or invalid."
},
"404": {
"description": "Domain not found."
}
},
"security": [
{
"Authorization": []
}
]
}
},
"/domains/{name}": {
"get": {
"tags": [
"Domains"
],
"summary": "Returns a single domain.",
"description": "Returns information of a single domain specified by its domain name.",
"operationId": "getSingleDomain",
"parameters": [
{
"name": "name",
"in": "path",
"required": true,
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "OK"
},
"401": {
"description": "API key is missing or invalid."
},
"404": {
"description": "Domain not found."
}
},
"security": []
}
}
},
"components": {
"securitySchemes": {
"Authorization": {
"type": "apiKey",
"description": "Api Authentication",
"name": "X-API-Key",
"in": "header"
}
}
},
"tags": [
{
"name": "Server"
}
]
}

View File

@ -1,8 +1,8 @@
{
"name": "24unix/bindapi",
"description": "manage Bind9 DNS server via REST API",
"version": "2023.0.1",
"build_number": "338",
"version": "1.0.9",
"build_number": "374",
"authors": [
{
"name": "Micha Espey",
@ -30,7 +30,7 @@
"robmorgan/phinx": "^0.15",
"symfony/property-access": "^6.1",
"symfony/serializer": "^6.1",
"zircote/swagger-php": "^4.2"
"zircote/swagger-php": "^4.8"
},
"config": {
"optimize-autoloader": true,

108
composer.lock generated
View File

@ -4,20 +4,20 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "06c636117e4391261d02c4bf3a2f356d",
"content-hash": "5dbbe0ea570912e4a3664e4798edaccd",
"packages": [
{
"name": "arubacao/tld-checker",
"version": "1.2.227",
"version": "1.2.229",
"source": {
"type": "git",
"url": "https://github.com/arubacao/tld-checker.git",
"reference": "aee43babd8548eb407923002a5c978d9a95ec409"
"reference": "194a704aef0b5e07b5695c5a2769f664e7cf9185"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/arubacao/tld-checker/zipball/aee43babd8548eb407923002a5c978d9a95ec409",
"reference": "aee43babd8548eb407923002a5c978d9a95ec409",
"url": "https://api.github.com/repos/arubacao/tld-checker/zipball/194a704aef0b5e07b5695c5a2769f664e7cf9185",
"reference": "194a704aef0b5e07b5695c5a2769f664e7cf9185",
"shasum": ""
},
"require": {
@ -65,9 +65,9 @@
],
"support": {
"issues": "https://github.com/arubacao/tld-checker/issues",
"source": "https://github.com/arubacao/tld-checker/tree/1.2.227"
"source": "https://github.com/arubacao/tld-checker/tree/1.2.229"
},
"time": "2024-04-07T04:01:17+00:00"
"time": "2024-04-21T04:01:27+00:00"
},
{
"name": "cakephp/chronos",
@ -515,16 +515,16 @@
},
{
"name": "monolog/monolog",
"version": "3.5.0",
"version": "3.6.0",
"source": {
"type": "git",
"url": "https://github.com/Seldaek/monolog.git",
"reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448"
"reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/c915e2634718dbc8a4a15c61b0e62e7a44e14448",
"reference": "c915e2634718dbc8a4a15c61b0e62e7a44e14448",
"url": "https://api.github.com/repos/Seldaek/monolog/zipball/4b18b21a5527a3d5ffdac2fd35d3ab25a9597654",
"reference": "4b18b21a5527a3d5ffdac2fd35d3ab25a9597654",
"shasum": ""
},
"require": {
@ -547,7 +547,7 @@
"phpstan/phpstan": "^1.9",
"phpstan/phpstan-deprecation-rules": "^1.0",
"phpstan/phpstan-strict-rules": "^1.4",
"phpunit/phpunit": "^10.1",
"phpunit/phpunit": "^10.5.17",
"predis/predis": "^1.1 || ^2",
"ruflin/elastica": "^7",
"symfony/mailer": "^5.4 || ^6",
@ -600,7 +600,7 @@
],
"support": {
"issues": "https://github.com/Seldaek/monolog/issues",
"source": "https://github.com/Seldaek/monolog/tree/3.5.0"
"source": "https://github.com/Seldaek/monolog/tree/3.6.0"
},
"funding": [
{
@ -612,7 +612,7 @@
"type": "tidelift"
}
],
"time": "2023-10-27T15:32:31+00:00"
"time": "2024-04-12T21:02:21+00:00"
},
{
"name": "netresearch/jsonmapper",
@ -1194,47 +1194,46 @@
},
{
"name": "symfony/console",
"version": "v6.4.6",
"version": "v7.0.6",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f"
"reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/a2708a5da5c87d1d0d52937bdeac625df659e11f",
"reference": "a2708a5da5c87d1d0d52937bdeac625df659e11f",
"url": "https://api.github.com/repos/symfony/console/zipball/fde915cd8e7eb99b3d531d3d5c09531429c3f9e5",
"reference": "fde915cd8e7eb99b3d531d3d5c09531429c3f9e5",
"shasum": ""
},
"require": {
"php": ">=8.1",
"symfony/deprecation-contracts": "^2.5|^3",
"php": ">=8.2",
"symfony/polyfill-mbstring": "~1.0",
"symfony/service-contracts": "^2.5|^3",
"symfony/string": "^5.4|^6.0|^7.0"
"symfony/string": "^6.4|^7.0"
},
"conflict": {
"symfony/dependency-injection": "<5.4",
"symfony/dotenv": "<5.4",
"symfony/event-dispatcher": "<5.4",
"symfony/lock": "<5.4",
"symfony/process": "<5.4"
"symfony/dependency-injection": "<6.4",
"symfony/dotenv": "<6.4",
"symfony/event-dispatcher": "<6.4",
"symfony/lock": "<6.4",
"symfony/process": "<6.4"
},
"provide": {
"psr/log-implementation": "1.0|2.0|3.0"
},
"require-dev": {
"psr/log": "^1|^2|^3",
"symfony/config": "^5.4|^6.0|^7.0",
"symfony/dependency-injection": "^5.4|^6.0|^7.0",
"symfony/event-dispatcher": "^5.4|^6.0|^7.0",
"symfony/config": "^6.4|^7.0",
"symfony/dependency-injection": "^6.4|^7.0",
"symfony/event-dispatcher": "^6.4|^7.0",
"symfony/http-foundation": "^6.4|^7.0",
"symfony/http-kernel": "^6.4|^7.0",
"symfony/lock": "^5.4|^6.0|^7.0",
"symfony/messenger": "^5.4|^6.0|^7.0",
"symfony/process": "^5.4|^6.0|^7.0",
"symfony/stopwatch": "^5.4|^6.0|^7.0",
"symfony/var-dumper": "^5.4|^6.0|^7.0"
"symfony/lock": "^6.4|^7.0",
"symfony/messenger": "^6.4|^7.0",
"symfony/process": "^6.4|^7.0",
"symfony/stopwatch": "^6.4|^7.0",
"symfony/var-dumper": "^6.4|^7.0"
},
"type": "library",
"autoload": {
@ -1268,7 +1267,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v6.4.6"
"source": "https://github.com/symfony/console/tree/v7.0.6"
},
"funding": [
{
@ -1284,7 +1283,7 @@
"type": "tidelift"
}
],
"time": "2024-03-29T19:07:53+00:00"
"time": "2024-04-01T11:04:53+00:00"
},
{
"name": "symfony/deprecation-contracts",
@ -2297,16 +2296,16 @@
},
{
"name": "zircote/swagger-php",
"version": "4.8.7",
"version": "4.9.0",
"source": {
"type": "git",
"url": "https://github.com/zircote/swagger-php.git",
"reference": "2357fafbb084be0f9eda7b5c1a659704fed65b28"
"reference": "b46a36d006f4db4d761995a5add1e7ab0386ed1d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/2357fafbb084be0f9eda7b5c1a659704fed65b28",
"reference": "2357fafbb084be0f9eda7b5c1a659704fed65b28",
"url": "https://api.github.com/repos/zircote/swagger-php/zipball/b46a36d006f4db4d761995a5add1e7ab0386ed1d",
"reference": "b46a36d006f4db4d761995a5add1e7ab0386ed1d",
"shasum": ""
},
"require": {
@ -2372,9 +2371,9 @@
],
"support": {
"issues": "https://github.com/zircote/swagger-php/issues",
"source": "https://github.com/zircote/swagger-php/tree/4.8.7"
"source": "https://github.com/zircote/swagger-php/tree/4.9.0"
},
"time": "2024-03-23T06:35:46+00:00"
"time": "2024-04-18T22:32:11+00:00"
}
],
"packages-dev": [
@ -2556,16 +2555,16 @@
},
{
"name": "odan/phinx-migrations-generator",
"version": "6.1.1",
"version": "6.2.0",
"source": {
"type": "git",
"url": "https://github.com/odan/phinx-migrations-generator.git",
"reference": "167447e716935c6bffbde8ce86b6baeb3ce55f29"
"reference": "93aa47086d8aa513ac60447838455b51c940227a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/odan/phinx-migrations-generator/zipball/167447e716935c6bffbde8ce86b6baeb3ce55f29",
"reference": "167447e716935c6bffbde8ce86b6baeb3ce55f29",
"url": "https://api.github.com/repos/odan/phinx-migrations-generator/zipball/93aa47086d8aa513ac60447838455b51c940227a",
"reference": "93aa47086d8aa513ac60447838455b51c940227a",
"shasum": ""
},
"require": {
@ -2573,8 +2572,7 @@
"ext-pdo": "*",
"php": "~8.1 || ~8.2",
"riimu/kit-phpencoder": "^2.4",
"robmorgan/phinx": "^0.15.2",
"symfony/console": "^6"
"robmorgan/phinx": "^0.15.2 | ^0.16"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3",
@ -2608,9 +2606,9 @@
],
"support": {
"issues": "https://github.com/odan/phinx-migrations-generator/issues",
"source": "https://github.com/odan/phinx-migrations-generator/tree/6.1.1"
"source": "https://github.com/odan/phinx-migrations-generator/tree/6.2.0"
},
"time": "2024-04-11T15:35:30+00:00"
"time": "2024-04-13T07:45:13+00:00"
},
{
"name": "phar-io/manifest",
@ -4118,16 +4116,16 @@
},
{
"name": "squizlabs/php_codesniffer",
"version": "3.9.1",
"version": "3.9.2",
"source": {
"type": "git",
"url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
"reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909"
"reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/267a4405fff1d9c847134db3a3c92f1ab7f77909",
"reference": "267a4405fff1d9c847134db3a3c92f1ab7f77909",
"url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/aac1f6f347a5c5ac6bc98ad395007df00990f480",
"reference": "aac1f6f347a5c5ac6bc98ad395007df00990f480",
"shasum": ""
},
"require": {
@ -4194,7 +4192,7 @@
"type": "open_collective"
}
],
"time": "2024-03-31T21:03:09+00:00"
"time": "2024-04-23T20:25:34+00:00"
},
{
"name": "theseer/tokenizer",

View File

@ -4,6 +4,6 @@
"dbDatabase": "sampledb",
"dbUser": "sampleuser",
"dbPassword": "secret",
"encryptionKey": "12345678901234567890123456789012",
"encryptionKey": "changeme",
"debug": false
}

View File

@ -0,0 +1,33 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class ConfigTable extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('config', [
'id' => false,
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->addColumn('name', 'string', [
'null' => false,
'limit' => 256,
'collation' => 'utf8mb4_general_ci',
'encoding' => 'utf8mb4',
])
->addColumn('value', 'string', [
'null' => false,
'limit' => 256,
'collation' => 'utf8mb4_general_ci',
'encoding' => 'utf8mb4',
'after' => 'name',
])
->removeColumn('version')
->save();
}
}

View File

@ -0,0 +1,24 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class ApiKeyCreationDate extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('apikeys', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->addColumn('created_at', 'timestamp', [
'null' => false,
'after' => 'apikey',
])
->save();
}
}

View File

@ -0,0 +1,24 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class UniqueConfigValues extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('config', [
'id' => false,
'primary_key' => ['name'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->addIndex(['name'], [
'name' => 'name',
'unique' => true,
])
->save();
}
}

View File

@ -0,0 +1,25 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class TimeStampDefaultforApiKeys extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('apikeys', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->changeColumn('created_at', 'timestamp', [
'null' => true,
'default' => 'current_timestamp()',
'after' => 'apikey',
])
->save();
}
}

View File

@ -0,0 +1,37 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class AddSelfToNameservers extends Phinx\Migration\AbstractMigration
{
public function change()
{
// $this->table('domains', [
// 'id' => false,
// 'primary_key' => ['id'],
// 'engine' => 'InnoDB',
// 'encoding' => 'utf8mb4',
// 'collation' => 'utf8mb4_unicode_ci',
// 'comment' => '',
// 'row_format' => 'DYNAMIC',
// ])
// ->removeColumn('self')
// ->save();
$this->table('nameservers', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->addColumn('self', 'enum', [
'null' => false,
'limit' => 3,
'values' => ['yes', 'no'],
'after' => 'apikey_prefix',
])
->save();
}
}

View File

@ -0,0 +1,41 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class UUIDForConfig extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('config', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->addColumn('id', 'uuid', [
'null' => false,
])
->changeColumn('name', 'string', [
'null' => false,
'limit' => 256,
'collation' => 'utf8mb4_general_ci',
'encoding' => 'utf8mb4',
'after' => 'id',
])
->changeColumn('value', 'string', [
'null' => false,
'limit' => 256,
'collation' => 'utf8mb4_general_ci',
'encoding' => 'utf8mb4',
'after' => 'name',
])
->addIndex(['id'], [
'name' => 'id',
'unique' => true,
])
->save();
}
}

View File

@ -0,0 +1,24 @@
<?php
use Phinx\Db\Adapter\MysqlAdapter;
class DefaultUUIDforConfig extends Phinx\Migration\AbstractMigration
{
public function change()
{
$this->table('config', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'comment' => '',
'row_format' => 'DYNAMIC',
])
->changeColumn('id', 'uuid', [
'null' => false,
'default' => 'uuid()',
])
->save();
}
}

View File

@ -112,23 +112,71 @@ return array (
),
'columns' =>
array (
'version' =>
'id' =>
array (
'TABLE_CATALOG' => 'def',
'TABLE_NAME' => 'config',
'COLUMN_NAME' => 'version',
'COLUMN_NAME' => 'id',
'ORDINAL_POSITION' => 1,
'COLUMN_DEFAULT' => 'uuid()',
'IS_NULLABLE' => 'NO',
'DATA_TYPE' => 'uuid',
'CHARACTER_MAXIMUM_LENGTH' => NULL,
'CHARACTER_OCTET_LENGTH' => NULL,
'NUMERIC_PRECISION' => NULL,
'NUMERIC_SCALE' => NULL,
'DATETIME_PRECISION' => NULL,
'CHARACTER_SET_NAME' => NULL,
'COLLATION_NAME' => NULL,
'COLUMN_TYPE' => 'uuid',
'COLUMN_KEY' => 'PRI',
'EXTRA' => '',
'PRIVILEGES' => 'select,insert,update,references',
'COLUMN_COMMENT' => '',
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
'name' =>
array (
'TABLE_CATALOG' => 'def',
'TABLE_NAME' => 'config',
'COLUMN_NAME' => 'name',
'ORDINAL_POSITION' => 2,
'COLUMN_DEFAULT' => NULL,
'IS_NULLABLE' => 'NO',
'DATA_TYPE' => 'varchar',
'CHARACTER_MAXIMUM_LENGTH' => 11,
'CHARACTER_OCTET_LENGTH' => 44,
'CHARACTER_MAXIMUM_LENGTH' => 256,
'CHARACTER_OCTET_LENGTH' => 1024,
'NUMERIC_PRECISION' => NULL,
'NUMERIC_SCALE' => NULL,
'DATETIME_PRECISION' => NULL,
'CHARACTER_SET_NAME' => 'utf8mb4',
'COLLATION_NAME' => 'utf8mb4_general_ci',
'COLUMN_TYPE' => 'varchar(11)',
'COLUMN_TYPE' => 'varchar(256)',
'COLUMN_KEY' => 'UNI',
'EXTRA' => '',
'PRIVILEGES' => 'select,insert,update,references',
'COLUMN_COMMENT' => '',
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
'value' =>
array (
'TABLE_CATALOG' => 'def',
'TABLE_NAME' => 'config',
'COLUMN_NAME' => 'value',
'ORDINAL_POSITION' => 3,
'COLUMN_DEFAULT' => NULL,
'IS_NULLABLE' => 'NO',
'DATA_TYPE' => 'varchar',
'CHARACTER_MAXIMUM_LENGTH' => 256,
'CHARACTER_OCTET_LENGTH' => 1024,
'NUMERIC_PRECISION' => NULL,
'NUMERIC_SCALE' => NULL,
'DATETIME_PRECISION' => NULL,
'CHARACTER_SET_NAME' => 'utf8mb4',
'COLLATION_NAME' => 'utf8mb4_general_ci',
'COLUMN_TYPE' => 'varchar(256)',
'COLUMN_KEY' => '',
'EXTRA' => '',
'PRIVILEGES' => 'select,insert,update,references',
@ -139,6 +187,60 @@ return array (
),
'indexes' =>
array (
'PRIMARY' =>
array (
1 =>
array (
'Table' => 'config',
'Non_unique' => 0,
'Key_name' => 'PRIMARY',
'Seq_in_index' => 1,
'Column_name' => 'id',
'Collation' => 'A',
'Sub_part' => NULL,
'Packed' => NULL,
'Null' => '',
'Index_type' => 'BTREE',
'Comment' => '',
'Index_comment' => '',
),
),
'name' =>
array (
1 =>
array (
'Table' => 'config',
'Non_unique' => 0,
'Key_name' => 'name',
'Seq_in_index' => 1,
'Column_name' => 'name',
'Collation' => 'A',
'Sub_part' => NULL,
'Packed' => NULL,
'Null' => '',
'Index_type' => 'BTREE',
'Comment' => '',
'Index_comment' => '',
),
),
'id' =>
array (
1 =>
array (
'Table' => 'config',
'Non_unique' => 0,
'Key_name' => 'id',
'Seq_in_index' => 1,
'Column_name' => 'id',
'Collation' => 'A',
'Sub_part' => NULL,
'Packed' => NULL,
'Null' => '',
'Index_type' => 'BTREE',
'Comment' => '',
'Index_comment' => '',
),
),
),
'foreign_keys' => NULL,
),
@ -879,6 +981,30 @@ return array (
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
'created_at' =>
array (
'TABLE_CATALOG' => 'def',
'TABLE_NAME' => 'apikeys',
'COLUMN_NAME' => 'created_at',
'ORDINAL_POSITION' => 5,
'COLUMN_DEFAULT' => 'current_timestamp()',
'IS_NULLABLE' => 'YES',
'DATA_TYPE' => 'timestamp',
'CHARACTER_MAXIMUM_LENGTH' => NULL,
'CHARACTER_OCTET_LENGTH' => NULL,
'NUMERIC_PRECISION' => NULL,
'NUMERIC_SCALE' => NULL,
'DATETIME_PRECISION' => 0,
'CHARACTER_SET_NAME' => NULL,
'COLLATION_NAME' => NULL,
'COLUMN_TYPE' => 'timestamp',
'COLUMN_KEY' => '',
'EXTRA' => '',
'PRIVILEGES' => 'select,insert,update,references',
'COLUMN_COMMENT' => '',
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
),
'indexes' =>
array (
@ -1060,6 +1186,30 @@ return array (
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
'self' =>
array (
'TABLE_CATALOG' => 'def',
'TABLE_NAME' => 'nameservers',
'COLUMN_NAME' => 'self',
'ORDINAL_POSITION' => 7,
'COLUMN_DEFAULT' => NULL,
'IS_NULLABLE' => 'NO',
'DATA_TYPE' => 'enum',
'CHARACTER_MAXIMUM_LENGTH' => 3,
'CHARACTER_OCTET_LENGTH' => 12,
'NUMERIC_PRECISION' => NULL,
'NUMERIC_SCALE' => NULL,
'DATETIME_PRECISION' => NULL,
'CHARACTER_SET_NAME' => 'utf8mb4',
'COLLATION_NAME' => 'utf8mb4_unicode_ci',
'COLUMN_TYPE' => 'enum(\'yes\',\'no\')',
'COLUMN_KEY' => '',
'EXTRA' => '',
'PRIVILEGES' => 'select,insert,update,references',
'COLUMN_COMMENT' => '',
'IS_GENERATED' => 'NEVER',
'GENERATION_EXPRESSION' => NULL,
),
),
'indexes' =>
array (

6
dist/systemd/README.md vendored Normal file
View File

@ -0,0 +1,6 @@
Copy these files to /etc/systems/system, adapt the path in the service unit and enable the timer by issuing:
systemctl daemon-reload
systemctl enable bindAPI.timer
systemctl start bindAPI.timer
systemctl list-timers

6
dist/systemd/bindAPI.service vendored Normal file
View File

@ -0,0 +1,6 @@
[Unit]
Description=BindAPI Service to check zone file and reload configuration
[Service]
User=<paneluser>
ExecStart=/home/users/<user>/<bindApi>/bin/console -q cron:run

10
dist/systemd/bindAPI.timer vendored Normal file
View File

@ -0,0 +1,10 @@
[Unit]
Description=Runs BindAPI every minute
[Timer]
OnBootSec=1min
OnUnitActiveSec=1min
Unit=bindAPI.service
[Install]
WantedBy=timers.target

View File

@ -1,17 +1,20 @@
<?php declare(strict_types=1);
namespace App\Controller;
use Exception;
use App\Service\BindAPI;
error_reporting(error_level: E_ALL);
require dirname(path: __DIR__) . '/vendor/autoload.php';
$uri = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH);
$uri = explode(separator: '/', string: $uri);
$parsedUrl = parse_url(url: $_SERVER['REQUEST_URI'], component: PHP_URL_PATH);
$uri = explode(separator: '/', string: $parsedUrl);
if ($uri[1] !== 'api') {
$baseRoutes = ['app', 'api'];
$uriPrefix = $uriFirstThreeLetters = substr(string: $uri[1], offset: 0, length: 3);
if (!in_array(needle: $uriPrefix, haystack: $baseRoutes)) {
// only handle $baseRoutes, elso go to swagger ui
$scheme = $_SERVER['REQUEST_SCHEME'];
$host = $_SERVER['SERVER_NAME'];
$header = "$scheme://$host/openapi/index.html";
@ -23,15 +26,22 @@ header(header: "Access-Control-Allow-Origin: *");
header(header: "Content-Type: application/json; charset=UTF-8");
header(header: "Access-Control-Allow-Methods: OPTIONS,GET,POST,PUT,DELETE");
header(header: "Access-Control-Max-Age: 3600");
header(header: "Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With");
header(header: "Access-Control-Allow-Headers: Content-Type, Access-Control-Allow-Headers, Authorization, X-Requested-With, x-api-key");
$requestMethod = $_SERVER["REQUEST_METHOD"];
if ($requestMethod === "OPTIONS") {
// Respond with OK status code for preflight requests
http_response_code(response_code: 200);
exit();
}
try {
$app = new BindAPI();
$app = new BindAPI(quiet: false);
$app->handleRequest(requestMethod: $requestMethod, uri: $uri);
} catch (Exception $e) {
echo json_encode(value: [
'error' => $e->getMessage()
]);
}

View File

@ -0,0 +1,5 @@
<?php
const DEFAULT_NS = 'ns2.24unix.net';
const NAMESERVERS = ['ns1.24unix.net', 'ns2.24unix.net'];

View File

@ -19,7 +19,8 @@
<script>
window.onload = function () {
// Begin Swagger UI call region
const ui = SwaggerUIBundle({
let ui;
ui = SwaggerUIBundle({
url: "/openapi/bindapi.json",
dom_id: "#swagger-ui",
deepLinking: true,

View File

@ -1,94 +0,0 @@
<?php declare(strict_types=1);
namespace App\Controller;
error_reporting(error_level: E_ALL);
use App\Repository\DomainRepository;
use App\Repository\DynDNSRepository;
use DI\Container;
use DI\ContainerBuilder;
use DI\DependencyException;
use DI\NotFoundException;
use Exception;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
use function DI\autowire;
/**
*
*/
class BindAPI
{
private Logger $logger;
private Container $container;
/**
* @throws Exception
*/
public function __construct(bool $quiet)
{
// init the logger
$dateFormat = "Y:m:d H:i:s";
$output = "%datetime% %channel%.%level_name% %message%\n"; // %context% %extra%
$formatter = new LineFormatter(format: $output, dateFormat: $dateFormat);
$debug = (new ConfigController(quiet: $quiet))->getConfig(configKey: 'debug');
if ($debug) {
$stream = new StreamHandler(stream: dirname(path: __DIR__, levels: 2) . '/bindAPI.log', level: Level::Debug);
} else {
$stream = new StreamHandler(stream: dirname(path: __DIR__, levels: 2) . '/bindAPI.log', level: Level::Info);
}
$stream->setFormatter(formatter: $formatter);
$this->logger = new Logger(name: 'bindAPI');
$this->logger->pushHandler(handler: $stream);
$this->logger->debug(message: 'bindAPI started');
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
ConfigController::class => autowire()
->constructorParameter(parameter: 'quiet', value: $quiet),
CLIController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
->constructorParameter(parameter: 'quiet', value: $quiet),
DomainController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
->constructorParameter(parameter: 'quiet', value: $quiet),
DomainRepository::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger),
DynDnsRepository::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger),
RequestController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
]);
$this->container = $containerBuilder->build();
}
/**
* @throws DependencyException
* @throws NotFoundException
*/
public function runCommand(array $arguments): void
{
$this->logger->debug(message: 'runCommand()');
$cliController = $this->container->get(name: CLIController::class);
$cliController->runCommand(arguments: $arguments);
}
/**
* @throws DependencyException
* @throws NotFoundException
*/
public function handleRequest(string $requestMethod, array $uri): void
{
$this->logger->debug(message: 'handleRequest()');
$requestController = $this->container->get(name: RequestController::class);
$requestController->handleRequest(requestMethod: $requestMethod, uri: $uri);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -68,7 +68,7 @@ class CommandGroupContainer
exit(1);
}
} else {
echo COLOR_DEFAULT . 'Unknown subcommand ' . COLOR_YELLOW . $subcommand . COLOR_DEFAULT .' for ' . COLOR_YELLOW . $command . COLOR_DEFAULT . '.' . PHP_EOL;
echo COLOR_DEFAULT . 'Unknown command group ' . COLOR_YELLOW . $command . COLOR_DEFAULT . '.' . PHP_EOL;
exit(1);
}
} else {

View File

@ -2,9 +2,6 @@
namespace App\Controller;
/**
*
*/
class ConfigController
{
private array $config;

View File

@ -6,6 +6,7 @@ use App\Entity\Domain;
use App\Repository\DomainRepository;
use App\Repository\NameserverRepository;
use App\Repository\PanelRepository;
use App\Service\ApiClient;
use Monolog\Logger;
error_reporting(error_level: E_ALL);
@ -25,7 +26,7 @@ class DomainController
public function __construct(
private readonly NameserverRepository $nameserverRepository,
private readonly ApiController $checkController,
private readonly ApiClient $checkController,
private readonly DomainRepository $domainRepository,
private readonly PanelRepository $panelRepository,
private readonly ConfigController $configController,
@ -70,28 +71,58 @@ class DomainController
function updateSlaveZones(): void
{
$this->logger->debug(message: 'Delete all slave zones');
$zones = glob(pattern: $this->localZonesDir . '*');
foreach ($zones as $zone) {
unlink(filename: $zone);
}
$this->logger->debug(message: 'update slave zones');
$existingZones = glob(pattern: $this->localZonesDir . '*');
$domains = $this->domainRepository->findAll();
$longestEntry = $this->domainRepository->getLongestEntry('name');
$longestEntry = $this->domainRepository->getLongestEntry(field: 'name');
$self = $this->panelRepository->getSelf();
foreach ($domains as $domain) {
$zoneFile = $this->localZonesDir . $domain->getName();
if (!$this->quiet) {
echo ' ' . COLOR_YELLOW . str_pad($domain->getName(), $longestEntry + 1, " ", STR_PAD_RIGHT) ;
echo ' ' . COLOR_YELLOW . str_pad(string: $domain->getName(), length: $longestEntry + 1, pad_string: " ", pad_type: STR_PAD_RIGHT) ;
}
if ($this->createSlaveZoneFile(domain: $domain)) {
if (strcmp(string1: $self->getName(), string2: $domain->getPanel()) !== 0) {
if (!file_exists(filename: $zoneFile)) {
if (!$this->quiet) {
echo COLOR_GREEN . ' OK' . COLOR_DEFAULT . PHP_EOL;
}
$this->createSlaveZoneFile(domain: $domain);
} else {
if (($key = array_search(needle: $zoneFile, haystack: $existingZones)) !== false) {
if (isset($existingZones[$key])) {
unset($existingZones[$key]);
}
} else {
echo 'missing value: ' . $zoneFile;
}
if (!$this->quiet) {
echo COLOR_DEFAULT . 'Zone already exists.' . PHP_EOL;
}
}
} else {
if (!$this->quiet) {
echo COLOR_GREEN . ' OK' . COLOR_DEFAULT . PHP_EOL;
echo COLOR_DEFAULT . 'We are master for ' . COLOR_YELLOW . $domain->getName() . PHP_EOL;
}
}
}
// remove stale zones
foreach ($existingZones as $zone) {
if (!$this->quiet) {
echo 'Removing stale zone: ' . COLOR_YELLOW . $zone . COLOR_DEFAULT . PHP_EOL;
}
echo $zone . PHP_EOL;
unlink(filename: $zone);
}
$this->createIncludeFile();
$semaphore = $this->localZonesDir . 'zones.flag';
if (file_exists(filename: $semaphore)) {
unlink(filename: $semaphore);
$this->createIncludeFile();
}
}
@ -300,7 +331,9 @@ class DomainController
*/
public function createSlaveZoneFile(Domain $domain): bool
{
$domainName = $domain->getName();
touch(filename: $this->localZonesDir . 'zones.flag');
$domainName = $domain->getName();
$this->logger->info(message: "createZoneFile($domainName)");
// check if we're a master zone

View File

@ -10,39 +10,40 @@ use App\Repository\ApikeyRepository;
use App\Repository\DomainRepository;
use App\Repository\DynDNSRepository;
use App\Repository\PanelRepository;
use App\Service\ApiClient;
use Monolog\Logger;
use OpenApi\Attributes as OAT;
use OpenApi\Attributes as OA;
use OpenApi\Attributes\OpenApi;
use OpenApi\Generator;
use UnhandledMatchError;
use function Symfony\Component\String\s;
// TODO attributes for swaggerUI
/**
*
*/
#[OAT\Info(version: '0.0.1', title: 'bindAPI')]
#[OAT\Server(
#[OA\Info(version: VERSION, title: 'bindAPI')]
#[OA\Server(
url: "{schema}://{hostname}/api",
description: "The bindAPI URL.",
variables: [
new OAT\ServerVariable(
serverVariable: "schema",
default: "https",
enum: ["https", "http"]
new OA\ServerVariable(
serverVariable: 'schema',
default: 'https',
enum: ['http', 'https']
),
new OAT\ServerVariable(
serverVariable: "hostname",
default: "ns2.24unix.net",
new OA\ServerVariable(
serverVariable: 'hostname',
default: DEFAULT_NS,
enum: NAMESERVERS
)
]
)]
#[OAT\Tag(
#[OA\Tag(
name: "Server"
)]
#[OAT\SecurityScheme(
#[OA\SecurityScheme(
securityScheme: "Authorization",
type: "apiKey",
description: "description",
description: "Api Authentication",
name: "X-API-Key",
in: "header"
)]
@ -56,60 +57,96 @@ class RequestController
private array $uri;
/**
* @param ApiController $apiController
* @param ApikeyRepository $apikeyRepository
* @param DomainController $domainController
* @param DomainRepository $domainRepository
* @param DynDNSRepository $dynDNSRepository
* @param PanelRepository $panelRepository
* @param ConfigController $configController
* @param EncryptionController $encryptionController
* @param Logger $logger
*/
public function __construct(
private readonly ApiController $apiController,
private readonly ApikeyRepository $apikeyRepository,
private readonly DomainController $domainController,
private readonly DomainRepository $domainRepository,
private readonly DynDNSRepository $dynDNSRepository,
private readonly PanelRepository $panelRepository,
private readonly ConfigController $configController,
private readonly EncryptionController $encryptionController,
private readonly Logger $logger)
{
$this->status = '';
$this->response = '';
$this->message = '';
$this->result = [];
}
// server tag
private string $baseDir;
/**
* @return void
*/
#[OAT\Get(
path: '/domains',
operationId: 'getAllDomains',
description: 'Returns a list of all domains on this server.',
summary: 'Listing all domains.',
// security: [
// 'Authorization' => [
//
// "read:api"
// ]
// ],
servers: [],
tags: ['Domains'],
#[OA\Get(
path: '/ping',
operationId: 'ping',
description: 'Checks for connectivity and valid APIkey',
security: [
['Authorization' => []]
],
tags: ['Server'],
responses: [
new OAT\Response(
new OA\Response(
response: 200,
description: 'OK'
),
new OAT\Response(
new OA\Response(
response: 401,
description: 'API key is missing or invalid.'
)
]
)]
private function handlePing(): void
{
if ($this->validateApiKey()) {
$this->status = '200 OK';
$this->response = 'pong';
} else {
$this->status = '401 Unauthorized';
$this->message = 'API key is missing or invalid';
}
}
#[OA\Get(
path: '/version',
operationId: 'version',
description: 'Check the API version of the nameserver.',
security: [
['Authorization' => []]
],
tags: ['Server'],
responses: [
new OA\Response(
response: 200,
description: 'x.y.z, aka major, minor, patch'
),
new OA\Response(
response: 401,
description: 'API key is missing or invalid.'
)
]
)]
private function getVersion(): void
{
if ($this->validateApiKey()) {
$this->status = '200 OK';
$composerJson = json_decode(json: file_get_contents(filename: $this->baseDir . 'composer.json'));
$version = $composerJson->version;
$buildNumber = $composerJson->build_number;
$this->result = [
'version' => $version,
'buildnumber' => $buildNumber,
];
} else {
$this->status = '401 Unauthorized';
$this->message = 'API key is missing or invalid';
}
}
#[OA\Get(
path: '/domains',
operationId: 'getAllDomains',
description: 'Returns a list of all domains on this server.',
summary: 'List all domains.',
security: [
['Authorization' => []]
],
tags: ['Domains'],
responses: [
new OA\Response(
response: 200,
description: 'OK'
),
new OA\Response(
response: 401,
description: 'API key is missing or invalid.'
),
new OAT\Response(
new OA\Response(
response: 404,
description: 'Domain not found.'
)]
@ -131,16 +168,6 @@ class RequestController
/**
*/
private function handlePing(): void
{
if ($this->checkPassword()) {
$this->status = '200 OK';
$this->response = 'pong';
} else {
$this->status = '401 Unauthorized';
$this->message = 'API key is missing or invalid';
}
}
/**
@ -148,7 +175,7 @@ class RequestController
*/
private function handleDomains(): void
{
if ($this->checkPassword()) {
if ($this->validateApiKey()) {
try {
match ($this->requestMethod) {
'GET' => $this->handleDomainsGetRequest(),
@ -164,131 +191,8 @@ class RequestController
}
/**
* @OA\Tag(name = "Server")
* @OA\Get(
* path = "/ping",
* summary = "Returning pong.",
* description = "Can be used to check API or server availability.",
* tags={"Server"},
* @OA\Response(response = "200", description = "OK"),
* @OA\Response(response = "401", description = "API key is missing or invalid."),
* security={
* {"Authorization":{"read"}}
* }
* )
*
* @OA\Tag(name = "Domains")
* @OA\Put(
* path="/domains/{name}",
* summary="Updates a domain.",
* description="Updates a domain. Only supplied fields will be updated, existing won't be affected.",
* tags={"Domains"},
* @OA\Response(response="200", description="OK"),
* @OA\Response(response = "401", description = "API key is missing or invalid."),
* @OA\Response(response="404", description="Domain not found."),
* security={
* {"Authorization":{"read":"write"}}
* }
* )
* @OA\Delete (
* path="/domains/{name}",
* summary="Deletes a domain.",
* description="Deletes a domain.",
* tags={"Domains"},
* @OA\Response(response="200", description="OK"),
* @OA\Response(response = "401", description = "API key is missing or invalid."),
* @OA\Response(response="404", description="Domain not found."),
* security={
* {"Authorization":{"read":"write"}}
* }
* )
* @param string $requestMethod
* @param array $uri
*
* @return void
*/
#[
OAT\Get(
path: '/domains/{name}',
operationId: 'getSingleDomain',
description: 'Returns information of a single domain specified by its domain name.',
summary: 'Returns a single domain.',
security: [
],
tags: ['Domains'],
parameters: [
new OAT\Parameter(name: 'name', in: 'path', required: true, schema: new OAT\Schema(type: 'string')),
],
responses: [
new OAT\Response(
response: 200,
description: 'OK'
),
new OAT\Response(
response: 401,
description: 'API key is missing or invalid.'
),
new OAT\Response(
response: 404,
description: 'Domain not found.'
)]
)]
public function handleRequest(string $requestMethod, array $uri): void
{
$this->logger->debug(message: "Request: $requestMethod $uri[1]");
$this->requestMethod = strtoupper(string: $requestMethod);
$this->uri = $uri;
$command = $this->uri[2];
if (empty($command) || !(($command == 'domains') || ($command == 'ping') || ($command == 'apidoc') || ($command == 'dyndns'))) {
$this->status = "404 Not Found";
$this->message = "Endpoint not found.";
} else {
try {
match ($command) {
'dyndns' => $this->handleDynDNS(),
'ping' => $this->handlePing(),
'domains' => $this->handleDomains(),
};
} catch (UnhandledMatchError) {
$this->status = '400 Bad Request';
$this->message = 'Unknown path: ' . $command;
}
}
if (!empty($this->status)) {
header(header: $_SERVER['SERVER_PROTOCOL'] . ' ' . $this->status);
}
if (!empty($this->response)) {
echo json_encode(value: [
'response' => $this->response
]);
} elseif (!empty($this->result)) {
echo json_encode(value: [
'result' => $this->result
]);
} elseif (!empty($this->message)) {
echo json_encode(value: [
'message' => $this->message
]);
} else {
echo json_encode(value: [
'message' => $this->message ?? 'Error: No message.'
]);
}
}
/**
* @return bool
*/
private function checkPassword(): bool
private function validateApiKey(): bool
{
$headers = array_change_key_case(array: getallheaders(), case: CASE_UPPER);
$apiKey = $headers['X-API-KEY'] ?? '';
@ -318,10 +222,33 @@ class RequestController
return true;
}
#[OA\Get(
path: '/domains/{name}',
operationId: 'getSingleDomain',
description: 'Returns information of a single domain specified by its domain name.',
summary: 'Returns a single domain.',
security: [
['Authorization' => []]
],
tags: ['Domains'],
parameters: [
new OA\Parameter(name: 'name', in: 'path', required: true, schema: new OA\Schema(type: 'string')),
],
responses: [
new OA\Response(
response: 200,
description: 'OK'
),
new OA\Response(
response: 401,
description: 'API key is missing or invalid.'
),
new OA\Response(
response: 404,
description: 'Domain not found.'
)]
/**
* @return void
*/
)]
private function handleDomainsGetRequest(): void
{
$name = $this->uri[3] ?? '';
@ -433,9 +360,6 @@ class RequestController
}
/**
* @return void
*/
private function handleDomainsDeleteRequest(): void
{
$deleteData = fopen(filename: 'php://input', mode: 'r');
@ -470,7 +394,7 @@ class RequestController
{
$this->logger->debug(message: 'handleDynDNS()');
if ($this->checkPassword()) {
if ($this->validateApiKey()) {
$host = $this->uri[3] ?? '';
if (empty($host)) {
@ -537,7 +461,7 @@ class RequestController
$panel = $this->panelRepository->findByName(name: $domain->getPanel());
if (!empty($panel->getAaaa())) {
$domainData = $this->apiController->sendCommand(
$domainData = $this->apiClient->sendCommand(
requestType: 'GET',
serverName: $panel->getName(),
versionIP: 6,
@ -545,7 +469,7 @@ class RequestController
command: 'domains/name/' . $domainName,
serverType: 'panel');
} else {
$domainData = $this->apiController->sendCommand(
$domainData = $this->apiClient->sendCommand(
requestType: 'GET',
serverName: $panel->getName(),
versionIP: 4,
@ -558,7 +482,7 @@ class RequestController
$domainID = $domainDecodedData->id;
if (!empty($panel->getAaaa())) {
$dnsData = $this->apiController->sendCommand(
$dnsData = $this->apiClient->sendCommand(
requestType: 'GET',
serverName: $panel->getName(),
versionIP: 6,
@ -566,7 +490,7 @@ class RequestController
command: 'dns/' . $domainID,
serverType: 'panel');
} else {
$dnsData = $this->apiController->sendCommand(
$dnsData = $this->apiClient->sendCommand(
requestType: 'GET',
serverName: $panel->getName(),
versionIP: 4,
@ -607,7 +531,7 @@ class RequestController
]);
if (!empty($panel->getAaaa())) {
$result = $this->apiController->sendCommand(
$result = $this->apiClient->sendCommand(
requestType: 'PUT',
serverName: $panel->getName(),
versionIP: 6,
@ -617,7 +541,7 @@ class RequestController
body: json_decode(json: $newDnsData, associative: true)
);
} else {
$result = $this->apiController->sendCommand(
$result = $this->apiClient->sendCommand(
requestType: 'PUT',
serverName: $panel->getName(),
versionIP: 4,
@ -645,11 +569,6 @@ class RequestController
}
/**
* @param String $host
*
* @return string
*/
private function getDomain(string $host): string
{
$host = strtolower(string: trim(string: $host));
@ -664,5 +583,89 @@ class RequestController
return $host;
}
// private function apiDoc(): void
// {
// $srcDir = dirname(path: __DIR__);
// $requestControllerPath = $srcDir . '/Controller/RequestController.php';
//
// $openApi = Generator::scan(sources: [$requestControllerPath]);
// header(header: 'Content-Type: application/json');
//
// echo $openApi->toJson();
// exit(0);
// }
public function __construct(
private readonly ApiClient $apiClient,
private readonly ApikeyRepository $apikeyRepository,
private readonly DomainController $domainController,
private readonly DomainRepository $domainRepository,
private readonly DynDNSRepository $dynDNSRepository,
private readonly PanelRepository $panelRepository,
private readonly ConfigController $configController,
private readonly EncryptionController $encryptionController,
private readonly Logger $logger)
{
$this->baseDir = dirname(path: __DIR__, levels: 2) . '/';
$this->status = '';
$this->response = '';
$this->message = '';
$this->result = [];
}
public function handleRequest(string $requestMethod, array $uri): void
{
$this->logger->debug(message: "Request: $requestMethod $uri[1]");
$this->requestMethod = strtoupper(string: $requestMethod);
$this->uri = $uri;
$command = $this->uri[2];
// use my router class from address book?
$routes = ['ping', 'version', 'domains', 'apidoc', 'dyndns'];
if (empty($command) || !(in_array(needle: $command, haystack: $routes))) {
$this->status = "404 Not Found";
$this->message = "Endpoint not found.";
} else {
try {
match ($command) {
// server
'ping' => $this->handlePing(),
'version' => $this->getVersion(),
// domains
'domains' => $this->handleDomains(),
'dyndns' => $this->handleDynDNS(),
// 'apidoc' => $this->apiDoc(),
};
} catch (UnhandledMatchError) {
$this->status = '400 Bad Request';
$this->message = 'Unknown path: ' . $command;
}
}
// process api requests
if (!empty($this->status)) {
header(header: $_SERVER['SERVER_PROTOCOL'] . ' ' . $this->status);
}
if (!empty($this->response)) {
echo json_encode(value: [
'response' => $this->response
]);
} elseif (!empty($this->result)) {
echo json_encode(value: $this->result);
} elseif (!empty($this->message)) {
echo json_encode(value: [
'message' => $this->message
]);
} else {
echo json_encode(value: [
'message' => $this->message ?? 'Error: No message.'
]);
}
}
}

View File

@ -5,20 +5,16 @@ namespace App\Entity;
use App\Controller\ConfigController;
use App\Controller\EncryptionController;
use Exception;
use SodiumException;
/**
*
*/
class Apikey
{
public function __construct(
private int $id = 0,
private string $name = '',
private string $apikey = '',
private string $apikeyPrefix = '',
private readonly string $passphrase = ''
private readonly string $passphrase = '',
private string $createdAt = ''
)
{
if ($this->passphrase) {
@ -31,82 +27,45 @@ class Apikey
try {
$this->apikey = $encryptionController->safeEncrypt(message: $this->passphrase, key: $encryptionKey);
} catch (Exception|SodiumException $e) {
} catch (Exception $e) {
exit($e->getMessage() . PHP_EOL);
}
}
}
/**
* @return string
*/
public function getPassphrase(): string
public function getCreatedAt(): string
{
return $this->passphrase;
return $this->createdAt;
}
/**
* @return String
*/
public function getApikey(): string
{
return $this->apikey;
}
/**
* @return string
*/
public function getApikeyPrefix(): string
{
return $this->apikeyPrefix;
}
/**
* @return int
*/
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @return String
*/
public function getName(): string
{
return $this->name;
}
/**
* @param string $apikeyPrefix
*/
public function setApikeyPrefix(string $apikeyPrefix): void
{
$this->apikeyPrefix = $apikeyPrefix;
}
/**
* @param String $apiToken
*/
public function setApikey(string $apikey): void
{
$this->apikey = $apikey;
}
/**
* @param String $name
*/
public function setName(string $name): void
{
$this->name = $name;

View File

@ -12,7 +12,6 @@ use SodiumException;
*
*/
#[OAT\Schema(schema: 'nameserver')]
class Nameserver
{
/**
@ -31,8 +30,10 @@ class Nameserver
private string $aaaa = '',
private readonly string $passphrase = '',
private string $apikey = '',
private string $apikeyPrefix = '')
{
private string $apikeyPrefix = '',
private string $self = 'no'
)
{
if ($this->passphrase) {
$configController = new ConfigController(quiet: false);
$encryptionController = new EncryptionController();
@ -50,6 +51,16 @@ class Nameserver
}
public function getSelf(): string
{
return $this->self;
}
public function setSelf(string $self): void
{
$this->self = $self;
}
/**
* @return string
*/
@ -67,90 +78,90 @@ class Nameserver
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getA(): string
{
return $this->a;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getA(): string
{
return $this->a;
}
/**
* @param string $a
*/
public function setA(string $a): void
{
$this->a = $a;
}
/**
* @param string $a
*/
public function setA(string $a): void
{
$this->a = $a;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getAaaa(): string
{
return $this->aaaa;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getAaaa(): string
{
return $this->aaaa;
}
/**
* @param string $aaaa
*/
public function setAaaa(string $aaaa): void
{
$this->aaaa = $aaaa;
}
/**
* @param string $aaaa
*/
public function setAaaa(string $aaaa): void
{
$this->aaaa = $aaaa;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getApikey(): string
{
return $this->apikey;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getApikey(): string
{
return $this->apikey;
}
/**
* @param string $apikey
*/
public function setApikey(string $apikey): void
{
$this->apikey = $apikey;
}
/**
* @param string $apikey
*/
public function setApikey(string $apikey): void
{
$this->apikey = $apikey;
}
/**
* @return int
*/
#[OAT\Property(type: 'int')]
public function getId(): int
{
return $this->id;
}
/**
* @return int
*/
#[OAT\Property(type: 'int')]
public function getId(): int
{
return $this->id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @param int $id
*/
public function setId(int $id): void
{
$this->id = $id;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getName(): string
{
return $this->name;
}
/**
* @return string
*/
#[OAT\Property(type: 'string')]
public function getName(): string
{
return $this->name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* @param string $name
*/
public function setName(string $name): void
{
$this->name = $name;
}
/**
* @return string

View File

@ -1,6 +1,6 @@
<?php
namespace App\Controller;
namespace App\Provider;
//error_reporting(error_level: E_ALL);
@ -8,6 +8,7 @@ namespace App\Controller;
use PDO;
use PDOException;
use PHPUnit\Exception;
use App\Controller\ConfigController;
/**
*
@ -22,6 +23,7 @@ class DatabaseConnection
const TABLE_PANELS = self::TABLE_PREFIX . "panels";
const TABLE_APIKEYS = self::TABLE_PREFIX . "apikeys";
const TABLE_DYNDNS = self::TABLE_PREFIX . "dyndns";
const TABLE_SETTINGS = self::TABLE_PREFIX . 'config';
public function __construct(private readonly ConfigController $configController)
{

View File

@ -3,7 +3,7 @@ namespace App\Repository;
error_reporting(error_level: E_ALL);
use App\Controller\DatabaseConnection;
use App\Provider\DatabaseConnection;
use App\Controller\EncryptionController;
use App\Entity\Apikey;
use PDO;
@ -18,27 +18,24 @@ class ApikeyRepository
{}
/**
* @return array|false
*/
public function findAll(): bool|array
{
$sql = "
SELECT id, name, apikey_prefix, apikey
SELECT id, name, apikey_prefix, apikey, created_at
FROM " . DatabaseConnection::TABLE_APIKEYS;
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute();
$apikeys = [];
$apiKeys = [];
while ($result = $statement->fetch()) {
$apikey = new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
$apikeys[] = $apikey;
$apikey = new Apikey(id: $result['id'], name: $result['name'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], createdAt: $result['created_at']);
$apiKeys[] = $apikey;
}
return $apikeys;
return $apiKeys;
} catch (PDOException $e) {
exit($e->getMessage());
}

View File

@ -3,7 +3,7 @@
namespace App\Repository;
use App\Controller\ConfigController;
use App\Controller\DatabaseConnection;
use App\Provider\DatabaseConnection;
use App\Entity\Domain;
use Monolog\Logger;
use PDO;
@ -236,6 +236,7 @@ readonly class DomainRepository
}
}
// FIXME check for master/slave in config generation
public function getLongestEntry(string $field): int
{
$sql = "

View File

@ -2,7 +2,7 @@
namespace App\Repository;
use App\Controller\DatabaseConnection;
use App\Provider\DatabaseConnection;
use App\Entity\DynDNS;
use Monolog\Logger;
use PDO;

View File

@ -2,7 +2,7 @@
namespace App\Repository;
use App\Controller\DatabaseConnection;
use App\Provider\DatabaseConnection;
use App\Entity\Nameserver;
use PDO;
use PDOException;
@ -26,7 +26,7 @@ class NameserverRepository
{
$nameservers = [];
$sql = "
SELECT id, name, a, aaaa, apikey, apikey_prefix
SELECT id, name, a, aaaa, apikey, apikey_prefix, self
FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
ORDER BY name";
@ -34,7 +34,7 @@ class NameserverRepository
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute();
while ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
$nameserver = new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
$nameserver = new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], self: $result['self']);
$nameservers[] = $nameserver;
}
return $nameservers;
@ -50,7 +50,7 @@ class NameserverRepository
public function findFirst(): ?Nameserver
{
$sql = "
SELECT id, name, a, aaaa, apikey, apikey_prefix
SELECT id, name, a, aaaa, apikey, apikey_prefix, self
FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
ORDER BY name";
@ -58,7 +58,7 @@ class NameserverRepository
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->execute();
$result = $statement->fetch(mode: PDO::FETCH_ASSOC);
return new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
return new Nameserver(name: $result['name'], id: $result['id'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], self: $result['self']);
} catch (PDOException $e) {
exit($e->getMessage());
}
@ -73,7 +73,7 @@ class NameserverRepository
public function findByID(int $id): ?Nameserver
{
$sql = "
SELECT id, name, a, aaaa, apikey, apikey_prefix
SELECT id, name, a, aaaa, apikey, apikey_prefix, self
FROM . " . DatabaseConnection::TABLE_NAMESERVERS . "
WHERE id = :id";
@ -82,7 +82,7 @@ class NameserverRepository
$statement->bindParam(param: ':id', var: $id);
$statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], self: $result['self']);
} else {
return null;
}
@ -100,7 +100,7 @@ class NameserverRepository
public function findByName(string $name): ?Nameserver
{
$sql = "
SELECT id, name, a, aaaa, apikey, apikey_prefix
SELECT id, name, a, aaaa, apikey, apikey_prefix, self
FROM " . DatabaseConnection::TABLE_NAMESERVERS . "
WHERE name = :name";
@ -109,7 +109,7 @@ class NameserverRepository
$statement->bindParam(param: ':name', var: $name);
$statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix']);
return new Nameserver(name: $result['name'], a: $result['a'], aaaa: $result['aaaa'], apikey: $result['apikey'], apikeyPrefix: $result['apikey_prefix'], self: $result['self']);
} else {
return null;
}
@ -131,11 +131,17 @@ class NameserverRepository
$aaaa = $nameserver->getAaaa();
$apikey = $nameserver->getApikey();
$apikeyPrefix = $nameserver->getApikeyPrefix();
$self = $nameserver->getSelf();
if ($self === '') {
$selfValue = 'no';
} else {
$selfValue = $self;
}
$sql = "
INSERT INTO " . DatabaseConnection::TABLE_NAMESERVERS . " (name, a, aaaa, apikey, apikey_prefix)
VALUES (:name, :a, :aaaa, :apikey, :apikey_prefix)";
INSERT INTO " . DatabaseConnection::TABLE_NAMESERVERS . " (name, a, aaaa, apikey, apikey_prefix, self)
VALUES (:name, :a, :aaaa, :apikey, :apikey_prefix, :self)";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
@ -144,6 +150,7 @@ class NameserverRepository
$statement->bindParam(param: ':aaaa', var: $aaaa);
$statement->bindParam(param: ':apikey', var: $apikey);
$statement->bindParam(param: ':apikey_prefix', var: $apikeyPrefix);
$statement->bindParam(param: ':self', var: $selfValue);
$statement->execute();
return intval(value: $this->databaseConnection->getConnection()->lastInsertId());
@ -166,6 +173,7 @@ class NameserverRepository
$apikey = $nameserver->getApikey();
$apikeyPrefix = $nameserver->getApikeyPrefix();
$passphrase = $nameserver->getPassphrase();
$self =$nameserver->getSelf();
$current = $this->findByID(id: $id);
@ -185,6 +193,10 @@ class NameserverRepository
$apikeyPrefix = $current->getApikeyPrefix();
}
if (empty($self)) {
$self = $current->getSelf();
}
$sql = "
UPDATE " . DatabaseConnection::TABLE_NAMESERVERS . " SET
@ -192,7 +204,8 @@ class NameserverRepository
a = :a,
aaaa = :aaaa,
apikey = :apikey,
apikey_prefix = :apikey_prefix
apikey_prefix = :apikey_prefix,
self = :self
WHERE id = :id";
try {
@ -203,6 +216,7 @@ class NameserverRepository
$statement->bindParam(param: 'aaaa', var: $aaaa);
$statement->bindParam(param: 'apikey', var: $apikey);
$statement->bindParam(param: 'apikey_prefix', var: $apikeyPrefix);
$statement->bindParam(param: 'self', var: $self);
$statement->execute();
try {
sodium_memzero(string: $apikey);

View File

@ -2,7 +2,7 @@
namespace App\Repository;
use App\Controller\DatabaseConnection;
use App\Provider\DatabaseConnection;
use App\Entity\Panel;
use PDO;
use PDOException;
@ -162,6 +162,8 @@ class PanelRepository
$apikey = $panel->getApikey();
$apikeyPrefix = $panel->getApikeyPrefix();
$passphrase = $panel->getPassphrase();
$self = $panel->getSelf();
echo 'self: ' . $self;
$current = $this->findByID(id: $id);
@ -185,7 +187,6 @@ class PanelRepository
$self = $current->getSelf();
}
$sql = "
UPDATE " . DatabaseConnection::TABLE_PANELS . " SET
name = :name,

View File

@ -0,0 +1,69 @@
<?php declare(strict_types=1);
namespace App\Repository;
error_reporting(error_level: E_ALL);
use App\Provider\DatabaseConnection;
use App\Controller\EncryptionController;
use PDO;
use PDOException;
/**
*
*/
readonly class SettingsRepository
{
public function __construct(private DatabaseConnection $databaseConnection, EncryptionController $encryptionController)
{}
public function findByName(string $name): string|bool
{
$sql = "
SELECT value
FROM " . DatabaseConnection::TABLE_SETTINGS . "
WHERE name = :name;
";
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':name', var: $name);
$statement->execute();
if ($result = $statement->fetch(mode: PDO::FETCH_ASSOC)) {
return $result['value'];
} else {
return false;
}
} catch (PDOException $e) {
exit($e->getMessage());
}
}
public function set(string $name, string $value): int
{
$currentSetting = $this->findByName($name);
if ($currentSetting !== false) {
$sql = "
UPDATE " . DatabaseConnection::TABLE_SETTINGS . "
SET value = :value
WHERE name = :name
";
} else {
$sql = "
INSERT INTO " . DatabaseConnection::TABLE_SETTINGS . " (id, name, value)
VALUES (UUID(), :name, :value)
";
}
try {
$statement = $this->databaseConnection->getConnection()->prepare(query: $sql);
$statement->bindParam(param: ':name', var: $name);
$statement->bindParam(param: ':value', var: $value);
$statement->execute();
return intval(value: $this->databaseConnection->getConnection()->lastInsertId());
} catch (PDOException $e) {
exit($e->getMessage());
}
}
}

View File

@ -1,25 +1,13 @@
<?php declare(strict_types=1);
namespace App\Controller;
namespace App\Service;
use UnhandledMatchError;
error_reporting(error_level: E_ALL);
class ApiController
class ApiClient
{
/**
* @param String $requestType
* @param String $serverName
* @param int $versionIP
* @param String $apiKey
* @param String $command
* @param String $serverType
* @param array $body
*
* @return array
*/
function sendCommand(string $requestType, string $serverName, int $versionIP, string $apiKey, string $command, string $serverType, array $body = []): array
{
$error = false;
@ -75,6 +63,7 @@ class ApiController
break;
case 400:
$result = $resultJSON;
$error = true;
break;
case 401:
$result = 'Missing or wrong API Key';
@ -82,11 +71,14 @@ class ApiController
break;
case 404:
$result = '404 Not Found';
$error = true;
break;
case 500:
$result = 'server error';
$error = true;
break;
default:
$error = true;
$result = 'Unhandled error: ' . $httpResponse;
}
} else {
@ -113,7 +105,7 @@ class ApiController
$options = array(
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => true,
CURLOPT_ENCODING => "",
CURLOPT_ENCODING => '',
CURLOPT_AUTOREFERER => true,
CURLOPT_CONNECTTIMEOUT => 120,
CURLOPT_TIMEOUT => 120,
@ -139,4 +131,6 @@ class ApiController
return $header;
}
}

94
src/Service/BindAPI.php Executable file
View File

@ -0,0 +1,94 @@
<?php declare(strict_types=1);
namespace App\Service;
error_reporting(error_level: E_ALL);
use App\Controller\ConfigController;
use App\Controller\CLIController;
use App\Controller\DomainController;
use App\Controller\RequestController;
use App\Repository\DomainRepository;
use App\Repository\DynDNSRepository;
use App\Service\ApiClient;
use DI\Container;
use DI\ContainerBuilder;
use DI\DependencyException;
use DI\NotFoundException;
use Exception;
use Monolog\Formatter\LineFormatter;
use Monolog\Handler\StreamHandler;
use Monolog\Level;
use Monolog\Logger;
use function DI\autowire;
class BindAPI
{
private Logger $logger;
private Container $container;
public function __construct(bool $quiet)
{
// init the logger
$dateFormat = "Y:m:d H:i:s";
$output = "%datetime% %channel%.%level_name% %message%\n"; // %context% %extra%
$formatter = new LineFormatter(format: $output, dateFormat: $dateFormat);
$debug = (new ConfigController(quiet: $quiet))->getConfig(configKey: 'debug');
if ($debug) {
$stream = new StreamHandler(stream: dirname(path: __DIR__, levels: 2) . '/var/log/bindAPI.debug', level: Level::Debug);
} else {
$stream = new StreamHandler(stream: dirname(path: __DIR__, levels: 2) . '/var/log/bindAPI.info', level: Level::Info);
}
$stream->setFormatter(formatter: $formatter);
$this->logger = new Logger(name: 'bindAPI');
$this->logger->pushHandler(handler: $stream);
$this->logger->debug(message: 'bindAPI started');
$containerBuilder = new ContainerBuilder();
$containerBuilder->addDefinitions([
ApiClient::class => autowire(),
ConfigController::class => autowire()
->constructorParameter(parameter: 'quiet', value: $quiet),
CLIController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
->constructorParameter(parameter: 'quiet', value: $quiet),
DomainController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
->constructorParameter(parameter: 'quiet', value: $quiet),
DomainRepository::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger),
DynDnsRepository::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger),
RequestController::class => autowire()
->constructorParameter(parameter: 'logger', value: $this->logger)
]);
$this->container = $containerBuilder->build();
}
/**
* @throws DependencyException
* @throws NotFoundException
*/
public function runCommand(array $arguments): void
{
$this->logger->debug(message: 'runCommand()');
$cliController = $this->container->get(name: CLIController::class);
$cliController->runCommand(arguments: $arguments);
}
/**
* @throws DependencyException
* @throws NotFoundException
*/
public function handleRequest(string $requestMethod, array $uri): void
{
$this->logger->debug(message: 'handleRequest()');
$requestController = $this->container->get(name: RequestController::class);
$requestController->handleRequest(requestMethod: $requestMethod, uri: $uri);
}
}

View File

@ -1,11 +1,12 @@
<?php
use App\Controller\BindAPI;
use App\Service\BindAPI;
error_reporting(error_level: E_ALL & ~E_DEPRECATED);
if (!is_file(filename: dirname(path: __DIR__, levels: 2) . '/vendor/autoload.php')) {
exit('Required runtime components are missing. Try running "composer install".' . PHP_EOL);
echo 'Required runtime components are missing. Try running "' . COLOR_YELLOW . 'composer install' . COLOR_DEFAULT . '".' . PHP_EOL;
exit(1);
}
require dirname(path: __DIR__, levels: 2) . '/vendor/autoload.php';
@ -38,11 +39,11 @@ if (array_key_exists(key: 'v', array: $options) || array_key_exists(key: 'versio
$authorName = $authors[0]->name;
$authorEmail = $authors[0]->email;
echo 'Name: $name' . PHP_EOL;
echo 'Description: $description' . PHP_EOL;
echo 'Version: $version' . PHP_EOL;
echo 'Build Number: $buildNumber' . PHP_EOL;
echo 'Author: $authorName ($authorEmail)' . PHP_EOL;
echo "Name: $name" . PHP_EOL;
echo "Description: $description" . PHP_EOL;
echo "Version: $version" . PHP_EOL;
echo "Build Number: $buildNumber" . PHP_EOL;
echo "Author: $authorName ($authorEmail)" . PHP_EOL;
exit(0);
}