├── .gitignore ├── build.zip ├── cloud.php ├── composer.json ├── composer.lock ├── config.php.example ├── init.php ├── readme.md └── src ├── APIResponseCodes.php ├── App.php ├── Console ├── AddDomainsCommand.php ├── AddSubdomainsCommand.php ├── BaseCommand.php ├── ChangeIPCommand.php ├── RemoveDomainsCommand.php ├── ShowDomainsCommand.php └── VersionCommand.php ├── FileSystem ├── DomainsReader.php ├── FileReader.php ├── FileWriter.php └── LogWriter.php ├── Models ├── DnsRecord.php └── Domain.php └── Version.php /.gitignore: -------------------------------------------------------------------------------- 1 | /vendor 2 | /*.csv 3 | /config.php 4 | /domains.txt 5 | /.idea -------------------------------------------------------------------------------- /build.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/plzdontshare/cloudflareimport/7eb545e74f3a527d3fb159bbc1312e8e3748429f/build.zip -------------------------------------------------------------------------------- /cloud.php: -------------------------------------------------------------------------------- 1 | add(new \PlzDontShare\CloudFlareImport\Console\VersionCommand($app)); 12 | $console->add(new \PlzDontShare\CloudFlareImport\Console\ChangeIPCommand($app)); 13 | $console->add(new \PlzDontShare\CloudFlareImport\Console\ShowDomainsCommand($app)); 14 | $console->add(new \PlzDontShare\CloudFlareImport\Console\AddDomainsCommand($app)); 15 | $console->add(new \PlzDontShare\CloudFlareImport\Console\AddSubdomainsCommand($app)); 16 | $console->add(new \PlzDontShare\CloudFlareImport\Console\RemoveDomainsCommand($app)); 17 | 18 | $console->run(); -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "require": { 3 | "jamesryanbell/cloudflare": "^1.11.1", 4 | "symfony/console": "^4.2" 5 | }, 6 | "autoload": { 7 | "psr-4": { 8 | "PlzDontShare\\CloudFlareImport\\": "src/" 9 | } 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /composer.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_readme": [ 3 | "This file locks the dependencies of your project to a known state", 4 | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file", 5 | "This file is @generated automatically" 6 | ], 7 | "content-hash": "83f5422d3fd7a6a2c23e63285b358a29", 8 | "packages": [ 9 | { 10 | "name": "jamesryanbell/cloudflare", 11 | "version": "1.11.1", 12 | "source": { 13 | "type": "git", 14 | "url": "https://github.com/jamesryanbell/cloudflare.git", 15 | "reference": "75e93bb822619d8ddb8b1843588caa2fad739491" 16 | }, 17 | "dist": { 18 | "type": "zip", 19 | "url": "https://api.github.com/repos/jamesryanbell/cloudflare/zipball/75e93bb822619d8ddb8b1843588caa2fad739491", 20 | "reference": "75e93bb822619d8ddb8b1843588caa2fad739491", 21 | "shasum": "" 22 | }, 23 | "require-dev": { 24 | "phpunit/phpunit": "< 6", 25 | "satooshi/php-coveralls": "dev-master" 26 | }, 27 | "type": "library", 28 | "autoload": { 29 | "psr-4": { 30 | "Cloudflare\\": "src/CloudFlare" 31 | } 32 | }, 33 | "notification-url": "https://packagist.org/downloads/", 34 | "license": [ 35 | "MIT" 36 | ], 37 | "authors": [ 38 | { 39 | "name": "James Bell", 40 | "email": "james@james-bell.co.uk" 41 | } 42 | ], 43 | "description": "CloudFlare API - PHP", 44 | "keywords": [ 45 | "api", 46 | "cloudflare" 47 | ], 48 | "time": "2017-08-04T19:01:02+00:00" 49 | }, 50 | { 51 | "name": "symfony/console", 52 | "version": "v4.2.3", 53 | "source": { 54 | "type": "git", 55 | "url": "https://github.com/symfony/console.git", 56 | "reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4" 57 | }, 58 | "dist": { 59 | "type": "zip", 60 | "url": "https://api.github.com/repos/symfony/console/zipball/1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4", 61 | "reference": "1f0ad51dfde4da8a6070f06adc58b4e37cbb37a4", 62 | "shasum": "" 63 | }, 64 | "require": { 65 | "php": "^7.1.3", 66 | "symfony/contracts": "^1.0", 67 | "symfony/polyfill-mbstring": "~1.0" 68 | }, 69 | "conflict": { 70 | "symfony/dependency-injection": "<3.4", 71 | "symfony/process": "<3.3" 72 | }, 73 | "provide": { 74 | "psr/log-implementation": "1.0" 75 | }, 76 | "require-dev": { 77 | "psr/log": "~1.0", 78 | "symfony/config": "~3.4|~4.0", 79 | "symfony/dependency-injection": "~3.4|~4.0", 80 | "symfony/event-dispatcher": "~3.4|~4.0", 81 | "symfony/lock": "~3.4|~4.0", 82 | "symfony/process": "~3.4|~4.0" 83 | }, 84 | "suggest": { 85 | "psr/log": "For using the console logger", 86 | "symfony/event-dispatcher": "", 87 | "symfony/lock": "", 88 | "symfony/process": "" 89 | }, 90 | "type": "library", 91 | "extra": { 92 | "branch-alias": { 93 | "dev-master": "4.2-dev" 94 | } 95 | }, 96 | "autoload": { 97 | "psr-4": { 98 | "Symfony\\Component\\Console\\": "" 99 | }, 100 | "exclude-from-classmap": [ 101 | "/Tests/" 102 | ] 103 | }, 104 | "notification-url": "https://packagist.org/downloads/", 105 | "license": [ 106 | "MIT" 107 | ], 108 | "authors": [ 109 | { 110 | "name": "Fabien Potencier", 111 | "email": "fabien@symfony.com" 112 | }, 113 | { 114 | "name": "Symfony Community", 115 | "homepage": "https://symfony.com/contributors" 116 | } 117 | ], 118 | "description": "Symfony Console Component", 119 | "homepage": "https://symfony.com", 120 | "time": "2019-01-25T14:35:16+00:00" 121 | }, 122 | { 123 | "name": "symfony/contracts", 124 | "version": "v1.0.2", 125 | "source": { 126 | "type": "git", 127 | "url": "https://github.com/symfony/contracts.git", 128 | "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf" 129 | }, 130 | "dist": { 131 | "type": "zip", 132 | "url": "https://api.github.com/repos/symfony/contracts/zipball/1aa7ab2429c3d594dd70689604b5cf7421254cdf", 133 | "reference": "1aa7ab2429c3d594dd70689604b5cf7421254cdf", 134 | "shasum": "" 135 | }, 136 | "require": { 137 | "php": "^7.1.3" 138 | }, 139 | "require-dev": { 140 | "psr/cache": "^1.0", 141 | "psr/container": "^1.0" 142 | }, 143 | "suggest": { 144 | "psr/cache": "When using the Cache contracts", 145 | "psr/container": "When using the Service contracts", 146 | "symfony/cache-contracts-implementation": "", 147 | "symfony/service-contracts-implementation": "", 148 | "symfony/translation-contracts-implementation": "" 149 | }, 150 | "type": "library", 151 | "extra": { 152 | "branch-alias": { 153 | "dev-master": "1.0-dev" 154 | } 155 | }, 156 | "autoload": { 157 | "psr-4": { 158 | "Symfony\\Contracts\\": "" 159 | }, 160 | "exclude-from-classmap": [ 161 | "**/Tests/" 162 | ] 163 | }, 164 | "notification-url": "https://packagist.org/downloads/", 165 | "license": [ 166 | "MIT" 167 | ], 168 | "authors": [ 169 | { 170 | "name": "Nicolas Grekas", 171 | "email": "p@tchwork.com" 172 | }, 173 | { 174 | "name": "Symfony Community", 175 | "homepage": "https://symfony.com/contributors" 176 | } 177 | ], 178 | "description": "A set of abstractions extracted out of the Symfony components", 179 | "homepage": "https://symfony.com", 180 | "keywords": [ 181 | "abstractions", 182 | "contracts", 183 | "decoupling", 184 | "interfaces", 185 | "interoperability", 186 | "standards" 187 | ], 188 | "time": "2018-12-05T08:06:11+00:00" 189 | }, 190 | { 191 | "name": "symfony/polyfill-mbstring", 192 | "version": "v1.10.0", 193 | "source": { 194 | "type": "git", 195 | "url": "https://github.com/symfony/polyfill-mbstring.git", 196 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494" 197 | }, 198 | "dist": { 199 | "type": "zip", 200 | "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/c79c051f5b3a46be09205c73b80b346e4153e494", 201 | "reference": "c79c051f5b3a46be09205c73b80b346e4153e494", 202 | "shasum": "" 203 | }, 204 | "require": { 205 | "php": ">=5.3.3" 206 | }, 207 | "suggest": { 208 | "ext-mbstring": "For best performance" 209 | }, 210 | "type": "library", 211 | "extra": { 212 | "branch-alias": { 213 | "dev-master": "1.9-dev" 214 | } 215 | }, 216 | "autoload": { 217 | "psr-4": { 218 | "Symfony\\Polyfill\\Mbstring\\": "" 219 | }, 220 | "files": [ 221 | "bootstrap.php" 222 | ] 223 | }, 224 | "notification-url": "https://packagist.org/downloads/", 225 | "license": [ 226 | "MIT" 227 | ], 228 | "authors": [ 229 | { 230 | "name": "Nicolas Grekas", 231 | "email": "p@tchwork.com" 232 | }, 233 | { 234 | "name": "Symfony Community", 235 | "homepage": "https://symfony.com/contributors" 236 | } 237 | ], 238 | "description": "Symfony polyfill for the Mbstring extension", 239 | "homepage": "https://symfony.com", 240 | "keywords": [ 241 | "compatibility", 242 | "mbstring", 243 | "polyfill", 244 | "portable", 245 | "shim" 246 | ], 247 | "time": "2018-09-21T13:07:52+00:00" 248 | } 249 | ], 250 | "packages-dev": [], 251 | "aliases": [], 252 | "minimum-stability": "stable", 253 | "stability-flags": [], 254 | "prefer-stable": false, 255 | "prefer-lowest": false, 256 | "platform": [], 257 | "platform-dev": [] 258 | } 259 | -------------------------------------------------------------------------------- /config.php.example: -------------------------------------------------------------------------------- 1 | [ 5 | 'email' => '', 6 | 'key' => '', 7 | ], 8 | ]; -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | [ 23 | 'email' => '', 24 | 'key' => '', 25 | ], 26 | ]; 27 | ``` 28 | 29 | По пунктам: 30 | - `email` - Это ваш email в Cloudflare. Обязателен. 31 | - `key` - API ключ, который вы можете получить на странице https://www.cloudflare.com/a/profile нажав на кнопку "View API Key" напротив строчки "Global API Key". 32 | 33 | После настройки `config.php` можно начинать работать. 34 | Скрипт запускается из консоли командой: 35 | ``` 36 | php cloud.php 37 | ``` 38 | 39 | Где `` - одна из доступных команд. 40 | 41 | Доступны следующие команды: 42 | - version - Показывает текущую версию скрипта 43 | - show-domains - Возвращает список всех доменов добавленных в CloudFlare аккаунт 44 | - add-domains - Добавить домены 45 | - add-subdomains - Добавить сабдомены 46 | - remove-domains - Удалить домены из аккаунта 47 | - change-ip - Смена IP у добавленных доменов (у всех DNS записей для каждого домена) 48 | 49 | ### version 50 | Пример запуска: 51 | ``` 52 | php cloud.php version 53 | ``` 54 | 55 | ### show-domains 56 | Доступные параметры: 57 | - --save-to (-s) - позволяет указать имя файла куда будет записан результат (CSV файл) 58 | 59 | Примеры запуска: 60 | ``` 61 | # Обычный 62 | php cloud.php show-domains 63 | 64 | # С сохранением в файл 65 | php cloud.php show-domains --save-to my-domains.csv 66 | ``` 67 | ### add-domains 68 | Доступные аргументы: 69 | - filename - имя файла в котором хранятся домены (по-умолчанию: domains.txt) 70 | 71 | Доступные параметры: 72 | - --ip - IP на который будут прикрепляться домены (не обязателен) 73 | - --wildcard (-w) - Если указан, то для каждого домена будет добавлена wildcard (*) DNS запись 74 | - --skip-existing (-s) - Если указан, то скрипт не будет пытаться добавить DNS записи для доменов которые уже добавлены в аккаунт 75 | - --enable-proxy (-p) - Если указан, то скрипт будет включать проксирование для добавленных DNS записей 76 | - --enable-always-online - Если указан, то скрипт не будет отключать AlwaysOnline для доменов. (по-умолчанию скрипт отключает эту настройку) 77 | - --enable-https - Если указан, вклчюает опцию "Always use Https" (эта опция включает автоматический редирект http -> https) 78 | - --ssl-mode - Изменить тип SSL который использует CloudFlare. Доступные опции: off, flexible, full, strict. 79 | - --security-level - Изменить Security Level в настройках firewall. Доступные опции: essentially_off, low, medium, high, under_attack. 80 | - --stop-on-fail - Остановить выполнение скрипта если не смогли добавить домен (по-умолчанию false) 81 | - --failed-attempts - Кол-во повторных попыток добавить домен в случае ошибки ' Message: domain.com is not a registered domain'. По-умолчанию: 3. (пример: --failed-attempts=5 установит 5 попыток) 82 | 83 | Если не указан параметр `--ip`, то IP должен быть указан для КАЖДОГО домена в файле с доменами (в формате `домен|ip`). 84 | Пример такого файла: 85 | ``` 86 | domain1.com|127.0.0.1 87 | domain2.com|127.0.0.1 88 | domain3.com|127.0.0.2 89 | ``` 90 | 91 | Примеры запуска: 92 | ``` 93 | # Простой (домены лежат в файле domains.txt) 94 | php cloud.php add-domains --enable-proxy --ip "127.0.0.1" 95 | 96 | # С включенным wildcard 97 | php cloud.php add-domains --enable-proxy -w 98 | # или так 99 | php cloud.php add-domains --enable-proxy --wildcard 100 | 101 | # Кастомный файл с доменами 102 | php cloud.php add-domains freenom-domains.txt 103 | 104 | # Изменяем тип SSL, устанавливаем тип Flexible 105 | php cloud.php add-domains --ssl-mode="flexible" --enable-https 106 | # SSL от CloudFlare будет отключен 107 | php cloud.php add-domains --ssl-mode="off" 108 | ``` 109 | 110 | ### add-subdomains 111 | Доступные аргументы: 112 | - filename - имя файла в котором хранятся домены (по-умолчанию: domains.txt) 113 | 114 | Доступные параметры: 115 | - --ip - IP на который будут прикрепляться домены (не обязателен) 116 | - --enable-proxy (-p) - Если указан, то скрипт будет включать проксирование для добавленных DNS записей 117 | 118 | Если не указан параметр `--ip`, то действуют такие же правила как для команды `add-domains`. 119 | Сабдомены так же указываются в файле с доменами через запятую: 120 | ``` 121 | domain1.com|127.0.0.1|www,mobile,network 122 | domain2.com||wp,wordpress,www 123 | domain3.com||www1,www2,www3 124 | ``` 125 | 126 | (если в файле с домена опущен IP, то должен быть обязательно указан параметр `--ip` при вызове скрипта) 127 | 128 | Примеры запуска: 129 | ``` 130 | # Простой (домены лежат в файле domains.txt) 131 | php cloud.php add-subdomains --enable-proxy 132 | 133 | # С указание IP 134 | php cloud.php add-subdomains --ip "127.0.0.1" --enable-proxy 135 | ``` 136 | 137 | ### remove-domains 138 | Доступные аргументы: 139 | - filename - имя файла в котором хранятся домены (по-умолчанию: domains.txt) 140 | 141 | Примеры запуска: 142 | ``` 143 | php cloud.php remove-domains domains.txt 144 | ``` 145 | 146 | ### change-ip 147 | Доступные аргументы: 148 | - filename - имя файла в котором хранятся домены (по-умолчанию: domains.txt) 149 | 150 | Доступные параметры: 151 | - --ip - IP на который будут прикрепляться домены (не обязателен) 152 | 153 | (для IP работают такие же правила как и в командах `add-domains` и `add-subdomains`) 154 | 155 | Примеры запуска: 156 | ``` 157 | php cloud.php change-ip --ip "127.0.0.1" 158 | ``` 159 | 160 | -------------------------------------------------------------------------------- /src/APIResponseCodes.php: -------------------------------------------------------------------------------- 1 | api = $api; 35 | $this->config = $config; 36 | } 37 | 38 | /** 39 | * List zones (permission needed: #zone:read) 40 | * List, search, sort, and filter your zones 41 | * 42 | * @param string|null $name A domain name 43 | * @param string|null $status Status of the zone (active, pending, initializing, moved, deleted) 44 | * @param int|null $page Page number of paginated results 45 | * @param int|null $per_page Number of zones per page 46 | * @param string|null $order Field to order zones by (name, status, email) 47 | * @param string|null $direction Direction to order zones (asc, desc) 48 | * @param string|null $match Whether to match all search requirements or at least one (any) (any, all) 49 | * 50 | * @return array 51 | */ 52 | public function getCFDomains($name = null, $status = null, $page = 1, $per_page = 50, $order = null, $direction = null, $match = null) 53 | { 54 | $zoneApi = new Zone($this->api); 55 | $domains = []; 56 | 57 | do { 58 | $zones = $zoneApi->zones($name, $status, $page, $per_page, $order, $direction, $match); 59 | $this->checkSuccessApiResponse($zones); 60 | $total_count = (int)$zones->result_info->total_count; 61 | $total_pages = ceil($total_count / $per_page); 62 | 63 | foreach ($zones->result as $zone) { 64 | $domains[] = [ 65 | 'domain' => $zone->name, 66 | 'id' => $zone->id, 67 | 'nameservers' => $zone->name_servers, 68 | 'status' => $zone->status, 69 | ]; 70 | } 71 | 72 | $page++; 73 | } while ($page <= $total_pages); 74 | 75 | return $domains; 76 | } 77 | 78 | /** 79 | * @param $zones 80 | */ 81 | private function checkSuccessApiResponse($zones) 82 | { 83 | if ($zones->success !== true) { 84 | throw new RuntimeException($zones->errors[0]->message); 85 | } 86 | } 87 | 88 | /** 89 | * Read domains from $filename 90 | * 91 | * @param string $filename Path to file with domains 92 | * 93 | * @return array 94 | */ 95 | public function readDomains($filename) 96 | { 97 | $reader = new DomainsReader(new FileReader); 98 | 99 | return $reader->readDomains($filename); 100 | } 101 | 102 | /** 103 | * Add domain to CloudFlare 104 | * 105 | * @param Domain $domain 106 | * @param bool $skip_existing 107 | * 108 | * @return array 109 | * @throws Exception 110 | */ 111 | public function addDomain(Domain $domain, $skip_existing) 112 | { 113 | $zone = new Zone($this->api); 114 | 115 | try { 116 | $response = $zone->create($domain->domain); 117 | $domain_info = null; 118 | $this->checkSuccessApiResponse($response); 119 | $result = $response->result; 120 | $domain_info = [ 121 | 'id' => $result->id, 122 | 'nameservers' => (array)$result->name_servers, 123 | ]; 124 | } catch (Exception $e) { 125 | if ($skip_existing === true && $response->errors[0]->code === APIResponseCodes::DOMAIN_ALREADY_EXISTS) { 126 | $domain_info = $this->getCFDomainInfo($domain); 127 | } else { 128 | throw $e; 129 | } 130 | } 131 | 132 | return $domain_info; 133 | } 134 | 135 | /** 136 | * @param Domain $domain 137 | * 138 | * @return array 139 | */ 140 | public function getCFDomainInfo(Domain $domain) 141 | { 142 | $zone = new Zone($this->api); 143 | 144 | $zones_info = $zone->zones($domain->domain); 145 | $this->checkSuccessApiResponse($zones_info); 146 | $zone = $zones_info->result[0]; 147 | 148 | return [ 149 | 'domain' => $zone->name, 150 | 'id' => $zone->id, 151 | 'nameservers' => $zone->name_servers, 152 | 'status' => $zone->status, 153 | ]; 154 | } 155 | 156 | /** 157 | * @param string $zone_id 158 | * 159 | * @return mixed 160 | */ 161 | public function getCFZoneInfo($zone_id) 162 | { 163 | $zone = new Zone($this->api); 164 | $zone_info = $zone->zone($zone_id); 165 | $this->checkSuccessApiResponse($zone_info); 166 | 167 | return $zone_info->result; 168 | } 169 | 170 | /** 171 | * @param string $zone_id 172 | * @param string $ip 173 | * @param string $zone_type 174 | * @param string $zone_name 175 | * @param bool $enable_proxy 176 | * @param bool $skip_existing 177 | * 178 | * @return stdClass 179 | * @throws Exception 180 | */ 181 | public function addDnsRecord($zone_id, $ip, $zone_type, $zone_name, $enable_proxy, $skip_existing) 182 | { 183 | $result = null; 184 | 185 | try 186 | { 187 | $dns = new Zone\Dns($this->api); 188 | 189 | $response = $dns->create($zone_id, $zone_type, $zone_name, $ip, null, $enable_proxy); 190 | $this->checkSuccessApiResponse($response); 191 | 192 | $result = $response->result; 193 | } catch (Exception $e) { 194 | if ($skip_existing === true && $response->errors[0]->code === APIResponseCodes::RECORD_ALREADY_EXISTS) { 195 | $result = $this->getCFZoneInfo($zone_id); 196 | } else { 197 | throw $e; 198 | } 199 | } 200 | 201 | return $result; 202 | } 203 | 204 | /** 205 | * @param string $zone_id 206 | * @param string $dns_id 207 | * @param string $ip 208 | * @param string $zone_type 209 | * @param string $zone_name 210 | * @param bool $enable_proxy 211 | * 212 | * @return stdClass 213 | */ 214 | public function updateDnsRecord($zone_id, $dns_id, $ip, $zone_type, $zone_name, $enable_proxy) 215 | { 216 | $dns = new Zone\Dns($this->api); 217 | $response = $dns->update($zone_id, $dns_id, $zone_type, $zone_name, $ip, null, $enable_proxy); 218 | $this->checkSuccessApiResponse($response); 219 | 220 | return $response->result; 221 | } 222 | 223 | /** 224 | * @param string $zone_id 225 | * 226 | * @return array 227 | */ 228 | public function getDnsRecordsForDomain($zone_id) 229 | { 230 | $dns = new Zone\Dns($this->api); 231 | 232 | $response = $dns->list_records($zone_id); 233 | $this->checkSuccessApiResponse($response); 234 | 235 | return $response->result; 236 | } 237 | 238 | /** 239 | * @param DnsRecord $dnsRecord 240 | * @param string $new_ip 241 | * 242 | * @return stdClass 243 | */ 244 | public function changeIP(DnsRecord $dnsRecord, $new_ip) 245 | { 246 | return $this->updateDnsRecord($dnsRecord->zone_id, $dnsRecord->dns_id, $new_ip, $dnsRecord->type, $dnsRecord->name, $dnsRecord->proxied); 247 | } 248 | 249 | /** 250 | * @param string $zone_id 251 | * @param string $enabled "on" or "off" 252 | * 253 | * @return stdClass 254 | */ 255 | public function setAlwaysOnlineEnabled($zone_id, $enabled) 256 | { 257 | $zone = new Zone\Settings($this->api); 258 | 259 | $response = $zone->change_always_on($zone_id, $enabled); 260 | $this->checkSuccessApiResponse($response); 261 | 262 | return $response->result; 263 | } 264 | 265 | /** 266 | * @param string $zone_id 267 | * @param string $enabled "on" or "off" 268 | * 269 | * @return stdClass 270 | */ 271 | public function setAlwaysUseHttpsEnabled($zone_id, $enabled) 272 | { 273 | $zone = new Zone\Settings($this->api); 274 | 275 | $response = $zone->change_always_use_https($zone_id, $enabled); 276 | $this->checkSuccessApiResponse($response); 277 | 278 | return $response->result; 279 | } 280 | 281 | /** 282 | * Change SSL Mode 283 | * 284 | * @param string $zone_id 285 | * @param string $mode Values: off, flexible, full, strict 286 | * 287 | * @return mixed 288 | */ 289 | public function setSSLMode($zone_id, $mode) 290 | { 291 | $zone = new Zone\Settings($this->api); 292 | 293 | $response = $zone->change_ssl($zone_id, $mode); 294 | $this->checkSuccessApiResponse($response); 295 | 296 | return $response->result; 297 | } 298 | 299 | public function setSecurityLevel($zone_id, $value) 300 | { 301 | $zone = new Zone\Settings($this->api); 302 | 303 | $response = $zone->change_security_level($zone_id, $value); 304 | $this->checkSuccessApiResponse($response); 305 | 306 | return $response->result; 307 | } 308 | 309 | /** 310 | * @param Domain $domain 311 | * @param bool $proxy 312 | * 313 | * @return array 314 | */ 315 | public function addSubdomains(Domain $domain, $proxy) 316 | { 317 | $subdomains = []; 318 | $zone = $this->getCFDomainInfo($domain); 319 | 320 | foreach ($domain->subdomains as $subdomain) { 321 | try 322 | { 323 | $subdomains[] = $this->addDnsRecord($zone['id'], $domain->ip, 'A', $subdomain, $proxy, true); 324 | } catch (Exception $e) {} 325 | } 326 | 327 | return $subdomains; 328 | } 329 | 330 | /** 331 | * @param Domain $domain 332 | */ 333 | public function removeDomain(Domain $domain) 334 | { 335 | $alldomains = $this->getCFDomains(); 336 | 337 | $zone = new Zone($this->api); 338 | foreach ($alldomains as $d) { 339 | if ($d['domain'] === $domain->domain) { 340 | $zone->delete_zone($d['id']); 341 | } 342 | } 343 | } 344 | } -------------------------------------------------------------------------------- /src/Console/AddDomainsCommand.php: -------------------------------------------------------------------------------- 1 | setDescription('Add domains'); 18 | $this->addArgument('filename', InputArgument::OPTIONAL, 'Path to file with domains', 'domains.txt'); 19 | $this->addOption('ip', null, InputOption::VALUE_REQUIRED, 'Specify default IP for all domains'); 20 | $this->addOption('wildcard', 'w', InputOption::VALUE_NONE, 'If specified wildcard dns record will be created'); 21 | $this->addOption('skip-existing', 's', InputOption::VALUE_NONE, 'Skip existing domains'); 22 | $this->addOption('enable-proxy', 'p', InputOption::VALUE_NONE, 'Enable CloudFlare proxy for DNS records'); 23 | $this->addOption('enable-always-online', null, InputOption::VALUE_NONE, 'Skip disabling Always Online'); 24 | $this->addOption('enable-https', null, InputOption::VALUE_NONE, 'Enable "Always use HTTPS" option'); 25 | $this->addOption('ssl-mode', null, InputOption::VALUE_REQUIRED, 'SSL mode (off, flexible, full, strict)'); 26 | $this->addOption('security-level', null, InputOption::VALUE_REQUIRED, 'Set Security Level (essentially_off, low, medium, high, under_attack)'); 27 | $this->addOption('stop-on-fail', null, InputOption::VALUE_NONE, 'Stop if can not add domain to CF'); 28 | $this->addOption('failed-attempts', null, InputOption::VALUE_REQUIRED, 'Stop if can not add domain to CF', 3); 29 | } 30 | 31 | protected function process(InputInterface $input, OutputInterface $output) 32 | { 33 | $domains_file = $input->getArgument('filename'); 34 | $ip = $input->getOption('ip'); 35 | $wildcard = (bool)$input->getOption('wildcard'); 36 | $skip = (bool)$input->getOption('skip-existing'); 37 | $proxy = (bool)$input->getOption('enable-proxy'); 38 | $always_online = (bool)$input->getOption('enable-always-online'); 39 | $enable_https = (bool)$input->getOption('enable-https'); 40 | $ssl_mode = $input->getOption('ssl-mode'); 41 | $security_level = $input->getOption('security-level'); 42 | $stop_on_fail = (bool)$input->getOption('stop-on-fail'); 43 | $failed_attempts = (int)$input->getOption('failed-attempts'); 44 | 45 | $domains = $this->app->readDomains($domains_file); 46 | 47 | if (empty($domains)) { 48 | return $output->writeln("No domains found in '{$domains_file}'"); 49 | } 50 | 51 | $domains_count = count($domains); 52 | $output->writeln("Found {$domains_count} domains."); 53 | 54 | foreach ($domains as $domain) { 55 | $success = false; 56 | $attempts = 0; 57 | 58 | while ($attempts < $failed_attempts && $success === false) { 59 | try 60 | { 61 | $output->write("Adding domain '{$domain->domain}' to CF account... "); 62 | $domain_info = $this->app->addDomain($domain, $skip, $stop_on_fail); 63 | $output->writeln("success"); 64 | 65 | $output->write("Adding 'A' DNS record for domain '{$domain->domain}' ... "); 66 | $domain_ip = empty($domain->ip) ? $ip : $domain->ip; 67 | $this->app->addDnsRecord($domain_info['id'], $domain_ip, 'A', $domain->domain, $proxy, $skip); 68 | $output->writeln('success'); 69 | 70 | if ($wildcard) { 71 | $output->write("Adding wildcard record for '{$domain->domain}' ... "); 72 | $this->app->addDnsRecord($domain_info['id'], $domain_ip, 'A', '*', false, $skip); 73 | $output->writeln('success'); 74 | } 75 | 76 | if ($always_online === false) { 77 | $output->write("Disabling AlwayOnline for '{$domain->domain}' ... "); 78 | $this->app->setAlwaysOnlineEnabled($domain_info['id'], "off"); 79 | $output->writeln("success"); 80 | } 81 | 82 | if ($enable_https) { 83 | $output->write("Enabling \"Always use HTTPS\" option for '{$domain->domain}' ... "); 84 | $this->app->setAlwaysUseHttpsEnabled($domain_info['id'], "on"); 85 | $output->writeln("success"); 86 | } 87 | 88 | if (!empty($ssl_mode)) { 89 | $output->writeln("Changing SSL Mode to {$ssl_mode} ... "); 90 | $this->app->setSSLMode($domain_info['id'], $ssl_mode); 91 | $output->writeln("success"); 92 | } 93 | 94 | if (!empty($security_level)) { 95 | $output->writeln("Changing Security Level to {$security_level} ... "); 96 | $this->app->setSecurityLevel($domain_info['id'], $security_level); 97 | $output->writeln("success"); 98 | } 99 | 100 | $output->writeln("==============================="); 101 | $success = true; 102 | } catch (Exception $e) { 103 | $output->writeln("error. Message: " . $e->getMessage()); 104 | if ($stop_on_fail) { 105 | die; 106 | } 107 | 108 | ++$attempts; 109 | } 110 | } 111 | } 112 | 113 | $output->writeln("Finished"); 114 | } 115 | } -------------------------------------------------------------------------------- /src/Console/AddSubdomainsCommand.php: -------------------------------------------------------------------------------- 1 | setDescription("Add subdomains to existing domains"); 18 | $this->addArgument('filename', InputArgument::OPTIONAL, 'Path to file with domains', 'domains.txt'); 19 | $this->addOption('ip', null, InputOption::VALUE_REQUIRED, 'New IP'); 20 | $this->addOption('enable-proxy', 'p', InputOption::VALUE_NONE, 'Enable CloudFlare proxy for DNS records'); 21 | } 22 | 23 | protected function process(InputInterface $input, OutputInterface $output) 24 | { 25 | $domains_file = $input->getArgument('filename'); 26 | $ip = $input->getOption('ip'); 27 | $proxy = (bool)$input->getOption('enable-proxy'); 28 | 29 | $domains = $this->app->readDomains($domains_file); 30 | 31 | $count = count($domains); 32 | $output->writeln("Found {$count} domains"); 33 | 34 | foreach ($domains as $domain) { 35 | try 36 | { 37 | if (empty($domain->subdomains)) { 38 | $output->writeln("No subdomanis found for domain '{$domain->domain}'. Skipping..."); 39 | } 40 | $output->write("Adding subdomains: [" . implode(',', $domain->subdomains) . "] for domain '{$domain->domain}' ... "); 41 | $domain->ip = empty($domain->ip) ? $ip : $domain->ip; 42 | $this->app->addSubdomains($domain, $proxy); 43 | $output->writeln("success"); 44 | } catch (\Exception $e) { 45 | $output->writeln("error. Message: " . $e->getMessage()); 46 | } 47 | } 48 | } 49 | } -------------------------------------------------------------------------------- /src/Console/BaseCommand.php: -------------------------------------------------------------------------------- 1 | app = $app; 26 | 27 | parent::__construct(); 28 | } 29 | 30 | protected function execute(InputInterface $input, OutputInterface $output) 31 | { 32 | $output->writeln(sprintf("\t\t\tCloudFlare Domain Import %s", Version::VERSION)); 33 | $output->writeln("\t\t\t\tAuthor: NoHate"); 34 | $output->writeln("\t\t\t\tContact: @NoHate"); 35 | $output->writeln(''); 36 | 37 | $this->process($input, $output); 38 | } 39 | 40 | abstract protected function process(InputInterface $input, OutputInterface $output); 41 | } -------------------------------------------------------------------------------- /src/Console/ChangeIPCommand.php: -------------------------------------------------------------------------------- 1 | setDescription("Change IP for existing domains"); 18 | $this->addArgument('filename', InputArgument::OPTIONAL, 'Path to file with domains', 'domains.txt'); 19 | $this->addOption('all', 'a', InputOption::VALUE_NONE, 'Force update to all domains on account'); 20 | $this->addOption('ip', null, InputOption::VALUE_REQUIRED, 'New IP'); 21 | } 22 | 23 | protected function process(InputInterface $input, OutputInterface $output) 24 | { 25 | $all = (bool)$input->getOption('all'); 26 | 27 | if ($all) { 28 | $this->updateAllDomains($input, $output); 29 | } else { 30 | $this->updateDomainsFromFile($input, $output); 31 | } 32 | } 33 | 34 | private function updateAllDomains(InputInterface $input, OutputInterface $output) 35 | { 36 | // TODO: implement 37 | throw new \RuntimeException("Not Implemented"); 38 | } 39 | 40 | private function updateDomainsFromFile(InputInterface $input, OutputInterface $output) 41 | { 42 | $domains_file = $input->getArgument('filename'); 43 | $ip = $input->getOption('ip'); 44 | 45 | $domains = $this->app->readDomains($domains_file); 46 | 47 | $count = count($domains); 48 | $output->writeln("Found {$count} domains"); 49 | 50 | foreach ($domains as $domain) { 51 | $domain_ip = empty($domain->ip) ? $ip : $domain->ip; 52 | 53 | if (empty($domain_ip)) { 54 | $output->writeln("Missing IP for domain '{$domain->domain}'. Please specify default IP or add IP to '{$domains_file}' file"); 55 | continue; 56 | } 57 | 58 | $output->writeln("Changing IP to '{$domain_ip}' for '{$domain->domain}'"); 59 | 60 | try 61 | { 62 | $zone = $this->app->getCFDomainInfo($domain); 63 | $dns_records = $this->app->getDnsRecordsForDomain($zone['id']); 64 | $dns_records = array_map(function ($record) { 65 | return DnsRecord::createFromStdClass($record); 66 | }, $dns_records); 67 | 68 | $dns_records_count = count($dns_records); 69 | $output->writeln("Found {$dns_records_count} DNS records for domain '{$domain->domain}'"); 70 | 71 | foreach ($dns_records as $dns_record) { 72 | $output->write("Changing DNS record {$dns_record->name} ({$dns_record->type}) for domain '{$domain->domain}' ... "); 73 | $this->app->changeIP($dns_record, $ip); 74 | $output->writeln("success"); 75 | } 76 | } catch (\Exception $e) { 77 | $output->writeln("error. Message: " . $e->getMessage()); 78 | } 79 | } 80 | } 81 | } -------------------------------------------------------------------------------- /src/Console/RemoveDomainsCommand.php: -------------------------------------------------------------------------------- 1 | setDescription("Remove domains from CloudFlare account"); 18 | $this->addArgument('filename', InputArgument::OPTIONAL, 'Path to file with domains', 'domains.txt'); 19 | $this->addOption('all', 'a', InputOption::VALUE_NONE, 'Force update to all domains on account'); 20 | } 21 | 22 | protected function process(InputInterface $input, OutputInterface $output) 23 | { 24 | $all = (bool)$input->getOption('all'); 25 | 26 | if ($all) { 27 | $this->removeAllDomains($input, $output); 28 | } else { 29 | $this->removeDomainsFromFile($input, $output); 30 | } 31 | } 32 | 33 | private function removeAllDomains(InputInterface $input, OutputInterface $output) 34 | { 35 | // TODO: implement 36 | throw new \RuntimeException("Not Implemented"); 37 | } 38 | 39 | private function removeDomainsFromFile(InputInterface $input, OutputInterface $output) 40 | { 41 | $domains_file = $input->getArgument('filename'); 42 | 43 | $domains = $this->app->readDomains($domains_file); 44 | 45 | $count = count($domains); 46 | $output->writeln("Found {$count} domains"); 47 | 48 | foreach ($domains as $domain) { 49 | try 50 | { 51 | $output->write("Removing domain '{$domain->domain}' ... "); 52 | $this->app->removeDomain($domain); 53 | $output->writeln("success"); 54 | } catch (\Exception $e) { 55 | $output->writeln("error. Message: " . $e->getMessage()); 56 | } 57 | } 58 | } 59 | } -------------------------------------------------------------------------------- /src/Console/ShowDomainsCommand.php: -------------------------------------------------------------------------------- 1 | setDescription("Show all domains for your account"); 18 | $this->addOption('save-to', 's', InputOption::VALUE_REQUIRED, 'Filename where to save fetched domains'); 19 | } 20 | 21 | protected function process(InputInterface $input, OutputInterface $output) 22 | { 23 | $domains = $this->app->getCFDomains(); 24 | $save_to = $input->getOption('save-to'); 25 | 26 | if (empty($domains)) { 27 | return $output->writeln("No domains found for this account"); 28 | } 29 | 30 | $headers = array_keys($domains[0]); 31 | $headers = array_map('ucfirst', $headers); 32 | $domains = array_map(function ($domain) { 33 | $domain['nameservers'] = implode(', ', $domain['nameservers']); 34 | 35 | return $domain; 36 | }, $domains); 37 | 38 | $file_writer = new FileWriter; 39 | if (!empty($save_to)) { 40 | $full_path = $file_writer->makeFullPath($save_to); 41 | $file_writer->saveCSV($full_path, $domains); 42 | $output->writeln("Saved results to '{$full_path}'"); 43 | } 44 | 45 | $table = new Table($output); 46 | $table->setStyle('borderless'); 47 | $table->setHeaders($headers); 48 | $table->setRows($domains); 49 | $table->render(); 50 | } 51 | } -------------------------------------------------------------------------------- /src/Console/VersionCommand.php: -------------------------------------------------------------------------------- 1 | setDescription("Show current version"); 16 | } 17 | 18 | protected function process(InputInterface $input, OutputInterface $output) 19 | { 20 | $output->writeln(sprintf("Current Version: %s", Version::VERSION)); 21 | } 22 | } -------------------------------------------------------------------------------- /src/FileSystem/DomainsReader.php: -------------------------------------------------------------------------------- 1 | fileReader = $fileReader; 23 | } 24 | 25 | /** 26 | * Read domains from file 27 | * 28 | * @param string $filename 29 | * 30 | * @return array 31 | */ 32 | public function readDomains($filename) 33 | { 34 | $full_path = $this->fileReader->makeFullPath($filename); 35 | if (!$this->fileReader->exists($full_path)) { 36 | throw new RuntimeException("File '{$full_path}' does not exists!"); 37 | } 38 | 39 | $lines = $this->fileReader->readLines($filename); 40 | 41 | $domains = []; 42 | 43 | foreach ($lines as $line) { 44 | if (empty($line)) { 45 | continue; 46 | } 47 | $domains[] = Domain::createFromLine($line); 48 | } 49 | 50 | return $domains; 51 | } 52 | } -------------------------------------------------------------------------------- /src/FileSystem/FileReader.php: -------------------------------------------------------------------------------- 1 | dns_id = $dns_id; 45 | $this->type = $type; 46 | $this->name = $name; 47 | $this->content = $content; 48 | $this->proxied = $proxied; 49 | $this->zone_id = $zone_id; 50 | } 51 | 52 | /** 53 | * @param \stdClass $record 54 | * 55 | * @return static 56 | */ 57 | public static function createFromStdClass(\stdClass $record) 58 | { 59 | return new static($record->zone_id, $record->id, $record->type, $record->name, $record->content, $record->proxied); 60 | } 61 | } -------------------------------------------------------------------------------- /src/Models/Domain.php: -------------------------------------------------------------------------------- 1 | domain = $domain; 30 | $this->ip = $ip; 31 | $this->subdomains = $subdomains; 32 | } 33 | 34 | /** 35 | * Create instance from line 36 | * 37 | * @param string $line 38 | * 39 | * @return static 40 | */ 41 | public static function createFromLine($line) 42 | { 43 | $parts = explode('|', $line); 44 | 45 | if (count($parts) === 1) { 46 | return new static($parts[0]); 47 | } 48 | 49 | $domain = $parts[0]; 50 | $ip = $parts[1]; 51 | $subdomains = []; 52 | 53 | if (isset($parts[2])) { 54 | $subdomains = explode(',', $parts[2]); 55 | $subdomains = array_map('trim', $subdomains); 56 | } 57 | 58 | return new static($domain, $ip, $subdomains); 59 | } 60 | } -------------------------------------------------------------------------------- /src/Version.php: -------------------------------------------------------------------------------- 1 |