Skip to content

Commit

Permalink
Feature/nodejs (#397)
Browse files Browse the repository at this point in the history
* Add node support using nvm

* Add icon

* Rename to NodeJS

* Rename to NodeJS

* update php and node logo

* only services which have units can be started,restarted,stopped,disabled and enabled

* add tests

---------

Co-authored-by: Saeed Vaziry <[email protected]>
Co-authored-by: Saeed Vaziry <[email protected]>
  • Loading branch information
3 people authored Dec 24, 2024
1 parent da10431 commit 924920e
Show file tree
Hide file tree
Showing 21 changed files with 706 additions and 2 deletions.
32 changes: 32 additions & 0 deletions app/Actions/NodeJS/ChangeDefaultCli.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Actions\NodeJS;

use App\Enums\ServiceStatus;
use App\Models\Server;
use App\SSH\Services\NodeJS\NodeJS;
use Illuminate\Validation\ValidationException;

class ChangeDefaultCli
{
public function change(Server $server, array $input): void
{
$this->validate($server, $input);
$service = $server->nodejs($input['version']);
/** @var NodeJS $handler */
$handler = $service->handler();
$handler->setDefaultCli();
$server->defaultService('nodejs')->update(['is_default' => 0]);
$service->update(['is_default' => 1]);
$service->update(['status' => ServiceStatus::READY]);
}

public function validate(Server $server, array $input): void
{
if (! isset($input['version']) || ! in_array($input['version'], $server->installedNodejsVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is not installed')]
);
}
}
}
45 changes: 45 additions & 0 deletions app/Actions/NodeJS/InstallNewNodeJsVersion.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php

namespace App\Actions\NodeJS;

use App\Enums\NodeJS;
use App\Enums\ServiceStatus;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Validation\Rule;

class InstallNewNodeJsVersion
{
public function install(Server $server, array $input): void
{
$nodejs = new Service([
'server_id' => $server->id,
'type' => 'nodejs',
'type_data' => [],
'name' => 'nodejs',
'version' => $input['version'],
'status' => ServiceStatus::INSTALLING,
'is_default' => false,
]);
$nodejs->save();

dispatch(function () use ($nodejs) {
$nodejs->handler()->install();
$nodejs->status = ServiceStatus::READY;
$nodejs->save();
})->catch(function () use ($nodejs) {
$nodejs->delete();
})->onConnection('ssh');
}

public static function rules(Server $server): array
{
return [
'version' => [
'required',
Rule::in(config('core.nodejs_versions')),
Rule::notIn(array_merge($server->installedNodejsVersions(), [NodeJS::NONE])),
],
];
}
}
53 changes: 53 additions & 0 deletions app/Actions/NodeJS/UninstallNodeJS.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
<?php

namespace App\Actions\NodeJS;

use App\Enums\ServiceStatus;
use App\Models\Server;
use App\Models\Service;
use Illuminate\Support\Facades\Validator;
use Illuminate\Validation\ValidationException;

class UninstallNodeJS
{
public function uninstall(Server $server, array $input): void
{
$this->validate($server, $input);

/** @var Service $nodejs */
$nodejs = $server->nodejs($input['version']);
$nodejs->status = ServiceStatus::UNINSTALLING;
$nodejs->save();

dispatch(function () use ($nodejs) {
$nodejs->handler()->uninstall();
$nodejs->delete();
})->catch(function () use ($nodejs) {
$nodejs->status = ServiceStatus::FAILED;
$nodejs->save();
})->onConnection('ssh');
}

/**
* @throws ValidationException
*/
private function validate(Server $server, array $input): void
{
Validator::make($input, [
'version' => 'required|string',
])->validate();

if (! in_array($input['version'], $server->installedNodejsVersions())) {
throw ValidationException::withMessages(
['version' => __('This version is not installed')]
);
}

$hasSite = $server->sites()->where('nodejs_version', $input['version'])->first();
if ($hasSite) {
throw ValidationException::withMessages(
['version' => __('Cannot uninstall this version because some sites are using it!')]
);
}
}
}
32 changes: 32 additions & 0 deletions app/Enums/NodeJS.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace App\Enums;

use App\Traits\Enum;

final class NodeJS
{
use Enum;

const NONE = 'none';

const V4 = '4';

const V6 = '6';

const V8 = '8';

const V10 = '10';

const V12 = '12';

const V14 = '14';

const V16 = '16';

const V18 = '18';

const V20 = '20';

const V22 = '22';
}
20 changes: 20 additions & 0 deletions app/Models/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,17 @@ public function installedPHPVersions(): array
return $versions;
}

public function installedNodejsVersions(): array
{
$versions = [];
$nodes = $this->services()->where('type', 'nodejs')->get(['version']);
foreach ($nodes as $node) {
$versions[] = $node->version;
}

return $versions;
}

public function type(): ServerType
{
$typeClass = config('core.server_types_class')[$this->type];
Expand Down Expand Up @@ -377,6 +388,15 @@ public function php(?string $version = null): ?Service
return $this->service('php', $version);
}

public function nodejs(?string $version = null): ?Service
{
if (! $version) {
return $this->defaultService('nodejs');
}

return $this->service('nodejs', $version);
}

