Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ Homestead.yaml
npm-debug.log
yarn-error.log
.idea
cloudcli-server-options.json
8 changes: 4 additions & 4 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM php:7.1-apache
FROM php:7.4-apache

# https://github.com/composer/getcomposer.org/commits/master
ENV COMPOSER_COMMIT=cb19f2aa3aeaa2006c0cd69a7ef011eb31463067
# https://github.com/composer/getcomposer.org/commits/main
ENV COMPOSER_COMMIT=30a8caf5d8e7c4069e243c378d01c7e3a3967bcc
RUN curl -s "https://raw.githubusercontent.com/composer/getcomposer.org/${COMPOSER_COMMIT}/web/installer" | php &&\
mv composer.phar /usr/local/bin/composer && chmod +x /usr/local/bin/composer
RUN apt-get update && apt-get install -y git unzip libzip-dev && docker-php-ext-install zip bcmath
Expand All @@ -16,7 +16,7 @@ RUN chown -R 1000:1000 /home/cloudwm

USER cloudwm
WORKDIR /home/cloudwm
RUN composer install --no-autoloader --no-scripts --no-suggest --no-interaction --no-plugins
RUN composer install --no-autoloader --no-scripts --no-interaction --no-plugins

USER root
RUN sed -ri -e 's!/var/www/html!/home/cloudwm/public!g' /etc/apache2/sites-available/*.conf
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile.dev
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
FROM php:7.1
FROM php:7.4

# https://github.com/composer/getcomposer.org/commits/master
ENV COMPOSER_COMMIT=cb19f2aa3aeaa2006c0cd69a7ef011eb31463067
# https://github.com/composer/getcomposer.org/commits/main
ENV COMPOSER_COMMIT=30a8caf5d8e7c4069e243c378d01c7e3a3967bcc
RUN curl -s "https://raw.githubusercontent.com/composer/getcomposer.org/${COMPOSER_COMMIT}/web/installer" | php &&\
mv composer.phar /usr/local/bin/composer && chmod +x /usr/local/bin/composer
RUN apt-get update && apt-get install -y git unzip libzip-dev && docker-php-ext-install zip bcmath
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

Server-side component to support [cloudcli](https://github.com/cloudwm/cloudcli)

## Running the server locally
## Running the server with Docker

Build

Expand Down
10 changes: 8 additions & 2 deletions app/Cloudcli/BaseServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,14 @@ function getServerCommands($schemaCommand=null, $commands=null) {
return Schema::getServerCommands($schemaCommand, $commands);
}

function getSchema() {
return Schema::getSchema();
function getSchema($request) {
$supports = $request->input("supports");
if ($supports) {
$supports = explode(",", $supports);
} else {
$supports = [];
}
return Schema::getSchema($supports);
}

function root() {
Expand Down
4 changes: 0 additions & 4 deletions app/Cloudcli/BaseServerRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,6 @@ static function handleSvcRequest(Request $request, $server) {
}

static function handleRequest($request, $command, $server) {
if ($command["cmd"] == "_") {
// special server-only commands, must be called without a request
$request = null;
}
try {
$res = call_user_func([$server, $command['method']], $request, $command);
} catch (Exception $e) {
Expand Down
76 changes: 76 additions & 0 deletions app/Cloudcli/Common.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
<?php

namespace App\Cloudcli;

use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Psy\Formatter\TraceFormatter;
use Illuminate\Support\Facades\Log;

class Common
{
static function returnParseFlagsErrors($errors) {
$messages = [];
foreach ($errors as $name => $error) {
$messages[] = "{$name}: {$error}";
}
return ["error" => true, "message" => join("\n", $messages)];
}

static function parseFlags($request, $command) {
$values = [];
$errors = [];
foreach ($command["schemaCommand"]["flags"] as $flag) {
$value = $request->input($flag["name"]);
if (is_null($value)) {
$value = "";
}
if (Arr::get($flag, "required") && $value === "") {
$errors[$flag["name"]] = "required";
} else {
if (Arr::get($flag, "string_to_uppercase")) {
$value = Str::upper($value);
}
if (Arr::get($flag, "type") == "integer") {
if ((string)(int) $value == (string) $value) {
$value = (int) $value;
} else {
$errors[$flag["name"]] = "invalid value ('$value'), must be an integer";
}
}
$values[$flag["name"]] = $value;
}
}
return [$values, $errors];
}

static function handleException(\Throwable $e, $message="Unexpected error, please try again later") {
Log::error(join("\n", TraceFormatter::formatTrace($e)));
Log::error($e->getMessage());
return ["error" => true, "message" => $message];
}

static function info(string $message, array $context = []) {
Log::info($message, $context);
}

static function renderServerPath($path, $values) {
foreach ($values as $key => $value) {
$path = str_replace("<{$key}>", $value, $path);
}
return $path;
}

static function requestHandler($request, $command, $callback) {
try {
[$values, $errors] = Common::parseFlags($request, $command);
if (count($errors) > 0) {
return Common::returnParseFlagsErrors($errors);
} else {
return call_user_func($callback, $values);
}
} catch (\Exception $e) {
return Common::handleException($e);
}
}
}
123 changes: 123 additions & 0 deletions app/Cloudcli/Network.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
<?php

namespace App\Cloudcli;

use Illuminate\Support\Arr;

class Network
{
static function postSimpleJson($request, $command) {
return Common::requestHandler($request, $command, function($values) use ($request, $command) {
$path = Common::renderServerPath($command["schemaCommand"]["run"]["serverPath"], $values);
foreach ($command["schemaCommand"]["run"]["postSimpleJson"]["unsetFields"] as $field) {
unset($values[$field]);
}
return self::apiPostSimpleJson(
$request, $path, $values,
$command["schemaCommand"]["run"]["postSimpleJson"]["successMessage"],
Arr::get($command, "schemaCommand.run.postSimpleJson.httpMethod", "POST")
);
});
}

static function getListItems($request, $command) {
return Common::requestHandler($request, $command, function($values) use ($request, $command) {
$path = Common::renderServerPath($command["schemaCommand"]["run"]["serverPath"], $values);
return self::apiGetListItems(
$request, $path, $command, $values,
$command["schemaCommand"]["run"]["getListItems"]["itemsKey"],
$command["schemaCommand"]["run"]["getListItems"]["maxItems"]
);
});
}

static function apiGetListItems($request, $path, $command, $values, $itemsKey, $maxItems) {
$res = ProxyServerHttp::getHttpClient($request);
if ($res["error"]) {
throw new \Exception($res);
} else {
$res = $res["client"]->request("GET", $path);
$body = (string)$res->getBody();
if (empty($body) && $res->getStatusCode() == 200) {
return [];
} else {
$decoded_response = json_decode($body, true);
$last_error = (json_last_error() == JSON_ERROR_NONE) ? null : json_last_error_msg();
if (!$last_error && $res->getStatusCode() == 200) {
if (!Arr::exists($decoded_response, $itemsKey)) {
return [
"error" => true,
"message" => "Unexpected response ($decoded_response)"
];
} else {
$items = Arr::get($decoded_response, $itemsKey);
$countItems = count($items);
if ($countItems > $maxItems) {
return [
"error" => true,
"message" => "Invalid response (maxItems exceeded: {$countItems})"
];
} else {
$resItems = [];
foreach ($items as $item) {
$resItem = [];
foreach ($command["schemaCommand"]["run"]["fields"] as $field) {
$fieldName = $field["name"];
if (Arr::exists($item, $fieldName)) {
$value = $item[$fieldName];
} else {
$value = Arr::get($values, $fieldName);
}
$resItem[$fieldName] = $value;
}
$resItems[] = $resItem;
}
return $resItems;
}
}
} else {
return [
"error" => true,
"message" => json_encode($decoded_response)
];
}
}
}
}

static function apiPostSimpleJson($request, $path, $data, $successMessage=null, $httpMethod="POST") : array {
$res = ProxyServerHttp::getHttpClient($request);
if ($res["error"]) {
throw new \Exception($res);
} else {
$requestArgs = [];
if (count($data) > 0) {
$requestArgs["json"] = $data;
}
Common::info($path, [$httpMethod, $requestArgs]);
$res = $res["client"]->request($httpMethod, $path, $requestArgs);
$body = (string) $res->getBody();
if (empty($body) && $res->getStatusCode() == 200) {
$res = [];
} else {
$decoded_response = json_decode($body, true);
$last_error = (json_last_error() == JSON_ERROR_NONE) ? null : json_last_error_msg();
if (!$last_error && $res->getStatusCode() == 200) {
$res = $decoded_response;
} else {
$res = [
"error" => true,
"message" => json_encode($decoded_response)
];
}
}
if ($successMessage && !Arr::get($res, "error") && !Arr::get($res, "errors") && !Arr::get($res, "message")) {
$res = [
"message" => $successMessage,
"res" => json_encode($res)
];
}
return $res;
}
}
}
18 changes: 13 additions & 5 deletions app/Cloudcli/ProxyServer.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,25 @@
namespace App\Cloudcli;

use Exception;
use GuzzleHttp\Client;
use GuzzleHttp\Psr7\Response;
use GuzzleHttp\RequestOptions;
use http\Exception\InvalidArgumentException;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;
use Throwable;

class ProxyServer extends BaseServer {

public function __call($method, $args) {
$staticCallMethod = Arr::get($args[1], "schemaCommand.run.proxyServerStaticCallMethod");
if ($staticCallMethod) {
[$className, $methodName] = $staticCallMethod;
switch ($className) {
case "Network": $class = Network::class; break;
default: throw new \Exception("Invalid className: $className");
}
return call_user_func_array([$class, $methodName], $args);
} else {
return $this->get($args[0], $args[1]);
}
}

function listServers($request, $command) {
return $this->get($request, $command);
Expand Down
10 changes: 10 additions & 0 deletions app/Cloudcli/ProxyServerGet.php
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,13 @@ static function get(Request $request, $command) {
$arrayFlag = $flagName;
} elseif (Arr::get($flag, 'bool')) {
$boolFlags[] = $flagName;
} else {
if (Arr::get($flag, "required") && empty($request->input($flagName))) {
return ["error" => true, "message" => "$flagName is required", "status_code" => 400];
}
if (strstr($serverPath, "<" . $flagName . ">")) {
$serverPath = str_replace("<" . $flagName . ">", $request->input($flagName), $serverPath);
}
}
}
}
Expand Down Expand Up @@ -94,6 +101,9 @@ static function get(Request $request, $command) {
$res = ProxyServerHttp::parseClientResponse(
$res["client"]->get($serverPath)
);
if (Arr::get($schemaCommand, 'run.serverParseListItems')) {
$res = $res[$schemaCommand["run"]["serverParseListItems"]];
}
if (Arr::get($res, 'error') && Arr::get($res, 'response')) {
return $res['response'];
} else {
Expand Down
Loading