├── LICENSE.md ├── composer.json └── src ├── ApiProvider.php ├── ApiResource.php ├── Applications ├── Application.php ├── GitApplication.php └── WordPressApplication.php ├── Certificates ├── Certificate.php ├── CertificatesManager.php └── Commands │ ├── CertificateCommand.php │ ├── CloneCertificateCommand.php │ ├── CreateCertificateCommand.php │ ├── GetCertificateCommand.php │ ├── InstallCertificateCommand.php │ ├── ListCertificatesCommand.php │ └── ObtainLetsEncryptCertificateCommand.php ├── Commands ├── ApiCommand.php ├── BooleanResponseTrait.php ├── NotSupportingResourceClassTrait.php ├── RawBodyResponseTrait.php ├── ResourceCommand.php └── ServiceCommand.php ├── Configs ├── Commands │ ├── ConfigFileCommand.php │ ├── GetConfigFileCommand.php │ └── UpdateConfigFileCommand.php └── ConfigsManager.php ├── Contracts ├── ApplicationContract.php ├── ResourceContract.php └── ServiceContract.php ├── Daemons ├── Commands │ ├── CreateDaemonCommand.php │ ├── DaemonCommand.php │ ├── GetDaemonCommand.php │ └── ListDaemonsCommand.php ├── Daemon.php └── DaemonsManager.php ├── Deployment ├── Commands │ ├── DeployCommand.php │ ├── DisableDeploymentCommand.php │ ├── EnableDeploymentCommand.php │ ├── GetDeploymentLogCommand.php │ ├── GetDeploymentScriptCommand.php │ ├── ResetDeploymentStatusCommand.php │ └── UpdateDeploymentScriptCommand.php └── DeploymentManager.php ├── Exceptions ├── ObjectWasNotFoundException.php ├── Resources │ ├── DeleteResourceException.php │ └── UpdateResourceException.php └── Servers │ ├── PublicKeyWasNotFound.php │ └── ServerWasNotFoundException.php ├── Firewall ├── Commands │ ├── CreateFirewallRuleCommand.php │ ├── FirewallRuleCommand.php │ ├── GetFirewallRuleCommand.php │ └── ListFirewallRulesCommand.php ├── FirewallManager.php └── FirewallRule.php ├── Forge.php ├── Jobs ├── Commands │ ├── CreateJobCommand.php │ ├── GetJobCommand.php │ ├── JobCommand.php │ └── ListJobsCommand.php ├── Job.php └── JobsManager.php ├── Laravel ├── Commands │ ├── ForgeCredentials.php │ └── ForgeServers.php ├── Facades │ └── ForgeFacade.php ├── ForgeServiceProvider.php └── configs │ └── forge.php ├── Recipes ├── Commands │ ├── CreateRecipeCommand.php │ ├── GetRecipeCommand.php │ ├── ListRecipesCommand.php │ └── RecipeCommand.php ├── Recipe.php └── RecipesManager.php ├── Server.php ├── Servers ├── Factory.php └── Providers │ ├── Aws.php │ ├── Custom.php │ ├── DigitalOcean.php │ ├── Linode.php │ └── Provider.php ├── Services ├── BlackfireService.php ├── Commands │ ├── InstallServiceCommand.php │ ├── RebootServiceCommand.php │ ├── StopServiceCommand.php │ └── UninstallServiceCommand.php ├── Mysql │ ├── Commands │ │ ├── CreateMysqlDatabaseCommand.php │ │ ├── CreateMysqlUserCommand.php │ │ ├── GetMysqlDatabaseCommand.php │ │ ├── GetMysqlUserCommand.php │ │ ├── ListMysqlDatabasesCommand.php │ │ ├── ListMysqlUsersCommand.php │ │ ├── MysqlDatabaseCommand.php │ │ └── MysqlUserCommand.php │ ├── MysqlDatabase.php │ ├── MysqlUser.php │ └── MysqlUsers.php ├── MysqlService.php ├── NginxService.php ├── PapertrailService.php ├── PostgresService.php ├── Service.php └── ServicesManager.php ├── Sites ├── Commands │ ├── CreateSiteCommand.php │ ├── GetSiteCommand.php │ ├── ListSitesCommand.php │ └── SiteCommand.php ├── Site.php └── SitesManager.php ├── SshKeys ├── Commands │ ├── CreateSshKeyCommand.php │ ├── GetSshKeyCommand.php │ ├── ListSshKeysCommand.php │ └── SshKeyCommand.php ├── SshKey.php └── SshKeysManager.php ├── Traits ├── AbstractCollection.php ├── ArrayAccessTrait.php ├── LazyArrayAccess.php └── LazyIterator.php ├── Users ├── Commands │ └── GetAuthenticatedUserCommand.php ├── User.php └── UsersService.php └── Workers ├── Commands ├── CreateWorkerCommand.php ├── GetWorkerCommand.php ├── ListWorkersCommand.php └── WorkerCommand.php ├── Worker.php └── WorkersManager.php /LICENSE.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017-2019 tzurbaev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tzurbaev/laravel-forge-api", 3 | "description": "Laravel Forge API SDK", 4 | "type": "library", 5 | "license": "MIT", 6 | "authors": [ 7 | { 8 | "name": "tzurbaev", 9 | "email": "zurbaev@gmail.com" 10 | } 11 | ], 12 | "require": { 13 | "php": ">=7.2", 14 | "guzzlehttp/guzzle": "^6.3|^7.0" 15 | }, 16 | "require-dev": { 17 | "phpunit/phpunit": "^8.2", 18 | "mockery/mockery": "^1.2" 19 | }, 20 | "autoload": { 21 | "psr-4": { 22 | "Laravel\\Forge\\": "src/" 23 | } 24 | }, 25 | "autoload-dev": { 26 | "psr-4": { 27 | "Laravel\\Tests\\Forge\\": "tests/" 28 | } 29 | }, 30 | "scripts": { 31 | "test": "phpunit" 32 | }, 33 | "extra": { 34 | "laravel": { 35 | "providers": ["Laravel\\Forge\\Laravel\\ForgeServiceProvider"], 36 | "aliases": { 37 | "Forge": "Laravel\\Forge\\Laravel\\Facades\\ForgeFacade" 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/ApiProvider.php: -------------------------------------------------------------------------------- 1 | token = $token; 46 | } 47 | 48 | /** 49 | * HTTP client. 50 | * 51 | * @return \GuzzleHttp\ClientInterface 52 | */ 53 | public function getClient(): ClientInterface 54 | { 55 | if (!is_null($this->rateLimiter)) { 56 | call_user_func($this->rateLimiter); 57 | } 58 | 59 | if (!is_null($this->client)) { 60 | return $this->client; 61 | } 62 | 63 | return $this->client = $this->createClient(); 64 | } 65 | 66 | /** 67 | * API token. 68 | * 69 | * @return string 70 | */ 71 | public function getToken(): string 72 | { 73 | return $this->token; 74 | } 75 | 76 | /** 77 | * Create new HTTP client. 78 | * 79 | * @return \GuzzleHttp\ClientInterface 80 | */ 81 | public function createClient(): ClientInterface 82 | { 83 | $client = new Client([ 84 | 'base_uri' => static::BASE_URI, 85 | 'headers' => [ 86 | 'Authorization' => 'Bearer '.$this->getToken(), 87 | 'Accept' => 'application/json', 88 | 'Content-Type' => 'application/json', 89 | ], 90 | ]); 91 | 92 | return $client; 93 | } 94 | 95 | /** 96 | * Sets an optional rate limiting function. 97 | * 98 | * @param callable $rateLimiter 99 | */ 100 | public function setRateLimiter(callable $rateLimiter) 101 | { 102 | $this->rateLimiter = $rateLimiter; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/ApiResource.php: -------------------------------------------------------------------------------- 1 | api = $api; 44 | $this->data = $data; 45 | $this->owner = $owner; 46 | } 47 | 48 | /** 49 | * Resource type. 50 | * 51 | * @return string 52 | */ 53 | abstract public static function resourceType(); 54 | 55 | /** 56 | * Resource path (relative to owner or API root). 57 | * 58 | * @return string 59 | */ 60 | abstract public function resourcePath(); 61 | 62 | /** 63 | * Create new Resource instance from HTTP response. 64 | * 65 | * @param \Psr\Http\Message\ResponseInterface $response 66 | * @param \Laravel\Forge\ApiProvider $api 67 | * @param \Laravel\Forge\Contracts\ResourceContract $owner = null 68 | * 69 | * @return static 70 | */ 71 | public static function createFromResponse(ResponseInterface $response, ApiProvider $api, ResourceContract $owner = null) 72 | { 73 | $json = json_decode((string) $response->getBody(), true); 74 | $resourceType = static::resourceType(); 75 | 76 | if (empty($json[$resourceType])) { 77 | static::throwNotFoundException(); 78 | } 79 | 80 | return new static($api, $json[$resourceType], $owner); 81 | } 82 | 83 | /** 84 | * Throw HTTP Not Found exception. 85 | * 86 | * @throws \InvalidArgumentException 87 | */ 88 | protected static function throwNotFoundException() 89 | { 90 | throw new InvalidArgumentException('Given response is not a '.static::resourceType().' response.'); 91 | } 92 | 93 | /** 94 | * Determines if current resource has an owner. 95 | * 96 | * @return bool 97 | */ 98 | public function hasResourceOwner(): bool 99 | { 100 | return !is_null($this->owner); 101 | } 102 | 103 | /** 104 | * Get current resource owner. 105 | * 106 | * @return \Laravel\Forge\Contracts\ResourceContract|null 107 | */ 108 | public function resourceOwner() 109 | { 110 | return $this->owner; 111 | } 112 | 113 | /** 114 | * Get API provider. 115 | * 116 | * @return \Laravel\Forge\ApiProvider 117 | */ 118 | public function getApi(): ApiProvider 119 | { 120 | return $this->api; 121 | } 122 | 123 | /** 124 | * Get underlying API provider's HTTP client. 125 | * 126 | * @return \GuzzleHttp\ClientInterface 127 | */ 128 | public function getHttpClient(): ClientInterface 129 | { 130 | return $this->api->getClient(); 131 | } 132 | 133 | /** 134 | * Get resource data. 135 | * 136 | * @param string|int $key 137 | * @param mixed $default = null 138 | * 139 | * @return mixed|null 140 | */ 141 | public function getData($key, $default = null) 142 | { 143 | return $this->data[$key] ?? $default; 144 | } 145 | 146 | /** 147 | * Get full resource data. 148 | * 149 | * @return array 150 | */ 151 | public function getFullData() 152 | { 153 | return $this->data; 154 | } 155 | 156 | /** 157 | * Resource API URL. 158 | * 159 | * @param string $path = '' 160 | * @param bool $withPropagation = true 161 | * 162 | * @return string 163 | */ 164 | public function apiUrl(string $path = '', bool $withPropagation = true): string 165 | { 166 | $path = ($path ? '/'.ltrim($path, '/') : ''); 167 | $resourcePath = rtrim($this->resourcePath(), '/').'/'.$this->id().$path; 168 | 169 | if (!$this->hasResourceOwner() || !$withPropagation) { 170 | return $resourcePath; 171 | } 172 | 173 | return $this->resourceOwner()->apiUrl($resourcePath); 174 | } 175 | 176 | /** 177 | * Resource ID. 178 | * 179 | * @return int 180 | */ 181 | public function id(): int 182 | { 183 | return intval($this->getData('id', 0)); 184 | } 185 | 186 | /** 187 | * Resource name. 188 | * 189 | * @return string|null 190 | */ 191 | public function name() 192 | { 193 | return $this->getData('name'); 194 | } 195 | 196 | /** 197 | * Resource status. 198 | * 199 | * @return string|null 200 | */ 201 | public function status() 202 | { 203 | return $this->getData('status'); 204 | } 205 | 206 | /** 207 | * Get resource creation date. 208 | * 209 | * @return string|null 210 | */ 211 | public function createdAt() 212 | { 213 | return $this->getData('created_at'); 214 | } 215 | 216 | /** 217 | * Update resource data. 218 | * 219 | * @param array $payload 220 | * 221 | * @throws UpdateResourceException 222 | * 223 | * @return bool 224 | */ 225 | public function update(array $payload): bool 226 | { 227 | $resourceType = static::resourceType(); 228 | 229 | try { 230 | $response = $this->getHttpClient()->request('PUT', $this->apiUrl(), [ 231 | 'json' => $payload, 232 | ]); 233 | } catch (RequestException $e) { 234 | $this->throwResourceException($e->getResponse(), 'update', UpdateResourceException::class); 235 | } 236 | 237 | $json = json_decode((string) $response->getBody(), true); 238 | 239 | if (empty($json[$resourceType])) { 240 | return false; 241 | } 242 | 243 | $this->data = $json[$resourceType]; 244 | 245 | return true; 246 | } 247 | 248 | /** 249 | * Delete current resource. 250 | * 251 | * @throws \Laravel\Forge\Exceptions\Resources\DeleteResourceException 252 | * 253 | * @return bool 254 | */ 255 | public function delete() 256 | { 257 | try { 258 | $this->getHttpClient()->request('DELETE', $this->apiUrl()); 259 | } catch (RequestException $e) { 260 | $this->throwResourceException($e->getResponse(), 'delete', DeleteResourceException::class); 261 | } 262 | 263 | return true; 264 | } 265 | 266 | /** 267 | * Throw resource exception. 268 | * 269 | * @param \Psr\Http\Message\ResponseInterface $response 270 | * @param string $action 271 | * @param string $exceptionClass 272 | * 273 | * @throws \Exception 274 | */ 275 | protected function throwResourceException(ResponseInterface $response, string $action, string $exceptionClass) 276 | { 277 | $message = 'Unable to '.$action.' resource (type: '.static::resourceType().', ID: '.$this->id().').'; 278 | 279 | if (is_null($response)) { 280 | throw new InvalidArgumentException($message); 281 | } 282 | 283 | $message .= ' Server response: "'.((string) $response->getBody()).'".'; 284 | 285 | throw new $exceptionClass($message, $response->getStatusCode()); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/Applications/Application.php: -------------------------------------------------------------------------------- 1 | payload; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/Applications/GitApplication.php: -------------------------------------------------------------------------------- 1 | setRepositorySource('github', $repository); 29 | } 30 | 31 | /** 32 | * Indicates that application will be installed from Bitbucket repository. 33 | * 34 | * @param string $repository 35 | * 36 | * @return static 37 | */ 38 | public function fromBitbucket(string $repository) 39 | { 40 | return $this->setRepositorySource('bitbucket', $repository); 41 | } 42 | 43 | /** 44 | * Indicates that application will be installed from Gitlab repository. 45 | * 46 | * @param string $repository 47 | * 48 | * @return static 49 | */ 50 | public function fromGitlab(string $repository) 51 | { 52 | return $this->setRepositorySource('gitlab', $repository); 53 | } 54 | 55 | /** 56 | * Indicates that application will be installed from custom git repository. 57 | * 58 | * @param string $repository 59 | * 60 | * @return static 61 | */ 62 | public function fromGit(string $url) 63 | { 64 | return $this->setRepositorySource('custom', $url); 65 | } 66 | 67 | /** 68 | * Indicates which branch from the repository should be used. 69 | * 70 | * @param string $branch 71 | * 72 | * @return static 73 | */ 74 | public function usingBranch(string $branch) 75 | { 76 | return $this->setRepositoryBranch($branch); 77 | } 78 | 79 | /** 80 | * Indicates that application will use composer. 81 | * 82 | * @return static 83 | */ 84 | public function withComposer() 85 | { 86 | $this->payload['composer'] = true; 87 | 88 | return $this; 89 | } 90 | 91 | /** 92 | * Set git provider and repository name. 93 | * 94 | * @param string $provider 95 | * @param string $repository 96 | * 97 | * @return static 98 | */ 99 | protected function setRepositorySource(string $provider, string $repository) 100 | { 101 | $this->payload = [ 102 | 'provider' => $provider, 103 | 'repository' => $repository, 104 | ]; 105 | 106 | return $this; 107 | } 108 | 109 | /** 110 | * Set the branch name. 111 | * 112 | * @param string $branch 113 | * 114 | * @return static 115 | */ 116 | protected function setRepositoryBranch(string $branch) 117 | { 118 | $this->payload['branch'] = $branch; 119 | 120 | return $this; 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/Applications/WordPressApplication.php: -------------------------------------------------------------------------------- 1 | payload = [ 30 | 'database' => $database, 31 | 'user' => $user, 32 | ]; 33 | 34 | return $this; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Certificates/Certificate.php: -------------------------------------------------------------------------------- 1 | domain(); 37 | } 38 | 39 | /** 40 | * Certificate domain. 41 | * 42 | * @return string|null 43 | */ 44 | public function domain() 45 | { 46 | return $this->getData('domain'); 47 | } 48 | 49 | /** 50 | * Certificate type. 51 | * 52 | * @return string|null 53 | */ 54 | public function type() 55 | { 56 | return $this->getData('type'); 57 | } 58 | 59 | /** 60 | * Request status. 61 | * 62 | * @return string|null 63 | */ 64 | public function requestStatus() 65 | { 66 | return $this->getData('request_status'); 67 | } 68 | 69 | /** 70 | * Determines if certificate is active. 71 | * 72 | * @return bool 73 | */ 74 | public function active(): bool 75 | { 76 | return intval($this->getData('active')) === 1; 77 | } 78 | 79 | /** 80 | * Determines if current certificate was installed from existing certificate. 81 | * 82 | * @return bool 83 | */ 84 | public function existing(): bool 85 | { 86 | return intval($this->getData('existing')) === 1; 87 | } 88 | 89 | /** 90 | * Determines if current certificate is Let's Encrypt certificate. 91 | * 92 | * @return bool 93 | */ 94 | public function letsencrypt(): bool 95 | { 96 | return $this->type() === 'letsencrypt'; 97 | } 98 | 99 | /** 100 | * Get Certificate Signing Request value. 101 | * 102 | * @return string 103 | */ 104 | public function csr() 105 | { 106 | $response = $this->getHttpClient()->request('GET', $this->apiUrl('/csr')); 107 | 108 | return (string) $response->getBody(); 109 | } 110 | 111 | /** 112 | * Install certificate. 113 | * 114 | * @param string $content 115 | * @param bool $addIntermediates = false 116 | * 117 | * @return bool 118 | */ 119 | public function install(string $content, bool $addIntermediates = false): bool 120 | { 121 | $this->getHttpClient()->request('POST', $this->apiUrl('/install'), [ 122 | 'json' => [ 123 | 'certificate' => $content, 124 | 'add_intermediates' => $addIntermediates, 125 | ], 126 | ]); 127 | 128 | return true; 129 | } 130 | 131 | /** 132 | * Activate certificate. 133 | * 134 | * @return bool 135 | */ 136 | public function activate(): bool 137 | { 138 | $this->getHttpClient()->request('POST', $this->apiUrl('/activate')); 139 | 140 | return true; 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /src/Certificates/CertificatesManager.php: -------------------------------------------------------------------------------- 1 | operationType('new') 25 | ->identifiedAs($domain); 26 | } 27 | 28 | /** 29 | * Initialize new install certificate command. 30 | * 31 | * @param string $privateKey 32 | * @param string $certificate 33 | * 34 | * @return \Laravel\Forge\Certificates\Commands\InstallCertificateCommand 35 | */ 36 | public function install(string $privateKey, string $certificate) 37 | { 38 | return (new InstallCertificateCommand()) 39 | ->operationType('existing') 40 | ->usingPivateKey($privateKey) 41 | ->usingCertificate($certificate); 42 | } 43 | 44 | /** 45 | * Initialize new clone certificate command. 46 | * 47 | * @param int $certificateId 48 | * 49 | * @return \Laravel\Forge\Certificates\Commands\CloneCertificateCommand 50 | */ 51 | public function clone(int $certificateId) 52 | { 53 | return (new CloneCertificateCommand()) 54 | ->operationType('clone') 55 | ->usingCertificateId($certificateId); 56 | } 57 | 58 | /** 59 | * Initialize new obtain Let's Encrypt certificate command. 60 | * 61 | * @param string|array $domains 62 | * 63 | * @return \Laravel\Forge\Certificates\Commands\ObtainLetsEncryptCertificateCommand 64 | */ 65 | public function obtain($domains) 66 | { 67 | return (new ObtainLetsEncryptCertificateCommand()) 68 | ->usingDomains($domains); 69 | } 70 | 71 | /** 72 | * Initialize new list certificates command. 73 | * 74 | * @return \Laravel\Forge\Certificates\Commands\ListCertificatesCommand 75 | */ 76 | public function list() 77 | { 78 | return new ListCertificatesCommand(); 79 | } 80 | 81 | /** 82 | * Initialize new get certificate command. 83 | * 84 | * @param int $certificateId 85 | * 86 | * @return \Laravel\Forge\Certificates\Commands\GetCertificateCommand 87 | */ 88 | public function get(int $certificateId) 89 | { 90 | return (new GetCertificateCommand())->setResourceId($certificateId); 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /src/Certificates/Commands/CertificateCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('type', $type); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/Certificates/Commands/CloneCertificateCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('certificate_id', $certificateId); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/Certificates/Commands/CreateCertificateCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('domain', $domain); 17 | } 18 | 19 | /** 20 | * Set certificate organization name. 21 | * 22 | * @param string $organization 23 | */ 24 | public function ownedBy(string $organization) 25 | { 26 | return $this->attachPayload('organization', $organization); 27 | } 28 | 29 | /** 30 | * Set organization location. 31 | * 32 | * @param string $country 33 | * @param string $state 34 | * @param string $city 35 | * 36 | * @return static 37 | */ 38 | public function locatedAt(string $country, string $state, string $city) 39 | { 40 | return $this 41 | ->attachPayload('country', $country) 42 | ->attachPayload('state', $state) 43 | ->attachPayload('city', $city); 44 | } 45 | 46 | /** 47 | * Set certificate department. 48 | * 49 | * @param string $department 50 | * 51 | * @return static 52 | */ 53 | public function assignedTo(string $department) 54 | { 55 | return $this->attachPayload('department', $department); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/Certificates/Commands/GetCertificateCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('key', $privateKey); 17 | } 18 | 19 | /** 20 | * Set certificate content. 21 | * 22 | * @param string $certificate 23 | * 24 | * @return static 25 | */ 26 | public function usingCertificate(string $certificate) 27 | { 28 | return $this->attachPayload('certificate', $certificate); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Certificates/Commands/ListCertificatesCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('domains', $domains); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Commands/ApiCommand.php: -------------------------------------------------------------------------------- 1 | apiUrl(); 64 | } 65 | 66 | /** 67 | * HTTP request options. 68 | * 69 | * @return array 70 | */ 71 | public function requestOptions() 72 | { 73 | return [ 74 | 'json' => $this->payload, 75 | ]; 76 | } 77 | 78 | /** 79 | * Set command payload. 80 | * 81 | * @param array $payload 82 | * 83 | * @return static 84 | */ 85 | public function withPayload(array $payload) 86 | { 87 | $this->payload = $payload; 88 | 89 | return $this; 90 | } 91 | 92 | /** 93 | * Set payload data. 94 | * 95 | * @param string|int $key 96 | * @param mixed $value 97 | * 98 | * @return static 99 | */ 100 | public function attachPayload($key, $value) 101 | { 102 | if (is_null($this->payload)) { 103 | $this->payload = []; 104 | } 105 | 106 | $this->payload[$key] = $value; 107 | 108 | return $this; 109 | } 110 | 111 | /** 112 | * Return payload data. 113 | * 114 | * @param string|int $key 115 | * @param mixed $default = null 116 | * 117 | * @return mixed|null 118 | */ 119 | public function getPayloadData($key, $default = null) 120 | { 121 | if (is_null($this->payload)) { 122 | return; 123 | } 124 | 125 | return $this->payload[$key] ?? $default; 126 | } 127 | 128 | /** 129 | * Determines if payload has requried keys. 130 | * 131 | * @param string|int|array $keys 132 | * 133 | * @return bool 134 | */ 135 | public function hasPayloadData($keys): bool 136 | { 137 | if (is_null($this->payload)) { 138 | return false; 139 | } 140 | 141 | if (!is_array($keys)) { 142 | $keys = [$keys]; 143 | } 144 | 145 | foreach ($keys as $key) { 146 | if (!isset($this->payload[$key])) { 147 | return false; 148 | } 149 | } 150 | 151 | return true; 152 | } 153 | 154 | /** 155 | * Execute command on single or multiple resources. 156 | * 157 | * @param array|\Laravel\Forge\Contracts\ResourceContract $resource 158 | * 159 | * @throws \InvalidArgumentException 160 | * 161 | * @return bool|array 162 | */ 163 | public function on($resource) 164 | { 165 | if (!$this->runnable()) { 166 | throw new InvalidArgumentException('Command execution is restricted.'); 167 | } 168 | 169 | if (is_array($resource)) { 170 | return $this->executeOnMulitpleResources($resource); 171 | } 172 | 173 | return $this->executeOn($resource); 174 | } 175 | 176 | /** 177 | * Alias for "on" command. 178 | * 179 | * @param array|\Laravel\Forge\Contracts\ResourceContract $resource 180 | * 181 | * @throws \InvalidArgumentException 182 | * 183 | * @see \Laravel\Forge\Services\Commands\AbstractServiceCommand::on 184 | * 185 | * @return bool|array 186 | */ 187 | public function from($resource) 188 | { 189 | return $this->on($resource); 190 | } 191 | 192 | /** 193 | * Execute current command on given resource. 194 | * 195 | * @param \Laravel\Forge\Contracts\ResourceContract $resource 196 | * 197 | * @return bool|mixed 198 | */ 199 | protected function executeOn(ResourceContract $resource) 200 | { 201 | $response = $this->execute($resource); 202 | 203 | if (method_exists($this, 'handleResponse')) { 204 | return $this->handleResponse($response, $resource); 205 | } 206 | 207 | return true; 208 | } 209 | 210 | /** 211 | * Execute current command on multiple resources. 212 | * 213 | * @param array $resources 214 | * 215 | * @return array 216 | */ 217 | protected function executeOnMulitpleResources(array $resources): array 218 | { 219 | $results = []; 220 | 221 | foreach ($resources as $resource) { 222 | $results[$resource->name()] = $this->executeOn($resource); 223 | } 224 | 225 | return $results; 226 | } 227 | 228 | /** 229 | * Execute current command. 230 | * 231 | * @param \Laravel\Forge\Contracts\ResourceContract $resource 232 | */ 233 | protected function execute(ResourceContract $resource) 234 | { 235 | return $resource->getHttpClient()->request( 236 | $this->requestMethod(), 237 | $this->requestUrl($resource), 238 | $this->requestOptions() 239 | ); 240 | } 241 | } 242 | -------------------------------------------------------------------------------- /src/Commands/BooleanResponseTrait.php: -------------------------------------------------------------------------------- 1 | getBody(); 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /src/Commands/ResourceCommand.php: -------------------------------------------------------------------------------- 1 | resourcePath(); 48 | } 49 | 50 | /** 51 | * HTTP request method. 52 | * 53 | * @return string 54 | */ 55 | public function requestMethod() 56 | { 57 | if ($this->isListCommand()) { 58 | return 'GET'; 59 | } 60 | 61 | return is_null($this->getResourceId()) ? 'POST' : 'GET'; 62 | } 63 | 64 | /** 65 | * HTTP request URL. 66 | * 67 | * @param \Laravel\Forge\Contracts\ResourceContract $resource 68 | * 69 | * @return string 70 | */ 71 | public function requestUrl(ResourceContract $resource) 72 | { 73 | $resourcePath = $this->resourcePath(); 74 | 75 | if (!is_null($this->getResourceId())) { 76 | $resourcePath .= '/'.$this->getResourceId(); 77 | } 78 | 79 | return $resource->apiUrl($resourcePath); 80 | } 81 | 82 | /** 83 | * Set resource ID. 84 | * 85 | * @param int|string $resourceId 86 | * 87 | * @return static 88 | */ 89 | public function setResourceId($resourceId) 90 | { 91 | $this->resourceId = $resourceId; 92 | 93 | return $this; 94 | } 95 | 96 | /** 97 | * Get resource ID. 98 | * 99 | * @return int|string|null 100 | */ 101 | public function getResourceId() 102 | { 103 | return $this->resourceId; 104 | } 105 | 106 | /** 107 | * Handle command response. 108 | * 109 | * @param \Psr\Http\Message\ResponseInterface $response 110 | * @param \Laravel\Forge\Contracts\ResourceContract $owner 111 | * 112 | * @return \Laravel\Forge\Contracts\ResourceContract|array|bool|string 113 | */ 114 | public function handleResponse(ResponseInterface $response, ResourceContract $owner) 115 | { 116 | if ($this->isListCommand()) { 117 | return $this->handleListCommandResponse($response, $owner); 118 | } 119 | 120 | $className = $this->resourceClass(); 121 | 122 | return $className::createFromResponse($response, $owner->getApi(), $owner); 123 | } 124 | 125 | /** 126 | * List response handler. 127 | * 128 | * @param \Psr\Http\Message\ResponseInterface $response 129 | * @param \Laravel\Forge\Contracts\ResourceContract $owner 130 | * 131 | * @throws \InvalidArgumentException 132 | * 133 | * @return array 134 | */ 135 | public function handleListCommandResponse(ResponseInterface $response, ResourceContract $owner) 136 | { 137 | $itemsKey = $this->listResponseItemsKey(); 138 | 139 | $json = json_decode((string) $response->getBody(), true); 140 | 141 | if (!isset($json[$itemsKey])) { 142 | throw new InvalidArgumentException('Given response is not a '.$this->resourcePath().' response.'); 143 | } 144 | 145 | $items = []; 146 | $className = $this->resourceClass(); 147 | 148 | foreach ($json[$itemsKey] as $item) { 149 | $items[] = new $className($owner->getApi(), $item, $owner); 150 | } 151 | 152 | return $items; 153 | } 154 | } 155 | -------------------------------------------------------------------------------- /src/Commands/ServiceCommand.php: -------------------------------------------------------------------------------- 1 | service = $service; 25 | } 26 | 27 | /** 28 | * Associated service. 29 | * 30 | * @return \Laravel\Forge\Contracts\ServiceContract 31 | */ 32 | public function getService(): ServiceContract 33 | { 34 | return $this->service; 35 | } 36 | 37 | /** 38 | * HTTP request URL. 39 | * 40 | * @param \Laravel\Forge\Contracts\ResourceContract $resource 41 | * 42 | * @return string 43 | */ 44 | public function requestUrl(ResourceContract $resource) 45 | { 46 | return $resource->apiUrl('/'.$this->getService()->name().'/'.$this->command()); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Configs/Commands/ConfigFileCommand.php: -------------------------------------------------------------------------------- 1 | configFile = $file; 26 | 27 | return $this; 28 | } 29 | 30 | /** 31 | * Resource path. 32 | * 33 | * @return string 34 | */ 35 | public function resourcePath() 36 | { 37 | return $this->configFile; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Configs/Commands/GetConfigFileCommand.php: -------------------------------------------------------------------------------- 1 | usingFile($name); 20 | } 21 | 22 | /** 23 | * Initialize new update configuration command. 24 | * 25 | * @param string $name 26 | * @param string $content 27 | * 28 | * @return \Laravel\Forge\Configs\Commands\UpdateConfigFileCommand 29 | */ 30 | public function update(string $name, string $content) 31 | { 32 | return (new UpdateConfigFileCommand()) 33 | ->usingFile($name) 34 | ->withPayload(['content' => $content]); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/Contracts/ApplicationContract.php: -------------------------------------------------------------------------------- 1 | attachPayload('command', $command); 17 | } 18 | 19 | /** 20 | * Set the user of command. 21 | * 22 | * @param string $user 23 | * 24 | * @return static 25 | */ 26 | public function runningAs(string $user) 27 | { 28 | return $this->attachPayload('user', $user); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Daemons/Commands/DaemonCommand.php: -------------------------------------------------------------------------------- 1 | getData('command'); 37 | } 38 | 39 | /** 40 | * Daemon user. 41 | * 42 | * @return string|null 43 | */ 44 | public function user() 45 | { 46 | return $this->getData('user'); 47 | } 48 | 49 | /** 50 | * Restart daemon. 51 | * 52 | * @return bool 53 | */ 54 | public function restart() 55 | { 56 | $this->getHttpClient()->request('POST', $this->apiUrl('/restart')); 57 | 58 | return true; 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/Daemons/DaemonsManager.php: -------------------------------------------------------------------------------- 1 | start($command); 21 | } 22 | 23 | /** 24 | * Initialize new list daemons command. 25 | * 26 | * @return \Laravel\Forge\Daemons\Commands\ListDaemonsCommand 27 | */ 28 | public function list() 29 | { 30 | return new ListDaemonsCommand(); 31 | } 32 | 33 | /** 34 | * Initialize new get daemon command. 35 | * 36 | * @param int $daemonId 37 | * 38 | * @return \Laravel\Forge\Daemons\Commands\GetDaemonCommand 39 | */ 40 | public function get(int $daemonId) 41 | { 42 | return (new GetDaemonCommand())->setResourceId($daemonId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Deployment/Commands/DeployCommand.php: -------------------------------------------------------------------------------- 1 | withPayload(['content' => $script]); 55 | } 56 | 57 | /** 58 | * Perform deployment of given site. 59 | * 60 | * @return \Laravel\Forge\Deployment\Commands\DeployCommand 61 | */ 62 | public function deploy() 63 | { 64 | return new DeployCommand(); 65 | } 66 | 67 | /** 68 | * Reset deployment status for given site. 69 | * 70 | * @return \Laravel\Forge\Deployment\Commands\ResetDeploymentStatusCommand 71 | */ 72 | public function reset() 73 | { 74 | return new ResetDeploymentStatusCommand(); 75 | } 76 | 77 | /** 78 | * Get latest deployment log from given site. 79 | * 80 | * @return \Laravel\Forge\Deployment\Commands\GetDeploymentLogCommand 81 | */ 82 | public function log() 83 | { 84 | return new GetDeploymentLogCommand(); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /src/Exceptions/ObjectWasNotFoundException.php: -------------------------------------------------------------------------------- 1 | attachPayload('name', $name); 17 | } 18 | 19 | /** 20 | * Set rule port number. 21 | * 22 | * @param int $port 23 | * 24 | * @return static 25 | */ 26 | public function usingPort(int $port) 27 | { 28 | return $this->attachPayload('port', $port); 29 | } 30 | 31 | /** 32 | * Set rule ip address. 33 | * 34 | * @param string $ipAddress 35 | * 36 | * @return static 37 | */ 38 | public function usingIp(string $ipAddress) 39 | { 40 | return $this->attachPayload('ip_address', $ipAddress); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Firewall/Commands/FirewallRuleCommand.php: -------------------------------------------------------------------------------- 1 | identifiedAs($name); 21 | } 22 | 23 | /** 24 | * Initialize new list firewall rules command. 25 | * 26 | * @return \Laravel\Forge\Firewall\Commands\ListFirewallRulesCommand 27 | */ 28 | public function list() 29 | { 30 | return new ListFirewallRulesCommand(); 31 | } 32 | 33 | /** 34 | * Initialize new get firewall rule command. 35 | * 36 | * @param int $daemonId 37 | * 38 | * @return \Laravel\Forge\Firewall\Commands\GetFirewallRuleCommand 39 | */ 40 | public function get(int $daemonId) 41 | { 42 | return (new GetFirewallRuleCommand())->setResourceId($daemonId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Firewall/FirewallRule.php: -------------------------------------------------------------------------------- 1 | getData('port', 0)); 37 | } 38 | 39 | /** 40 | * Rule IP address. 41 | * 42 | * @return string|null 43 | */ 44 | public function ipAddress() 45 | { 46 | return $this->getData('ip_address'); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/Forge.php: -------------------------------------------------------------------------------- 1 | name] map. 29 | * 30 | * @var array 31 | */ 32 | protected $serversMap = []; 33 | 34 | /** 35 | * Single servers cache. 36 | * 37 | * @var array 38 | */ 39 | protected $serversCache = []; 40 | 41 | /** 42 | * Create new Servers manager instance. 43 | * 44 | * @param \Laravel\Forge\ApiProvider $api 45 | */ 46 | public function __construct(ApiProvider $api) 47 | { 48 | $this->api = $api; 49 | } 50 | 51 | /** 52 | * Get API provider. 53 | * 54 | * @return \Laravel\Forge\ApiProvider 55 | */ 56 | public function getApi(): ApiProvider 57 | { 58 | return $this->api; 59 | } 60 | 61 | /** 62 | * Get underlying API provider's HTTP client. 63 | * 64 | * @return \GuzzleHttp\ClientInterface 65 | */ 66 | public function getHttpClient(): ClientInterface 67 | { 68 | return $this->api->getClient(); 69 | } 70 | 71 | /** 72 | * Resource API URL. 73 | * 74 | * @param string $path = '' 75 | * @param bool $withPropagation = true 76 | * 77 | * @return string 78 | */ 79 | public function apiUrl(string $path = '', bool $withPropagation = true): string 80 | { 81 | return $path; 82 | } 83 | 84 | /** 85 | * Resource name. 86 | * 87 | * @return string 88 | */ 89 | public function name() 90 | { 91 | return 'forge'; 92 | } 93 | 94 | /** 95 | * {@inheritdoc} 96 | */ 97 | public function lazyLoad() 98 | { 99 | $response = $this->api->getClient()->request('GET', 'servers'); 100 | $data = json_decode((string) $response->getBody(), true); 101 | 102 | $this->items = []; 103 | $this->serversMap = []; 104 | 105 | if (empty($data['servers'])) { 106 | return $this->items; 107 | } 108 | 109 | foreach ($data['servers'] as $server) { 110 | $this->items[$server['name']] = new Server($this->api, $server); 111 | $this->serversMap[$server['id']] = $server['name']; 112 | } 113 | 114 | return $this->items; 115 | } 116 | 117 | /** 118 | * Generate items keys. 119 | */ 120 | public function generateKeys() 121 | { 122 | $this->keys = array_keys($this->items); 123 | } 124 | 125 | /** 126 | * Initialize servers factory. 127 | * 128 | * @return \Laravel\Forge\Servers\Factory 129 | */ 130 | public function create() 131 | { 132 | return new Factory($this->api); 133 | } 134 | 135 | /** 136 | * Returns single server. 137 | * 138 | * @param int $serverId 139 | * @param bool $reload (optional) indicates whether the server should be reloaded 140 | * 141 | * @return \Laravel\Forge\Server 142 | */ 143 | public function get(int $serverId, bool $reload = false) 144 | { 145 | if ($reload === true) { 146 | return $this->loadSingleServer($serverId); 147 | } 148 | 149 | if ($this->lazyLoadInitiated() && isset($this->serversMap[$serverId])) { 150 | return $this->items[$this->serversMap[$serverId]]; 151 | } elseif (isset($this->serversCache[$serverId])) { 152 | return $this->serversCache[$serverId]; 153 | } 154 | 155 | return $this->loadSingleServer($serverId); 156 | } 157 | 158 | /** 159 | * Get server provider credentials. 160 | * 161 | * @return array 162 | */ 163 | public function credentials(): array 164 | { 165 | $response = $this->api->getClient()->request('GET', 'credentials'); 166 | $json = json_decode((string) $response->getBody(), true); 167 | 168 | if (empty($json['credentials'])) { 169 | return []; 170 | } 171 | 172 | return $json['credentials']; 173 | } 174 | 175 | /** 176 | * Get first credential for given provider. 177 | * 178 | * @param string $provider 179 | * 180 | * @return int|null 181 | */ 182 | public function credentialFor(string $provider) 183 | { 184 | $credentials = $this->credentials(); 185 | 186 | if (!count($credentials)) { 187 | return; 188 | } 189 | 190 | foreach ($credentials as $credential) { 191 | if ($credential['type'] === $provider) { 192 | return intval($credential['id']); 193 | } 194 | } 195 | } 196 | 197 | /** 198 | * Load single server from API and save it to memory cache. 199 | * 200 | * @param int $serverId 201 | * 202 | * @throws \Laravel\Forge\Exceptions\Servers\ServerWasNotFoundException 203 | * 204 | * @return \Laravel\Forge\Server 205 | */ 206 | protected function loadSingleServer(int $serverId) 207 | { 208 | try { 209 | $response = $this->api->getClient()->request('GET', 'servers/'.$serverId); 210 | } catch (RequestException $e) { 211 | if ($e->getResponse()->getStatusCode() === 404) { 212 | throw new ServerWasNotFoundException('Server #'.$serverId.' was not found.', 404); 213 | } 214 | 215 | throw $e; 216 | } 217 | 218 | return $this->serversCache[$serverId] = Server::createFromResponse($response, $this->api); 219 | } 220 | 221 | /** 222 | * Sets an optional rate limiting function on the api provider. 223 | * 224 | * @param callable $rateLimiter 225 | */ 226 | public function setRateLimiter(callable $rateLimiter) 227 | { 228 | $this->api->setRateLimiter($rateLimiter); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /src/Jobs/Commands/CreateJobCommand.php: -------------------------------------------------------------------------------- 1 | getPayloadData('frequency') !== 'custom') { 17 | return true; 18 | } 19 | 20 | // If we're using 'custom' frequency, 21 | // all listed fields are required. 22 | 23 | return $this->hasPayloadData(['minute', 'hour', 'day', 'month', 'weekday']); 24 | } 25 | 26 | /** 27 | * Set job command. 28 | * 29 | * @param string $command 30 | * 31 | * @return static 32 | */ 33 | public function schedule(string $command) 34 | { 35 | return $this->attachPayload('command', $command); 36 | } 37 | 38 | /** 39 | * Set job user. 40 | * 41 | * @param string $user 42 | * 43 | * @return static 44 | */ 45 | public function runningAs(string $user) 46 | { 47 | return $this->attachPayload('user', $user); 48 | } 49 | 50 | /** 51 | * Indicates that job should run every minute. 52 | * 53 | * @return static 54 | */ 55 | public function everyMinute() 56 | { 57 | return $this->attachPayload('frequency', 'minutely'); 58 | } 59 | 60 | /** 61 | * Indicates that job should run every hour. 62 | * 63 | * @return static 64 | */ 65 | public function hourly() 66 | { 67 | return $this->attachPayload('frequency', 'hourly'); 68 | } 69 | 70 | /** 71 | * Indicates that job should run every day at midnight. 72 | * 73 | * @return static 74 | */ 75 | public function nightly() 76 | { 77 | return $this->attachPayload('frequency', 'nightly'); 78 | } 79 | 80 | /** 81 | * Indicates that job should run every week. 82 | * 83 | * @return static 84 | */ 85 | public function weekly() 86 | { 87 | return $this->attachPayload('frequency', 'weekly'); 88 | } 89 | 90 | /** 91 | * Indicates that job should run every month. 92 | * 93 | * @return static 94 | */ 95 | public function monthly() 96 | { 97 | return $this->attachPayload('frequency', 'monthly'); 98 | } 99 | 100 | /** 101 | * Schedule job at hour:minute. 102 | * 103 | * @param string $time 104 | * 105 | * @throws \InvalidArgumentException 106 | * 107 | * @return static 108 | */ 109 | public function atTime(string $time) 110 | { 111 | $exploded = explode(':', $time); 112 | 113 | if (sizeof($exploded) !== 2) { 114 | throw new InvalidArgumentException('Given argument "'.$time.'" is not a valid time.'); 115 | } 116 | 117 | list($hour, $minute) = $exploded; 118 | 119 | return $this->attachPayload('frequency', 'custom') 120 | ->attachPayload('hour', $hour) 121 | ->attachPayload('minute', $minute); 122 | } 123 | 124 | /** 125 | * Schedule job at given day. 126 | * 127 | * @param string|int $day 128 | * 129 | * @return static 130 | */ 131 | public function atDay($day) 132 | { 133 | return $this->attachPayload('frequency', 'custom') 134 | ->attachPayload('day', $day); 135 | } 136 | 137 | /** 138 | * Schedule job at given month. 139 | * 140 | * @param string|int $month 141 | * 142 | * @return static 143 | */ 144 | public function atMonth($month) 145 | { 146 | return $this->attachPayload('frequency', 'custom') 147 | ->attachPayload('month', $month); 148 | } 149 | 150 | /** 151 | * Schedule job at given weekday. 152 | * 153 | * @param string|int $weekday 154 | * 155 | * @return static 156 | */ 157 | public function atWeekday($weekday) 158 | { 159 | return $this->attachPayload('frequency', 'custom') 160 | ->attachPayload('weekday', $weekday); 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /src/Jobs/Commands/GetJobCommand.php: -------------------------------------------------------------------------------- 1 | getData('command'); 37 | } 38 | 39 | /** 40 | * Job frequency. 41 | * 42 | * @return string|null 43 | */ 44 | public function frequency() 45 | { 46 | return $this->getData('frequency'); 47 | } 48 | 49 | /** 50 | * Job user. 51 | * 52 | * @return string|null 53 | */ 54 | public function user() 55 | { 56 | return $this->getData('user'); 57 | } 58 | 59 | /** 60 | * Cron string for job. 61 | * 62 | * @return string|null 63 | */ 64 | public function cron() 65 | { 66 | return $this->getData('cron'); 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Jobs/JobsManager.php: -------------------------------------------------------------------------------- 1 | schedule($command); 21 | } 22 | 23 | /** 24 | * Alias for "schedule" method. 25 | * 26 | * @param string $command 27 | * 28 | * @return \Laravel\Forge\Jobs\Commands\CreateJobCommand 29 | */ 30 | public function create(string $command) 31 | { 32 | return $this->schedule($command); 33 | } 34 | 35 | /** 36 | * Initialize new list jobs command. 37 | * 38 | * @return \Laravel\Forge\Jobs\Commands\ListJobsCommand 39 | */ 40 | public function list() 41 | { 42 | return new ListJobsCommand(); 43 | } 44 | 45 | /** 46 | * Initialize new get job command. 47 | * 48 | * @param int $jobId 49 | * 50 | * @return \Laravel\Forge\Jobs\Commands\GetJobCommand 51 | */ 52 | public function get(int $jobId) 53 | { 54 | return (new GetJobCommand())->setResourceId($jobId); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Laravel/Commands/ForgeCredentials.php: -------------------------------------------------------------------------------- 1 | credentials(); 32 | 33 | $headers = ['ID', 'Name', 'Provider']; 34 | $rows = collect($credentials)->map(function ($credential) { 35 | return [$credential['id'], $credential['name'], $credential['type']]; 36 | }); 37 | 38 | $this->table($headers, $rows->toArray()); 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/Laravel/Commands/ForgeServers.php: -------------------------------------------------------------------------------- 1 | choice('What do you want to do?', [ 35 | 'list' => 'List Servers', 36 | 'create' => 'Create New Server', 37 | 'delete' => 'Delete Server', 38 | ]); 39 | 40 | switch ($action) { 41 | case 'list': 42 | return $this->handleListAction($forge); 43 | case 'create': 44 | return $this->handleCreateAction($forge); 45 | case 'delete': 46 | return $this->handleDeleteAction($forge); 47 | } 48 | } 49 | 50 | /** 51 | * Asks user to choose a server. 52 | * 53 | * @param \Laravel\Forge\Forge $forge 54 | * @param string $message = 'Choose Server' 55 | * 56 | * @return \Laravel\Forge\Server 57 | */ 58 | protected function chooseServer(Forge $forge, string $message = 'Choose Server') 59 | { 60 | $choices = []; 61 | 62 | foreach ($forge as $server) { 63 | $choices[$server->name()] = $server->id(); 64 | } 65 | 66 | $serverName = $this->choice($message, $choices); 67 | 68 | return $forge[$serverName]; 69 | } 70 | 71 | /** 72 | * "List Servers" handler. 73 | * 74 | * @param \Laravel\Forge\Forge $forge 75 | * 76 | * @return mixed 77 | */ 78 | protected function handleListAction(Forge $forge) 79 | { 80 | $headers = ['ID', 'Name', 'Size', 'Region', 'Ready?', 'PHP Version']; 81 | $rows = []; 82 | 83 | foreach ($forge as $server) { 84 | $rows[] = [ 85 | $server->id(), 86 | $server->name(), 87 | $server->size(), 88 | $server->region(), 89 | $server->isReady() ? 'Yes' : 'No', 90 | $server->phpVersion(), 91 | ]; 92 | } 93 | 94 | $this->info('Here are your servers:'); 95 | $this->table($headers, $rows); 96 | } 97 | 98 | /** 99 | * "Create New Server" handler. 100 | * 101 | * @param \Laravel\Forge\Forge $forge 102 | * 103 | * @return mixed 104 | */ 105 | protected function handleCreateAction(Forge $forge) 106 | { 107 | $provider = $this->choice('Choose provider', [ 108 | 'ocean2' => 'DigitalOcean', 109 | 'linode' => 'Linode', 110 | 'aws' => 'AWS', 111 | 'custom' => 'Custom VPS', 112 | ]); 113 | 114 | $name = $this->ask('Choose name for your new server'); 115 | 116 | switch ($provider) { 117 | case 'ocean2': 118 | return $this->createServer($forge->create()->droplet($name)); 119 | case 'linode': 120 | return $this->createServer($forge->create()->linode($name)); 121 | case 'aws': 122 | return $this->createServer($forge->create()->aws($name)); 123 | case 'custom': 124 | return $this->createServer($forge->create()->custom($name)); 125 | } 126 | } 127 | 128 | /** 129 | * Create server at specific provider. 130 | * 131 | * @param \Laravel\Forge\Servers\Providers\Provider $provider 132 | * 133 | * @return mixed 134 | */ 135 | protected function createServer(Provider $provider) 136 | { 137 | if ($provider->provider() !== 'custom') { 138 | $size = $this->ask('Enter the server size ID'); 139 | $provider->withSizeId($size); 140 | 141 | $regions = $provider->regions(); 142 | 143 | $region = $this->choice('Choose server region', $regions); 144 | 145 | if ($provider->provider() === 'linode') { 146 | $flippedRegions = array_flip($regions); 147 | $region = $flippedRegions[$region]; 148 | } 149 | 150 | $provider->at($region); 151 | } 152 | 153 | $phpVersion = $this->choice('Choose PHP version', $provider->phpVersions()); 154 | $provider->runningPhp($phpVersion); 155 | 156 | $databaseName = 'forge'; 157 | 158 | if ($this->confirm('Do you want to set new database name?')) { 159 | $databaseName = $this->ask('Choose database name'); 160 | } else { 161 | $this->comment('OK, using default database name ("forge").'); 162 | } 163 | 164 | $databaseType = $this->choice('Choose database type', [ 165 | 'mysql' => 'MySQL 5.7', 166 | 'mysql8' => 'MySQL 8.0', 167 | 'mariadb' => 'MariaDB', 168 | 'postgres' => 'PostgreSQL', 169 | ]); 170 | 171 | switch ($databaseType) { 172 | case 'mysql': 173 | $provider->withMysql($databaseName); 174 | $this->comment('OK, MySQL 5.7 server will be installed.'); 175 | break; 176 | case 'mysql8': 177 | $provider->withMysql($databaseName, 8); 178 | $this->comment('OK, MySQL 8.0 server will be installed.'); 179 | break; 180 | case 'mariadb': 181 | $provider->withMariaDb($databaseName); 182 | $this->comment('OK, MariaDb server will be installed'); 183 | break; 184 | case 'postgres': 185 | $provider->withPostgres($databaseName); 186 | $this->comment('OK, PostgreSQL server will be installed.'); 187 | break; 188 | } 189 | 190 | if ($this->confirm('Do you want to provision this server as node balancer?', false)) { 191 | $provider->asNodeBalancer(); 192 | $this->comment('OK, server will be provisioned as load balancer.'); 193 | } 194 | 195 | if ($provider->provider() === 'custom') { 196 | $publicIp = $this->ask('Please, provide public IP address for this VPS'); 197 | $privateIp = $this->ask('Please, provide private IP address for this VPS'); 198 | 199 | $provider->usingPublicIp($publicIp)->usingPrivateIp($privateIp); 200 | } 201 | 202 | $hasCredentials = $provider->hasPayload('credential_id'); 203 | $credentialMessage = 'Seems that you\'re using predefined credential. Do you want to change credential for this server?'; 204 | $updateCredential = $hasCredentials === false || $this->confirm($credentialMessage, false); 205 | 206 | if ($updateCredential) { 207 | $credentialId = $this->ask('Enter credential ID'); 208 | $provider->usingCredential($credentialId); 209 | } else { 210 | $this->comment('OK, default credential will be used.'); 211 | } 212 | 213 | try { 214 | $server = $provider->save(); 215 | } catch (RequestException $e) { 216 | $response = $e->getResponse(); 217 | 218 | $this->error('Request ended with error.'); 219 | $this->error('HTTP Status Code: '.$response->getStatusCode()); 220 | 221 | return $this->error((string) $response->getBody()); 222 | } 223 | 224 | $this->info('Great! Your new server "'.$server->name().'" was created!'); 225 | $this->info('Please allow up to 10-15 minutes to finish server provision.'); 226 | } 227 | 228 | /** 229 | * "Delete Server" handler. 230 | * 231 | * @param \Laravel\Forge\Forge $forge 232 | * 233 | * @return mixed 234 | */ 235 | protected function handleDeleteAction(Forge $forge) 236 | { 237 | $server = $this->chooseServer($forge); 238 | 239 | $this->error('THIS IS DESTRUCTIVE OPERATION! YOUR SERVER WILL BE DELETED AND THIS ACTION IS IRREVERSIBLE!'); 240 | $this->error('You\'re going to delete '.Str::upper($server->name()).' server.'); 241 | 242 | if (!$this->confirm('Are you totally sure you want to delete this server?', false)) { 243 | return $this->info('Ok, your server left untoched.'); 244 | } 245 | 246 | $this->error('We require one more confirmation that you\'re totally sure about deleting '.$server->name().' server.'); 247 | $confirmation = $this->ask('Enter server name to continue'); 248 | 249 | if ($server->name() !== $confirmation) { 250 | return $this->error('You\'ve entered wrong name, operation aborted.'); 251 | } 252 | 253 | $this->info('Ok, server '.$server->name().' will be deleted now.'); 254 | 255 | $server->delete(); 256 | 257 | $this->info('Server '.$server->name().' was sucessfully deleted.'); 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /src/Laravel/Facades/ForgeFacade.php: -------------------------------------------------------------------------------- 1 | app->singleton(Forge::class, function ($app) { 18 | $token = $app['config']->get('forge.token'); 19 | $forge = new Forge(new ApiProvider($token)); 20 | 21 | // Set default credentials (if any exists). 22 | $defaultCredentials = $app['config']->get('forge.default_credentials', []); 23 | 24 | if (!count($defaultCredentials)) { 25 | return $forge; 26 | } 27 | 28 | foreach ($defaultCredentials as $provider => $credential) { 29 | Factory::setDefaultCredential($provider, $credential); 30 | } 31 | 32 | return $forge; 33 | }); 34 | 35 | // Publish configuration file. 36 | $this->publishes([ 37 | __DIR__.'/configs/forge.php' => config_path('forge.php'), 38 | ]); 39 | 40 | // Register console commands. 41 | if ($this->app->runningInConsole()) { 42 | $this->commands([ 43 | Commands\ForgeCredentials::class, 44 | Commands\ForgeServers::class, 45 | ]); 46 | } 47 | } 48 | 49 | /** 50 | * Register the application services. 51 | */ 52 | public function register() 53 | { 54 | // 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/Laravel/configs/forge.php: -------------------------------------------------------------------------------- 1 | env('FORGE_TOKEN'), 8 | 9 | /** 10 | * Here you may specify default credentials for specific providers. 11 | * 12 | * Provider shortcodes: 13 | * 14 | * - DigitalOcean: "ocean2" 15 | * - Linode: "linode" 16 | * - AWS: "aws" 17 | */ 18 | 'default_credentials' => [ 19 | 20 | // 'ocean2' => 12345, 21 | // 'linode' => 54321, 22 | // 'aws' => 11111, 23 | 24 | ], 25 | ]; 26 | -------------------------------------------------------------------------------- /src/Recipes/Commands/CreateRecipeCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('name', $name); 17 | } 18 | 19 | /** 20 | * Set script content. 21 | * 22 | * @param string $script 23 | * 24 | * @return static 25 | */ 26 | public function usingScript(string $script) 27 | { 28 | return $this->attachPayload('script', $script); 29 | } 30 | 31 | /** 32 | * Set script user. 33 | * 34 | * @param string $user 35 | * 36 | * @return static 37 | */ 38 | public function runningAs(string $user) 39 | { 40 | return $this->attachPayload('user', $user); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Recipes/Commands/GetRecipeCommand.php: -------------------------------------------------------------------------------- 1 | getData('user'); 37 | } 38 | 39 | /** 40 | * Recipe script. 41 | * 42 | * @return string|null 43 | */ 44 | public function script() 45 | { 46 | return $this->getData('script'); 47 | } 48 | 49 | /** 50 | * Run the recipe. 51 | * 52 | * @param array $serverIds 53 | * 54 | * @return bool 55 | */ 56 | public function run(array $serverIds) 57 | { 58 | $formParams = ['json' => ['servers' => $serverIds]]; 59 | $this->getHttpClient()->request('POST', 'recipes/'.$this->id().'/run', $formParams); 60 | 61 | return true; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/Recipes/RecipesManager.php: -------------------------------------------------------------------------------- 1 | identifiedAs($name) 23 | ->usingScript($script); 24 | } 25 | 26 | /** 27 | * Initialize new list recipes command. 28 | * 29 | * @return \Laravel\Forge\Recipes\Commands\ListRecipesCommand 30 | */ 31 | public function list() 32 | { 33 | return new ListRecipesCommand(); 34 | } 35 | 36 | /** 37 | * Initialize new get recipe command. 38 | * 39 | * @param int $recipeId 40 | * 41 | * @return \Laravel\Forge\Recipes\Commands\GetRecipeCommand 42 | */ 43 | public function get(int $recipeId) 44 | { 45 | return (new GetRecipeCommand())->setResourceId($recipeId); 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/Server.php: -------------------------------------------------------------------------------- 1 | getData('credential_id')); 50 | } 51 | 52 | /** 53 | * Human readable server size. 54 | * 55 | * @return string|null 56 | */ 57 | public function size() 58 | { 59 | return $this->getData('size'); 60 | } 61 | 62 | /** 63 | * Server region. 64 | * 65 | * @return string|null 66 | */ 67 | public function region() 68 | { 69 | return $this->getData('region'); 70 | } 71 | 72 | /** 73 | * Server's PHP version. 74 | * 75 | * @return string|null 76 | */ 77 | public function phpVersion() 78 | { 79 | return $this->getData('php_version'); 80 | } 81 | 82 | /** 83 | * Server public IP address. 84 | * 85 | * @return string|null 86 | */ 87 | public function ip() 88 | { 89 | return $this->getData('ip_address'); 90 | } 91 | 92 | /** 93 | * Server private IP address. 94 | * 95 | * @return string|null 96 | */ 97 | public function privateIp() 98 | { 99 | return $this->getData('private_ip_address'); 100 | } 101 | 102 | /** 103 | * Server sudo password - only set on server save. 104 | * 105 | * @return string|null 106 | */ 107 | public function sudoPassword() 108 | { 109 | return $this->getData('sudo_password'); 110 | } 111 | 112 | /** 113 | * Server sudo password - only set on server save. 114 | * 115 | * @return string|null 116 | */ 117 | public function databasePassword() 118 | { 119 | return $this->getData('database_password'); 120 | } 121 | 122 | /** 123 | * Blackfire service status. 124 | * 125 | * @return string|null 126 | */ 127 | public function blackfireStatus() 128 | { 129 | return $this->getData('blackfire_status'); 130 | } 131 | 132 | /** 133 | * Papertrail service status. 134 | * 135 | * @return string 136 | */ 137 | public function papertrailStatus() 138 | { 139 | return $this->getData('papertrail_status'); 140 | } 141 | 142 | /** 143 | * Determines if server access was revoked from Forge. 144 | * 145 | * @return bool 146 | */ 147 | public function isRevoked(): bool 148 | { 149 | return intval($this->getData('revoked')) === 1; 150 | } 151 | 152 | /** 153 | * Determines if server was provisioned and ready to use. 154 | * 155 | * @return bool 156 | */ 157 | public function isReady(): bool 158 | { 159 | return intval($this->getData('is_ready')) === 1; 160 | } 161 | 162 | /** 163 | * Network status. 164 | * 165 | * @return array|null 166 | */ 167 | public function network() 168 | { 169 | return $this->getData('network'); 170 | } 171 | 172 | /** 173 | * Server tags. 174 | * 175 | * @return array 176 | */ 177 | public function tags() 178 | { 179 | return $this->getData('tags'); 180 | } 181 | 182 | /** 183 | * Server ssh port. 184 | * 185 | * @return int 186 | */ 187 | public function sshPort() 188 | { 189 | return $this->getData('ssh_port'); 190 | } 191 | 192 | /** 193 | * The server provider. 194 | * 195 | * @return string 196 | */ 197 | public function provider() 198 | { 199 | return $this->getData('provider'); 200 | } 201 | 202 | /** 203 | * The server provider id. 204 | * 205 | * @return string 206 | */ 207 | public function providerId() 208 | { 209 | return $this->getData('provider_id'); 210 | } 211 | 212 | /** 213 | * Reboot the server. 214 | * 215 | * @return bool 216 | */ 217 | public function reboot(): bool 218 | { 219 | $this->getHttpClient()->request('POST', $this->apiUrl('reboot')); 220 | 221 | return true; 222 | } 223 | 224 | /** 225 | * Enable PHP OPCache on the server. 226 | * 227 | * @return bool 228 | */ 229 | public function enableOPCache(): bool 230 | { 231 | $this->getHttpClient()->request('POST', $this->apiUrl('/php/opcache')); 232 | 233 | return true; 234 | } 235 | 236 | /** 237 | * Disable PHP OPCache on the server. 238 | * 239 | * @return bool 240 | */ 241 | public function disableOPCache(): bool 242 | { 243 | $this->getHttpClient()->request('DELETE', $this->apiUrl('/php/opcache')); 244 | 245 | return true; 246 | } 247 | 248 | /** 249 | * Revoke Forge access to server. 250 | * 251 | * @return bool 252 | **/ 253 | public function revokeAccess(): bool 254 | { 255 | $this->getHttpClient()->request('POST', $this->apiUrl('/revoke')); 256 | 257 | return true; 258 | } 259 | 260 | /** 261 | * Reconnect revoked server. 262 | * 263 | * @return string Public SSH key. 264 | */ 265 | public function reconnect(): string 266 | { 267 | $response = $this->getHttpClient()->request('POST', $this->apiUrl('/reconnect')); 268 | $json = json_decode((string) $response->getBody(), true); 269 | 270 | if (empty($json['public_key'])) { 271 | throw new PublicKeyWasNotFound( 272 | 'Public key was not found after reconnecting revoked server (ID: '.$this->id().').', 273 | 404 274 | ); 275 | } 276 | 277 | return $json['public_key']; 278 | } 279 | 280 | /** 281 | * Reactivate revoked server. Make sure you've installed public SSH key 282 | * before calling this method. 283 | * 284 | * @return bool 285 | */ 286 | public function reactivate(): bool 287 | { 288 | $this->getHttpClient()->request('POST', $this->apiUrl('/reactivate')); 289 | 290 | return true; 291 | } 292 | 293 | /** 294 | * Create new Resource instance from HTTP response. 295 | * 296 | * @param \Psr\Http\Message\ResponseInterface $response 297 | * @param \Laravel\Forge\ApiProvider $api 298 | * @param \Laravel\Forge\Contracts\ResourceContract $owner = null 299 | * 300 | * @return static 301 | */ 302 | public static function createFromResponse(ResponseInterface $response, ApiProvider $api, ResourceContract $owner = null) 303 | { 304 | $json = json_decode((string) $response->getBody(), true); 305 | $result = $json['server'] ?? null; 306 | 307 | if (is_null($result)) { 308 | static::throwNotFoundException(); 309 | } 310 | 311 | if (!empty($json['sudo_password'])) { 312 | $result['sudo_password'] = $json['sudo_password']; 313 | } 314 | 315 | if (!empty($json['database_password'])) { 316 | $result['database_password'] = $json['database_password']; 317 | } 318 | 319 | return new static($api, $result, $owner); 320 | } 321 | } 322 | -------------------------------------------------------------------------------- /src/Servers/Factory.php: -------------------------------------------------------------------------------- 1 | api = $api; 37 | } 38 | 39 | /** 40 | * Set default credential ID for given provider. 41 | * 42 | * @param string $provider 43 | * @param int $credentialId 44 | */ 45 | public static function setDefaultCredential(string $provider, int $credentialId) 46 | { 47 | static::$defaultCredentials[$provider] = $credentialId; 48 | } 49 | 50 | /** 51 | * Remove default credential for given provider or for all providers. 52 | * 53 | * @param string $provider = null 54 | */ 55 | public static function resetDefaultCredential(string $provider = null) 56 | { 57 | if (is_null($provider)) { 58 | static::$defaultCredentials = []; 59 | 60 | return; 61 | } 62 | 63 | unset(static::$defaultCredentials[$provider]); 64 | } 65 | 66 | /** 67 | * Create new DigitalOcean droplet. 68 | * 69 | * @param string $name 70 | * 71 | * @return \Laravel\Forge\Servers\Providers\Provider 72 | */ 73 | public function droplet(string $name) 74 | { 75 | return $this->applyDefaultCredential( 76 | (new DigitalOcean($this->api))->identifiedAs($name) 77 | ); 78 | } 79 | 80 | /** 81 | * Creates new Linode server. 82 | * 83 | * @param string $name 84 | * 85 | * @return \Laravel\Forge\Servers\Providers\Provider 86 | */ 87 | public function linode(string $name) 88 | { 89 | return $this->applyDefaultCredential( 90 | (new Linode($this->api))->identifiedAs($name) 91 | ); 92 | } 93 | 94 | /** 95 | * Creates new AWS server. 96 | * 97 | * @param string $name 98 | * 99 | * @return \Laravel\Forge\Servers\Providers\Provider 100 | */ 101 | public function aws(string $name) 102 | { 103 | return $this->applyDefaultCredential( 104 | (new Aws($this->api))->identifiedAs($name) 105 | ); 106 | } 107 | 108 | /** 109 | * Creates new custom server. 110 | * 111 | * @param string $name 112 | * 113 | * @return \Laravel\Forge\Servers\Providers\Provider 114 | */ 115 | public function custom(string $name) 116 | { 117 | return (new Custom($this->api))->identifiedAs($name); 118 | } 119 | 120 | /** 121 | * Create new server from raw payload data. 122 | * 123 | * @param array $payload 124 | * 125 | * @throws \GuzzleHttp\Exception\RequestException 126 | * 127 | * @return \Laravel\Forge\Server 128 | */ 129 | public function server(array $payload) 130 | { 131 | ksort($payload); 132 | 133 | $response = $this->api->getClient()->request('POST', 'servers', [ 134 | 'json' => $payload, 135 | ]); 136 | 137 | return Server::createFromResponse($response, $this->api); 138 | } 139 | 140 | /** 141 | * Apply default credential ID for given provider (if exists). 142 | * 143 | * @param \Laravel\Forge\Servers\Providers\Provider $provider 144 | * 145 | * @return \Laravel\Forge\Servers\Providers\Provider 146 | */ 147 | protected function applyDefaultCredential(Provider $provider): Provider 148 | { 149 | if (!empty(static::$defaultCredentials[$provider->provider()])) { 150 | $provider->usingCredential( 151 | static::$defaultCredentials[$provider->provider()] 152 | ); 153 | } 154 | 155 | return $provider; 156 | } 157 | } 158 | -------------------------------------------------------------------------------- /src/Servers/Providers/Aws.php: -------------------------------------------------------------------------------- 1 | 'California', 22 | 'eu-west-1' => 'Ireland', 23 | 'eu-central-1' => 'Frankfurt', 24 | 'ap-south-1' => 'Mumbai', 25 | 'us-west-2' => 'Oregon', 26 | 'sa-east-1' => 'Sao Paolo', 27 | 'ap-northeast-2' => 'Seoul', 28 | 'ap-southeast-1' => 'Singapore', 29 | 'ap-southeast-2' => 'Sydney', 30 | 'ap-northeast-1' => 'Tokyo', 31 | 'us-east-1' => 'Virginia', 32 | ]; 33 | } 34 | 35 | /** 36 | * {@inheritdoc} 37 | */ 38 | public function sizes() 39 | { 40 | return [ 41 | '512MB' => 512, 42 | '1GB' => 1, 43 | '2GB' => 2, 44 | '4GB' => 4, 45 | '8GB' => 8, 46 | '16GB' => 16, 47 | '32GB' => 32, 48 | '64GB' => 60, 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Servers/Providers/Custom.php: -------------------------------------------------------------------------------- 1 | payload['ip_address'])) { 33 | $errors[] = 'ip_address'; 34 | } 35 | 36 | if (empty($this->payload['private_ip_address'])) { 37 | $errors[] = 'private_ip_address'; 38 | } 39 | 40 | return count($errors) > 0 ? $errors : true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/Servers/Providers/DigitalOcean.php: -------------------------------------------------------------------------------- 1 | 'Amsterdam 2', 22 | 'ams3' => 'Amsterdam 3', 23 | 'blr1' => 'Bangalore', 24 | 'lon1' => 'London', 25 | 'fra1' => 'Frankfurt', 26 | 'nyc1' => 'New York 1', 27 | 'nyc2' => 'New York 2', 28 | 'nyc3' => 'New York 3', 29 | 'sfo1' => 'San Francisco 1', 30 | 'sfo2' => 'San Francisco 2', 31 | 'sgp1' => 'Singapore', 32 | 'tor1' => 'Toronto', 33 | ]; 34 | } 35 | 36 | /** 37 | * {@inheritdoc} 38 | */ 39 | public function sizes() 40 | { 41 | return [ 42 | '512MB' => 512, 43 | '1GB' => 1, 44 | '2GB' => 2, 45 | '4GB' => 4, 46 | '8GB' => 8, 47 | '16GB' => 16, 48 | 'm-16GB' => 'm16', 49 | '32GB' => 32, 50 | 'm-32GB' => 'm32', 51 | '64GB' => 64, 52 | 'm-64GB' => 'm-64', 53 | ]; 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/Servers/Providers/Linode.php: -------------------------------------------------------------------------------- 1 | 'Dallas', 22 | 3 => 'Fremont', 23 | 4 => 'Atlanta', 24 | 6 => 'Newark', 25 | 7 => 'London', 26 | 8 => 'Tokyo 1', 27 | 9 => 'Singapore', 28 | 10 => 'Frankfurt', 29 | 11 => 'Tokyo 2', 30 | ]; 31 | } 32 | 33 | /** 34 | * {@inheritdoc} 35 | */ 36 | public function sizes() 37 | { 38 | return [ 39 | '1GB' => 1, 40 | '2GB' => 2, 41 | '4GB' => 4, 42 | '8GB' => 8, 43 | '12GB' => 12, 44 | '16GB' => 16, 45 | '32GB' => 32, 46 | '60GB' => 60, 47 | '100GB' => 100, 48 | '200GB' => 200, 49 | ]; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/Servers/Providers/Provider.php: -------------------------------------------------------------------------------- 1 | api = $api; 29 | $this->initProvider(); 30 | } 31 | 32 | /** 33 | * Initializes server provider. 34 | */ 35 | protected function initProvider() 36 | { 37 | $this->payload['provider'] = $this->provider(); 38 | } 39 | 40 | /** 41 | * Server provider name. 42 | * 43 | * @return string 44 | */ 45 | public function provider() 46 | { 47 | return 'abstract'; 48 | } 49 | 50 | /** 51 | * Server provider regions list. 52 | * 53 | * @return array 54 | */ 55 | public function regions() 56 | { 57 | return []; 58 | } 59 | 60 | /** 61 | * Server provider server sizes. 62 | * 63 | * @return array 64 | */ 65 | public function sizes() 66 | { 67 | return []; 68 | } 69 | 70 | /** 71 | * Available PHP versions. 72 | * 73 | * @return array 74 | */ 75 | public function phpVersions() 76 | { 77 | return [56, 70, 71, 72, 73, 74, 80]; 78 | } 79 | 80 | /** 81 | * Validates payload before sending to Forge API. 82 | * 83 | * @return bool|array 84 | */ 85 | public function validate() 86 | { 87 | return true; 88 | } 89 | 90 | /** 91 | * Determines if given region is available at current provider. 92 | * 93 | * @param string $region 94 | * 95 | * @return bool 96 | */ 97 | public function regionAvailable(string $region) 98 | { 99 | return $this->resourceAvailable($this->regions(), $region); 100 | } 101 | 102 | /** 103 | * Determines if given resource exists in resources list. 104 | * 105 | * @param array $resources 106 | * @param mixed $resource 107 | * 108 | * @return bool 109 | */ 110 | protected function resourceAvailable(array $resources, $resource) 111 | { 112 | return isset($resources[$resource]); 113 | } 114 | 115 | /** 116 | * Determines if given key exists in current payload. 117 | * 118 | * @param string $key 119 | * 120 | * @return bool 121 | */ 122 | public function hasPayload(string $key): bool 123 | { 124 | return !empty($this->payload[$key]); 125 | } 126 | 127 | /** 128 | * Set credential ID to create server with. 129 | * 130 | * @param int $credentialId 131 | * 132 | * @return static 133 | */ 134 | public function usingCredential(int $credentialId) 135 | { 136 | $this->payload['credential_id'] = $credentialId; 137 | 138 | return $this; 139 | } 140 | 141 | /** 142 | * Set new server name. 143 | * 144 | * @param string $name 145 | * 146 | * @return static 147 | */ 148 | public function identifiedAs(string $name) 149 | { 150 | $this->payload['name'] = $name; 151 | 152 | return $this; 153 | } 154 | 155 | /** 156 | * Set server size ID. 157 | * 158 | * @param int|string $sizeId 159 | * 160 | * @return static 161 | */ 162 | public function withSizeId($sizeId) 163 | { 164 | if (!is_numeric(ltrim($sizeId, 0))) { 165 | throw new InvalidArgumentException('Given server size ID is not valid'); 166 | } 167 | 168 | $this->payload['size'] = (int) $sizeId; 169 | 170 | return $this; 171 | } 172 | 173 | /** 174 | * Set server region. 175 | * 176 | * @param string $region 177 | * 178 | * @return static 179 | */ 180 | public function at(string $region) 181 | { 182 | if (!$this->regionAvailable($region)) { 183 | throw new InvalidArgumentException('Given region is not supported by '.$this->provider().' provider.'); 184 | } 185 | 186 | $this->payload['region'] = $region; 187 | 188 | return $this; 189 | } 190 | 191 | /** 192 | * Set PHP version. 193 | * 194 | * @param int|string $version 195 | * 196 | * @return static 197 | */ 198 | public function runningPhp($version) 199 | { 200 | $phpVersion = intval(str_replace(['php', '.'], '', $version)); 201 | 202 | if (!in_array($phpVersion, $this->phpVersions())) { 203 | throw new InvalidArgumentException('PHP version "php'.$phpVersion.'" is not supported.'); 204 | } 205 | 206 | $this->payload['php_version'] = 'php'.$phpVersion; 207 | 208 | return $this; 209 | } 210 | 211 | /** 212 | * Indicates that server should be provisioned with MariaDB instead of MySQL. 213 | * 214 | * @param string $database = 'forge' 215 | * 216 | * @return static 217 | */ 218 | public function withMariaDb(string $database = 'forge') 219 | { 220 | $this->payload['mariadb'] = 1; 221 | $this->payload['database'] = $database; 222 | $this->payload['database_type'] = 'mariadb'; 223 | 224 | return $this; 225 | } 226 | 227 | /** 228 | * Indicates that server should be provisioned with MySQL. 229 | * If you need to create MySQL 8 instead of 5.7, pass 8 230 | * as second argument. 231 | * 232 | * @param string $database = 'forge' 233 | * @param int $version = 5 234 | * 235 | * @return static 236 | */ 237 | public function withMysql(string $database = 'forge', int $version = 5) 238 | { 239 | $this->payload['mariadb'] = 0; 240 | $this->payload['database'] = $database; 241 | $this->payload['database_type'] = 'mysql'.($version === 8 ? '8' : ''); 242 | 243 | return $this; 244 | } 245 | 246 | /** 247 | * Indicates that server should be provisioned with PostrgreSQL. 248 | * 249 | * @param string $database = 'forge' 250 | * 251 | * @return static 252 | */ 253 | public function withPostgres(string $database = 'forge') 254 | { 255 | $this->payload['mariadb'] = 0; 256 | $this->payload['database'] = $database; 257 | $this->payload['database_type'] = 'postgres'; 258 | 259 | return $this; 260 | } 261 | 262 | /** 263 | * Indicates that server should be provisioned as load balancer. 264 | * 265 | * @param bool $install = true 266 | * 267 | * @deprecated since 1.3.1 268 | * @see Provider::asNodeBalancer() 269 | * 270 | * @return static 271 | */ 272 | public function asLoadBalancer(bool $install = true) 273 | { 274 | return $this->asNodeBalancer($install); 275 | } 276 | 277 | /** 278 | * Indicates that server should be provisioned as load balancer. 279 | * 280 | * @param bool $install = true 281 | * 282 | * @return static 283 | */ 284 | public function asNodeBalancer(bool $install = true) 285 | { 286 | return $this->togglePayload('node_balancer', $install); 287 | } 288 | 289 | /** 290 | * Servers ID that the new server should be connected to. 291 | * 292 | * @param array $servers 293 | * 294 | * @return static 295 | */ 296 | public function connectedTo(array $servers) 297 | { 298 | $this->payload['network'] = $servers; 299 | 300 | return $this; 301 | } 302 | 303 | /** 304 | * Public IP address. 305 | * 306 | * @param string $ip 307 | * 308 | * @return static 309 | */ 310 | public function usingPublicIp(string $ip) 311 | { 312 | $this->payload['ip_address'] = $ip; 313 | 314 | return $this; 315 | } 316 | 317 | /** 318 | * Private IP address. 319 | * 320 | * @param string $ip 321 | * 322 | * @return static 323 | */ 324 | public function usingPrivateIp(string $ip) 325 | { 326 | $this->payload['private_ip_address'] = $ip; 327 | 328 | return $this; 329 | } 330 | 331 | /** 332 | * Set recipe that should be run after provisioning. 333 | * 334 | * @param int $id 335 | * 336 | * @return static 337 | */ 338 | public function withRecipe(int $id) 339 | { 340 | $this->payload['recipe_id'] = $id; 341 | 342 | return $this; 343 | } 344 | 345 | /** 346 | * Create new server. 347 | * 348 | * @throws \GuzzleHttp\Exception\RequestException 349 | * @throws \InvalidArgumentException 350 | * 351 | * @return \Laravel\Forge\Server 352 | */ 353 | public function save() 354 | { 355 | $validationResult = $this->validate(); 356 | 357 | if ($validationResult !== true) { 358 | throw new InvalidArgumentException( 359 | 'Some required parameters are missing: '.implode(', ', $validationResult) 360 | ); 361 | } 362 | 363 | $response = $this->api->getClient()->request('POST', 'servers', [ 364 | 'json' => $this->sortPayload(), 365 | ]); 366 | 367 | return Server::createFromResponse($response, $this->api); 368 | } 369 | 370 | /** 371 | * Sort payload data by key name. 372 | * 373 | * @return array 374 | */ 375 | protected function sortPayload(): array 376 | { 377 | $payload = $this->payload; 378 | 379 | ksort($payload); 380 | 381 | return $payload; 382 | } 383 | 384 | /** 385 | * Toggle boolean payload key. 386 | * 387 | * @param string $key 388 | * @param bool $install 389 | * 390 | * @return static 391 | */ 392 | protected function togglePayload(string $key, bool $install) 393 | { 394 | if ($install === false && isset($this->payload[$key])) { 395 | unset($this->payload[$key]); 396 | } elseif ($install === true) { 397 | $this->payload[$key] = 1; 398 | } 399 | 400 | return $this; 401 | } 402 | } 403 | -------------------------------------------------------------------------------- /src/Services/BlackfireService.php: -------------------------------------------------------------------------------- 1 | getService()->installable(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Commands/RebootServiceCommand.php: -------------------------------------------------------------------------------- 1 | getService()->rebootable(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Commands/StopServiceCommand.php: -------------------------------------------------------------------------------- 1 | getService()->stoppable(); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/Services/Commands/UninstallServiceCommand.php: -------------------------------------------------------------------------------- 1 | getService()->uninstallable(); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/Services/Mysql/Commands/CreateMysqlDatabaseCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('name', $name); 17 | } 18 | 19 | /** 20 | * Also create database user and password. 21 | * 22 | * @param string $user 23 | * @param string $password 24 | * 25 | * @return static 26 | */ 27 | public function withUser(string $user, string $password) 28 | { 29 | return $this->attachPayload('user', $user)->attachPayload('password', $password); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/Services/Mysql/Commands/CreateMysqlUserCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('name', $name) 20 | ->attachPayload('password', $password); 21 | } 22 | 23 | /** 24 | * Databases the user will have access to. 25 | * 26 | * @param array|int|\Laravel\Forge\Services\Mysql\MysqlDatabase $databases 27 | * 28 | * @return static 29 | */ 30 | public function withAccessTo($databases) 31 | { 32 | return $this->attachPayload('databases', $this->extractDatabaseIds($databases)); 33 | } 34 | 35 | /** 36 | * Extracts database IDs from single MysqlDatabase instance, 37 | * single integer ID, array of MysqlDatabase instances or from 38 | * array of integer IDs. 39 | * 40 | * @return array 41 | */ 42 | protected function extractDatabaseIds($databases): array 43 | { 44 | if ($databases instanceof MysqlDatabase) { 45 | return [$databases->id()]; 46 | } elseif (is_integer($databases)) { 47 | return [intval($databases)]; 48 | } 49 | 50 | $databaseIds = []; 51 | 52 | foreach ($databases as $database) { 53 | if ($database instanceof MysqlDatabase) { 54 | $databaseIds[] = $database->id(); 55 | } else { 56 | $databaseIds[] = intval($database); 57 | } 58 | } 59 | 60 | return $databaseIds; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Services/Mysql/Commands/GetMysqlDatabaseCommand.php: -------------------------------------------------------------------------------- 1 | getData('name'); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Services/Mysql/MysqlUser.php: -------------------------------------------------------------------------------- 1 | getData('name'); 37 | } 38 | 39 | /** 40 | * Database IDs the user has access to. 41 | * 42 | * @return array 43 | */ 44 | public function databases(): array 45 | { 46 | return $this->getData('databases', []); 47 | } 48 | 49 | /** 50 | * Determines if user has access to given database. 51 | * 52 | * @param int|\Laravel\Forge\Services\Mysql\MysqlDatabase $database 53 | * 54 | * @return bool 55 | */ 56 | public function hasAccessTo($database): bool 57 | { 58 | $databaseId = ($database instanceof MysqlDatabase ? $database->id() : intval($database)); 59 | 60 | return in_array($databaseId, $this->databases()); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Services/Mysql/MysqlUsers.php: -------------------------------------------------------------------------------- 1 | identifiedAs($name, $password); 22 | } 23 | 24 | /** 25 | * Initialize new list MySQL users command. 26 | * 27 | * @return \Laravel\Forge\Services\Mysql\Commands\ListMysqlUsersCommand 28 | */ 29 | public function list() 30 | { 31 | return new ListMysqlUsersCommand(); 32 | } 33 | 34 | /** 35 | * Initialize new get MySQL user command. 36 | * 37 | * @param int $userId 38 | * 39 | * @return \Laravel\Forge\Services\Mysql\Commands\GetMysqlUserCommand 40 | */ 41 | public function get(int $userId) 42 | { 43 | return (new GetMysqlUserCommand())->setResourceId($userId); 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/Services/MysqlService.php: -------------------------------------------------------------------------------- 1 | identifiedAs($name); 29 | } 30 | 31 | /** 32 | * Initialize new list MySQL databases command. 33 | * 34 | * @return \Laravel\Forge\Services\Mysql\Commands\ListMysqlDatabasesCommand 35 | */ 36 | public function list() 37 | { 38 | return new ListMysqlDatabasesCommand(); 39 | } 40 | 41 | /** 42 | * Initialize new get MySQL database command. 43 | * 44 | * @param int $databaseId 45 | * 46 | * @return \Laravel\Forge\Services\Mysql\Commands\GetMysqlDatabaseCommand 47 | */ 48 | public function get(int $databaseId) 49 | { 50 | return (new GetMysqlDatabaseCommand())->setResourceId($databaseId); 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/Services/NginxService.php: -------------------------------------------------------------------------------- 1 | attachPayload('domain', $domain); 17 | } 18 | 19 | /** 20 | * Indicates that site will be created as General PHP/Laravel Application. 21 | * 22 | * @return static 23 | */ 24 | public function asPhp() 25 | { 26 | return $this->attachPayload('project_type', 'php'); 27 | } 28 | 29 | /** 30 | * Identifies which web directory the public app will reside at 31 | * 32 | * @param string $directory 33 | * 34 | * @return static 35 | */ 36 | public function withDirectory(string $directory) 37 | { 38 | return $this->attachPayload('directory', $directory); 39 | } 40 | 41 | /** 42 | * Isolates site and create a new user. 43 | * 44 | * @param string $directory 45 | * 46 | * @return static 47 | */ 48 | public function isolated(string $username) 49 | { 50 | $this->attachPayload('isolated', true); 51 | $this->attachPayload('username', $username); 52 | 53 | return $this; 54 | } 55 | 56 | /** 57 | * Indicates that site will be created as Static HTML site. 58 | * 59 | * @return static 60 | */ 61 | public function asStatic() 62 | { 63 | return $this->attachPayload('project_type', 'html'); 64 | } 65 | 66 | /** 67 | * Indicates that site will be created as Symfony Application. 68 | * 69 | * @return static 70 | */ 71 | public function asSymfony() 72 | { 73 | return $this->attachPayload('project_type', 'symfony'); 74 | } 75 | 76 | /** 77 | * Indicates that site will be created as Symfony (Dev) Application. 78 | * 79 | * @return static 80 | */ 81 | public function asSymfonyDev() 82 | { 83 | return $this->attachPayload('project_type', 'symfony_dev'); 84 | } 85 | 86 | /** 87 | * Alias for "asPhp" method. 88 | * 89 | * @return static 90 | */ 91 | public function asLaravel() 92 | { 93 | return $this->asPhp(); 94 | } 95 | 96 | /** 97 | * Alias for "asPhp" method. 98 | * 99 | * @return static 100 | */ 101 | public function asGeneralPhp() 102 | { 103 | return $this->asPhp(); 104 | } 105 | 106 | /** 107 | * Alias for "asStatic" method. 108 | * 109 | * @return static 110 | */ 111 | public function asHtml() 112 | { 113 | return $this->asStatic(); 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/Sites/Commands/GetSiteCommand.php: -------------------------------------------------------------------------------- 1 | getData('name'); 38 | } 39 | 40 | /** 41 | * Site username. 42 | * 43 | * @return string|null 44 | */ 45 | public function username() 46 | { 47 | return $this->getData('username'); 48 | } 49 | 50 | /** 51 | * Project type. 52 | * 53 | * @return string 54 | */ 55 | public function projectType() 56 | { 57 | return $this->getData('project_type'); 58 | } 59 | 60 | /** 61 | * Site directory. 62 | * 63 | * @return string 64 | */ 65 | public function directory() 66 | { 67 | return $this->getData('directory'); 68 | } 69 | 70 | /** 71 | * Site repository. 72 | * 73 | * @return string 74 | */ 75 | public function repository() 76 | { 77 | return $this->getData('repository'); 78 | } 79 | 80 | /** 81 | * Site repository provider. 82 | * 83 | * @return string 84 | */ 85 | public function repositoryProvider() 86 | { 87 | return $this->getData('repository_provider'); 88 | } 89 | 90 | /** 91 | * Site repository branch. 92 | * 93 | * @return string 94 | */ 95 | public function repositoryBranch() 96 | { 97 | return $this->getData('repository_branch'); 98 | } 99 | 100 | /** 101 | * Site repository status. 102 | * 103 | * @return string 104 | */ 105 | public function repositoryStatus() 106 | { 107 | return $this->getData('repository_status'); 108 | } 109 | 110 | /** 111 | * Site deployment status. 112 | * 113 | * @return string | null 114 | */ 115 | public function deploymentStatus() 116 | { 117 | return $this->getData('deployment_status'); 118 | } 119 | 120 | /** 121 | * Site allows wildcard URIs. 122 | * 123 | * @return boolean 124 | */ 125 | public function wildcards() 126 | { 127 | return $this->getData('wildcards'); 128 | } 129 | 130 | /** 131 | * Site quick deploy enabled. 132 | * 133 | * @return boolean 134 | */ 135 | public function quickDeploy() 136 | { 137 | return $this->getData('quick_deploy'); 138 | } 139 | 140 | /** 141 | * Site hipchat room. 142 | * 143 | * @return string 144 | */ 145 | public function hipchatRoom() 146 | { 147 | return $this->getData('hipchat_room'); 148 | } 149 | 150 | /** 151 | * Site slack channel. 152 | * 153 | * @return string 154 | */ 155 | public function slackChannel() 156 | { 157 | return $this->getData('slack_channel'); 158 | } 159 | 160 | /** 161 | * Site app. 162 | * 163 | * @return string | null 164 | */ 165 | public function app() 166 | { 167 | return $this->getData('app'); 168 | } 169 | 170 | /** 171 | * Site app status. 172 | * 173 | * @return string | null 174 | */ 175 | public function appStatus() 176 | { 177 | return $this->getData('app_status'); 178 | } 179 | 180 | /** 181 | * Install new application on site. 182 | * 183 | * @param \Laravel\Forge\Contracts\ApplicationContract $application 184 | * 185 | * @return bool 186 | */ 187 | public function install(ApplicationContract $application) 188 | { 189 | $this->getHttpClient()->request('POST', $this->apiUrl($application->type()), [ 190 | 'json' => $application->payload(), 191 | ]); 192 | 193 | return true; 194 | } 195 | 196 | /** 197 | * Install new application on site. 198 | * 199 | * @param \Laravel\Forge\Contracts\ApplicationContract $application 200 | * 201 | * @return bool 202 | */ 203 | public function updateApplication(ApplicationContract $application) 204 | { 205 | $this->getHttpClient()->request('PUT', $this->apiUrl($application->type()), [ 206 | 'json' => $application->payload(), 207 | ]); 208 | 209 | return true; 210 | } 211 | 212 | /** 213 | * Uninstall application from site. 214 | * 215 | * @param \Laravel\Forge\Contracts\ApplicationContract $application 216 | * 217 | * @return bool 218 | */ 219 | public function uninstall(ApplicationContract $application) 220 | { 221 | $this->getHttpClient()->request('DELETE', $this->apiUrl($application->type())); 222 | 223 | return true; 224 | } 225 | 226 | /** 227 | * Connect load balancer. 228 | * 229 | * @return bool 230 | */ 231 | public function balance(array $serverIds) 232 | { 233 | $this->getHttpClient()->request('PUT', $this->apiUrl('/balancing'), [ 234 | 'json' => ['servers' => $serverIds] 235 | ]); 236 | 237 | return true; 238 | } 239 | } 240 | -------------------------------------------------------------------------------- /src/Sites/SitesManager.php: -------------------------------------------------------------------------------- 1 | identifiedAs($domain)->withDirectory('/public'); 21 | } 22 | 23 | /** 24 | * Initialize new list sites command. 25 | * 26 | * @return \Laravel\Forge\Sites\Commands\ListSitesCommand 27 | */ 28 | public function list() 29 | { 30 | return new ListSitesCommand(); 31 | } 32 | 33 | /** 34 | * Initialize new get site command. 35 | * 36 | * @return \Laravel\Forge\Sites\Commands\GetSiteCommand 37 | */ 38 | public function get(int $siteId) 39 | { 40 | return (new GetSiteCommand())->setResourceId($siteId); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/SshKeys/Commands/CreateSshKeyCommand.php: -------------------------------------------------------------------------------- 1 | attachPayload('name', $name); 17 | } 18 | 19 | /** 20 | * Set key content. 21 | * 22 | * @param string $content 23 | * 24 | * @return static 25 | */ 26 | public function withContent(string $content) 27 | { 28 | return $this->attachPayload('key', $content); 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/SshKeys/Commands/GetSshKeyCommand.php: -------------------------------------------------------------------------------- 1 | identifiedAs($name); 21 | } 22 | 23 | /** 24 | * Initialize new list SSH keys command. 25 | * 26 | * @return \Laravel\Forge\SshKeys\Commands\ListSshKeysCommand 27 | */ 28 | public function list() 29 | { 30 | return new ListSshKeysCommand(); 31 | } 32 | 33 | /** 34 | * Initialize new get SSH key command. 35 | * 36 | * @param int $keyId 37 | * 38 | * @return \Laravel\Forge\SshKeys\Commands\GetSshKeyCommand 39 | */ 40 | public function get(int $keyId) 41 | { 42 | return (new GetSshKeyCommand())->setResourceId($keyId); 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/Traits/AbstractCollection.php: -------------------------------------------------------------------------------- 1 | lazyLoadInitiated === true) { 44 | return; 45 | } 46 | 47 | $this->lazyLoad(); 48 | $this->generateKeys(); 49 | 50 | $this->lazyLoadInitiated = true; 51 | } 52 | 53 | /** 54 | * Determines if lazy load was already initiated. 55 | * 56 | * @return bool 57 | */ 58 | protected function lazyLoadInitiated(): bool 59 | { 60 | return $this->lazyLoadInitiated === true; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /src/Traits/ArrayAccessTrait.php: -------------------------------------------------------------------------------- 1 | data[$offset]); 17 | } 18 | 19 | /** 20 | * Puts value to given offset or appends it to current collection. 21 | * 22 | * @param mixed $offset 23 | * @param mixed $value 24 | */ 25 | public function offsetSet($offset, $value): void 26 | { 27 | if (is_null($offset)) { 28 | $this->data[] = $value; 29 | } else { 30 | $this->data[$offset] = $value; 31 | } 32 | 33 | $this->keys = array_keys($this->data); 34 | } 35 | 36 | /** 37 | * Retrieves given offset from current collection. 38 | * Returns NULL if no value found. 39 | * 40 | * @param mixed $offset 41 | * 42 | * @return mixed|null 43 | */ 44 | #[\ReturnTypeWillChange] 45 | public function offsetGet($offset) 46 | { 47 | return isset($this->data[$offset]) ? $this->data[$offset] : null; 48 | } 49 | 50 | /** 51 | * Drops given offset from current collection. 52 | * 53 | * @param mixed $offset 54 | */ 55 | public function offsetUnset($offset): void 56 | { 57 | unset($this->data[$offset]); 58 | 59 | $this->keys = array_keys($this->data); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/Traits/LazyArrayAccess.php: -------------------------------------------------------------------------------- 1 | checkLazyLoad(); 17 | 18 | return isset($this->items[$offset]); 19 | } 20 | 21 | /** 22 | * Puts value to given offset or appends it to current collection. 23 | * 24 | * @param mixed $offset 25 | * @param mixed $value 26 | */ 27 | public function offsetSet($offset, $value): void 28 | { 29 | $this->checkLazyLoad(); 30 | 31 | if (is_null($offset)) { 32 | $this->items[] = $value; 33 | } else { 34 | $this->items[$offset] = $value; 35 | } 36 | 37 | $this->keys = array_keys($this->items); 38 | } 39 | 40 | /** 41 | * Retrieves given offset from current collection. 42 | * Returns NULL if no value found. 43 | * 44 | * @param mixed $offset 45 | * 46 | * @return mixed|null 47 | */ 48 | #[\ReturnTypeWillChange] 49 | public function offsetGet($offset) 50 | { 51 | $this->checkLazyLoad(); 52 | 53 | return isset($this->items[$offset]) ? $this->items[$offset] : null; 54 | } 55 | 56 | /** 57 | * Drops given offset from current collection. 58 | * 59 | * @param mixed $offset 60 | */ 61 | public function offsetUnset($offset): void 62 | { 63 | $this->checkLazyLoad(); 64 | 65 | unset($this->items[$offset]); 66 | 67 | $this->keys = array_keys($this->items); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /src/Traits/LazyIterator.php: -------------------------------------------------------------------------------- 1 | checkLazyLoad(); 16 | 17 | $currentKey = $this->keys[$this->position]; 18 | 19 | return isset($this->items[$currentKey]) ? $this->items[$currentKey] : null; 20 | } 21 | 22 | /** 23 | * Current iterator position. 24 | * 25 | * @return mixed 26 | */ 27 | #[\ReturnTypeWillChange] 28 | public function key() 29 | { 30 | $this->checkLazyLoad(); 31 | 32 | return $this->keys[$this->position]; 33 | } 34 | 35 | /** 36 | * Increments iterator position. 37 | */ 38 | public function next(): void 39 | { 40 | $this->checkLazyLoad(); 41 | 42 | $this->position++; 43 | } 44 | 45 | /** 46 | * Rewinds iterator back to first position. 47 | */ 48 | public function rewind(): void 49 | { 50 | $this->checkLazyLoad(); 51 | 52 | $this->position = 0; 53 | } 54 | 55 | /** 56 | * Determines if there is some value at current iterator position. 57 | * 58 | * @return bool 59 | */ 60 | public function valid(): bool 61 | { 62 | $this->checkLazyLoad(); 63 | 64 | if (!isset($this->keys[$this->position])) { 65 | return false; 66 | } 67 | 68 | $currentKey = $this->keys[$this->position]; 69 | 70 | return isset($this->items[$currentKey]); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/Users/Commands/GetAuthenticatedUserCommand.php: -------------------------------------------------------------------------------- 1 | getData('id'); 37 | } 38 | 39 | /** 40 | * Get user name. 41 | * 42 | * @return string|null 43 | */ 44 | public function getName() 45 | { 46 | return $this->getData('name'); 47 | } 48 | 49 | /** 50 | * Get user email. 51 | * 52 | * @return string|null 53 | */ 54 | public function getEmail() 55 | { 56 | return $this->getData('email'); 57 | } 58 | 59 | /** 60 | * Get card's last four numbers. 61 | * 62 | * @return string|null 63 | */ 64 | public function getCardLastFour() 65 | { 66 | return $this->getData('card_last_four'); 67 | } 68 | 69 | /** 70 | * Determines whether the user is connected to GitHub. 71 | * 72 | * @return bool 73 | */ 74 | public function connectedToGitHub(): bool 75 | { 76 | return boolval($this->getData('connected_to_github')) === true; 77 | } 78 | 79 | /** 80 | * Determines whether the user is connected to GitLab. 81 | * 82 | * @return bool 83 | */ 84 | public function connectedToGitLab(): bool 85 | { 86 | return boolval($this->getData('connected_to_gitlab')) === true; 87 | } 88 | 89 | /** 90 | * Determines whether the user is connected to Bitbucket. 91 | * 92 | * @return bool 93 | */ 94 | public function connectedToBitbucket(): bool 95 | { 96 | return boolval($this->getData('connected_to_bitbucket')) === true; 97 | } 98 | 99 | /** 100 | * Determines whether the user is connected to Bitbucket V2. 101 | * 102 | * @return bool 103 | */ 104 | public function connectedToBitbucketTwo(): bool 105 | { 106 | return boolval($this->getData('connected_to_bitbucket_two')) === true; 107 | } 108 | 109 | /** 110 | * Determines whether the user is connected to DigitalOcean. 111 | * 112 | * @return bool 113 | */ 114 | public function connectedToDigitalOcean(): bool 115 | { 116 | return boolval($this->getData('connected_to_digitalocean')) === true; 117 | } 118 | 119 | /** 120 | * Determines whether the user is connected to Linode. 121 | * 122 | * @return bool 123 | */ 124 | public function connectedToLinode(): bool 125 | { 126 | return boolval($this->getData('connected_to_linode')) === true; 127 | } 128 | 129 | /** 130 | * Determines whether the user is connected to Vultr. 131 | * 132 | * @return bool 133 | */ 134 | public function connectedToVultr(): bool 135 | { 136 | return boolval($this->getData('connected_to_vultr')) === true; 137 | } 138 | 139 | /** 140 | * Determines whether the user is connected to Aws. 141 | * 142 | * @return bool 143 | */ 144 | public function connectedToAws(): bool 145 | { 146 | return boolval($this->getData('connected_to_aws')) === true; 147 | } 148 | 149 | /** 150 | * Determines whether the user has active Forge subscription. 151 | * 152 | * @return bool 153 | */ 154 | public function subscribed(): bool 155 | { 156 | return boolval($this->getData('subscribed')) === true; 157 | } 158 | 159 | /** 160 | * Determines whether the user can create new servers. 161 | * 162 | * @return bool 163 | */ 164 | public function canCreateServers(): bool 165 | { 166 | return boolval($this->getData('can_create_servers')) === true; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /src/Users/UsersService.php: -------------------------------------------------------------------------------- 1 | attachPayload('connection', $connection); 17 | } 18 | 19 | /** 20 | * Set queue name. 21 | * 22 | * @param string $queue 23 | * 24 | * @return static 25 | */ 26 | public function onQueue(string $queue) 27 | { 28 | return $this->attachPayload('queue', $queue); 29 | } 30 | 31 | /** 32 | * Set timeout. 33 | * 34 | * @param int $seconds 35 | * 36 | * @return static 37 | */ 38 | public function withTimeout(int $seconds) 39 | { 40 | return $this->attachPayload('timeout', $seconds); 41 | } 42 | 43 | /** 44 | * Set sleep seconds. 45 | * 46 | * @param int $seconds 47 | * 48 | * @return static 49 | */ 50 | public function sleepFor(int $seconds) 51 | { 52 | return $this->attachPayload('sleep', $seconds); 53 | } 54 | 55 | /** 56 | * Set max tries count. 57 | * 58 | * @param int $tries 59 | * 60 | * @return static 61 | */ 62 | public function maxTries(int $tries) 63 | { 64 | return $this->attachPayload('tries', $tries); 65 | } 66 | 67 | /** 68 | * Indicates that worker should start as daemon. 69 | * 70 | * @return static 71 | */ 72 | public function asDaemon() 73 | { 74 | return $this->attachPayload('daemon', true); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/Workers/Commands/GetWorkerCommand.php: -------------------------------------------------------------------------------- 1 | getData('connection'); 37 | } 38 | 39 | /** 40 | * Worker timeout. 41 | * 42 | * @return int 43 | */ 44 | public function timeout(): int 45 | { 46 | return intval($this->getData('timeout')); 47 | } 48 | 49 | /** 50 | * Max tries count. 51 | * 52 | * @return int 53 | */ 54 | public function maxTries(): int 55 | { 56 | return intval($this->getData('tries')); 57 | } 58 | 59 | /** 60 | * Determines if worker is daemon. 61 | * 62 | * @return bool 63 | */ 64 | public function daemon(): bool 65 | { 66 | return intval($this->getData('daemon')) === 1; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/Workers/WorkersManager.php: -------------------------------------------------------------------------------- 1 | usingConnection($connection); 21 | } 22 | 23 | /** 24 | * Alias for "start" method. 25 | * 26 | * @param string $connection 27 | * 28 | * @return \Laravel\Forge\Workers\Commands\CreateWorkerCommand 29 | */ 30 | public function create(string $connection) 31 | { 32 | return $this->start($connection); 33 | } 34 | 35 | /** 36 | * Initialize new get worker command. 37 | * 38 | * @param int $workerId 39 | * 40 | * @return \Laravel\Forge\Workers\Commands\GetWorkerCommand 41 | */ 42 | public function get(int $workerId) 43 | { 44 | return (new GetWorkerCommand())->setResourceId($workerId); 45 | } 46 | 47 | /** 48 | * Initialize new list workers command. 49 | * 50 | * @return \Laravel\Forge\Workers\Commands\ListWorkersCommand 51 | */ 52 | public function list() 53 | { 54 | return new ListWorkersCommand(); 55 | } 56 | } 57 | --------------------------------------------------------------------------------