public function memoryDatabase(?string $version = null): ?Service
{
if (! $version) {
Expand Down
2 changes: 2 additions & 0 deletions app/Models/Site.php
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
* @property ?Ssl $activeSsl
* @property string $ssh_key_name
* @property ?SourceControl $sourceControl
*
* @TODO: Add nodejs_version column
*/
class Site extends AbstractModel
{
Expand Down
25 changes: 25 additions & 0 deletions app/Policies/ServicePolicy.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,29 @@ public function delete(User $user, Service $service): bool
{
return ($user->isAdmin() || $service->server->project->users->contains($user)) && $service->server->isReady();
}

public function start(User $user, Service $service): bool
{
return $this->update($user, $service) && $service->unit;
}

public function stop(User $user, Service $service): bool
{
return $this->update($user, $service) && $service->unit;
}

public function restart(User $user, Service $service): bool
{
return $this->update($user, $service) && $service->unit;
}

public function disable(User $user, Service $service): bool
{
return $this->update($user, $service) && $service->unit;
}

public function enable(User $user, Service $service): bool
{
return $this->update($user, $service) && $service->unit;
}
}
77 changes: 77 additions & 0 deletions app/SSH/Services/NodeJS/NodeJS.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php

namespace App\SSH\Services\NodeJS;

use App\SSH\HasScripts;
use App\SSH\Services\AbstractService;
use Closure;
use Illuminate\Validation\Rule;

class NodeJS extends AbstractService
{
use HasScripts;

public function creationRules(array $input): array
{
return [
'version' => [
'required',
Rule::in(config('core.nodejs_versions')),
Rule::notIn([\App\Enums\NodeJS::NONE]),
Rule::unique('services', 'version')
->where('type', 'nodejs')
->where('server_id', $this->service->server_id),
],
];
}

public function deletionRules(): array
{
return [
'service' => [
function (string $attribute, mixed $value, Closure $fail) {
$hasSite = $this->service->server->sites()
->where('nodejs_version', $this->service->version)
->exists();
if ($hasSite) {
$fail('Some sites are using this NodeJS version.');
}
},
],
];
}

public function install(): void
{
$server = $this->service->server;
$server->ssh()->exec(
$this->getScript('install-nodejs.sh', [
'version' => $this->service->version,
'user' => $server->getSshUser(),
]),
'install-nodejs-'.$this->service->version
);
$this->service->server->os()->cleanup();
}

public function uninstall(): void
{
$this->service->server->ssh()->exec(
$this->getScript('uninstall-nodejs.sh', [
'version' => $this->service->version,
]),
'uninstall-nodejs-'.$this->service->version
);
$this->service->server->os()->cleanup();
}

public function setDefaultCli(): void
{
$this->service->server->ssh()->exec(
$this->getScript('change-default-nodejs.sh', [
'version' => $this->service->version,
]),
'change-default-nodejs'
);
}
}
13 changes: 13 additions & 0 deletions app/SSH/Services/NodeJS/scripts/change-default-nodejs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

if ! nvm alias default __version__; then
echo 'VITO_SSH_ERROR' && exit 1
fi

if ! nvm use default; then
echo 'VITO_SSH_ERROR' && exit 1
fi

echo "Default Node.js is now:"
node -v
68 changes: 68 additions & 0 deletions app/SSH/Services/NodeJS/scripts/install-nodejs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# Download NVM, if not already downloaded
if [ ! -d "$HOME/.nvm" ]; then
if ! git clone https://github.com/nvm-sh/nvm.git "$HOME/.nvm"; then
echo 'VITO_SSH_ERROR' && exit 1
fi
fi

# Checkout the latest stable version of NVM
if ! git -C "$HOME/.nvm" checkout v0.40.1; then
echo 'VITO_SSH_ERROR' && exit 1
fi

# Load NVM
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# Define the NVM initialization script
NVM_INIT='
export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"
'

# List of potential configuration files
CONFIG_FILES=("$HOME/.bash_profile" "$HOME/.bash_login" "$HOME/.profile")

# Flag to track if at least one file exists
FILE_EXISTS=false

# Loop through each configuration file and check if it exists
for config_file in "${CONFIG_FILES[@]}"; do
if [ -f "$config_file" ]; then
FILE_EXISTS=true
# Check if the NVM initialization is already present
if ! grep -q 'export NVM_DIR="$HOME/.nvm"' "$config_file"; then
echo "Adding NVM initialization to $config_file"
echo "$NVM_INIT" >> "$config_file"
else
echo "NVM initialization already exists in $config_file"
fi
fi
done

# If no file exists, fallback to .profile
if [ "$FILE_EXISTS" = false ]; then
FALLBACK_FILE="$HOME/.profile"
echo "No configuration files found. Creating $FALLBACK_FILE and adding NVM initialization."
echo "$NVM_INIT" >> "$FALLBACK_FILE"
fi

echo "NVM initialization process completed."

# Install NVM if not already installed
if ! command -v nvm > /dev/null 2>&1; then
if ! bash "$HOME/.nvm/install.sh"; then
echo 'VITO_SSH_ERROR' && exit 1
fi
fi

# Install the requested Node.js version
if ! nvm install __version__; then
echo 'VITO_SSH_ERROR' && exit 1
fi

echo "Node.js version __version__ installed successfully!"

echo "Node version:" && node -v
echo "NPM version:" && npm -v
echo "NPX version:" && npx -v
Loading

0 comments on commit 924920e

Please sign in to comment.