v1.0 Initial commit of project
This commit is contained in:
748
pub/api/classes/API.php
Normal file
748
pub/api/classes/API.php
Normal file
@@ -0,0 +1,748 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
class API
|
||||
{
|
||||
public $conn;
|
||||
|
||||
# The user uuid that requested the API
|
||||
protected $user_uuid;
|
||||
|
||||
# $user_type is either an API call (api) or an call from the frontend (frontend)
|
||||
protected $user_type;
|
||||
|
||||
# Either GET POST PUT or DELETE
|
||||
public $request_method;
|
||||
|
||||
protected $content_type;
|
||||
|
||||
# The original posted data to the API, this data is NOT sanitized and validated, never use this data for queries!
|
||||
public $postedData;
|
||||
|
||||
# The validated and sanitized data can be uses for the API actions
|
||||
public $data;
|
||||
|
||||
# The permission of the user to check if the action is allowed.
|
||||
public $permissions;
|
||||
|
||||
# The return url that the frontend request will forward to after the api call is done. if set to false it will only output
|
||||
# the json response with an http code. API calls always respond with json. $return_url can be set to supply the form with an input
|
||||
# with the name _return and value of the url to return to.
|
||||
public $return_url;
|
||||
|
||||
# Required fields & optional fields set by the API actions. This is an array like:
|
||||
# Example:
|
||||
# 'user_uuid' => ['type' => 'string', 'min' => 5, 'max' => 36],
|
||||
# 'user_enabled' => ['type' => 'int', 'min' => 0, 'max' => 99],
|
||||
# 'user_active' => ['type' => 'enum', 'values' => ['active', 'inactive', 'banned']],
|
||||
# 'user_email' => ['type' => 'string', 'format' => 'email'],
|
||||
private $requiredFields = [];
|
||||
private $optionalFields = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
# Setup Database connection
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/bin/php/db_connect.php';
|
||||
$this->conn = $GLOBALS['conn'];
|
||||
|
||||
if (!empty($_SESSION['user']['user_uuid'])) {
|
||||
$this->InitUserTypeFrontend();
|
||||
} else {
|
||||
$this->InitUserTypeAPI();
|
||||
}
|
||||
|
||||
$this->return_url = $this->setReturnUrl();
|
||||
|
||||
# user_uuid will be set if the user is authorized
|
||||
if (!$this->user_uuid) {
|
||||
$this->apiOutput(401, ['error' => 'Unauthorized']);
|
||||
}
|
||||
|
||||
# Only allow POST, GET, PUT and DELETE
|
||||
if (!$this->checkRequestMethod()) {
|
||||
$this->apiOutput(405, ['error' => 'Method not allowed']);
|
||||
}
|
||||
|
||||
|
||||
if (!$this->checkContentType()) {
|
||||
$this->apiOutput(400, ['error' => 'Unsupported Content-Type.']);
|
||||
}
|
||||
|
||||
if ($this->content_type === 'application/json') {
|
||||
if (!$this->checkJson()) {
|
||||
$this->apiOutput(400, ['error' => 'Invalid JSON format']);
|
||||
}
|
||||
}
|
||||
|
||||
// Disable builder input for non-GET requests to prevent potential SQL injection vulnerabilities.
|
||||
// Also disable the builder for users with the 'frontend' user type as an extra security measure.
|
||||
// The builder should only be active for API users making GET requests.
|
||||
// When building a frontend page, you can still programmatically construct a builder array
|
||||
// and set it via $_GET like so after the API class creation:
|
||||
// $_GET['builder'] = [1 => ['where' => [0 => 'permission_uuid', 1 => $permission_uuid]]];
|
||||
if ($this->request_method !== 'GET' || $this->user_type === 'frontend') {
|
||||
$this->disableBuilder();
|
||||
}
|
||||
|
||||
# This converts the posted data if needed to an PHP array
|
||||
$this->postedData = $this->processPostedData();
|
||||
|
||||
}
|
||||
|
||||
private function InitUserTypeFrontend()
|
||||
{
|
||||
$this->user_uuid = $_SESSION['user']['user_uuid'];
|
||||
$this->user_type = 'frontend';
|
||||
|
||||
# Load the locale for the user, this is used for the return message in the frontend and other globalFunctions.
|
||||
include_once $_SERVER['DOCUMENT_ROOT'] . '/bin/php/Functions/globalFunctions.php';
|
||||
$locale = getPreferredLocale();
|
||||
global $translations;
|
||||
$translations = require $_SERVER['DOCUMENT_ROOT'] . "/bin/locales/{$locale}.php";
|
||||
}
|
||||
|
||||
protected function RecursiveDeleteFolder($folderPath): bool
|
||||
{
|
||||
// Check if the folder exists
|
||||
if (!is_dir($folderPath)) {
|
||||
$this->apiOutput(500, ['error' => 'directory not found: ' . $folderPath]);
|
||||
}
|
||||
|
||||
// Get all files and folders in the directory
|
||||
$items = array_diff(scandir($folderPath), array('.', '..'));
|
||||
|
||||
// Loop through each item
|
||||
foreach ($items as $item) {
|
||||
|
||||
$itemPath = $folderPath . DIRECTORY_SEPARATOR . $item;
|
||||
|
||||
if (is_dir($itemPath)) {
|
||||
if (!$this->RecursiveDeleteFolder($itemPath)) {
|
||||
$this->apiOutput(500, ['error' => "Unable to remove directory: $itemPath"]);
|
||||
}
|
||||
} else {
|
||||
if (!unlink($itemPath)) {
|
||||
$this->apiOutput(500, ['error' => "Unable to delete file: $itemPath"]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// Remove the main folder after all contents are gone
|
||||
if (!rmdir($folderPath)) {
|
||||
$this->apiOutput(500, ['error' => "Unable to remove directory: $folderPath"]);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function InitUserTypeAPI()
|
||||
{
|
||||
$this->user_type = 'api';
|
||||
|
||||
$headers = getallheaders();
|
||||
$authHeader = $headers['Authorization'] ?? '';
|
||||
|
||||
if (!preg_match('/^Bearer\s+(.+)$/', $authHeader, $matches)) {
|
||||
$this->apiOutput(401, ['error' => 'Unauthorized, missing bearer token.']);
|
||||
}
|
||||
|
||||
$bearerToken = trim($matches[1]);
|
||||
|
||||
if (!preg_match('/^[a-f0-9\-]{36}\.[a-f0-9]{64}$/i', $bearerToken)) {
|
||||
$this->apiOutput(401, ['error' => 'Unauthorized, invalid token format.']);
|
||||
}
|
||||
|
||||
[$tokenId, $tokenSecret] = explode('.', $bearerToken, 2);
|
||||
|
||||
$this->user_uuid = $this->validateToken($tokenId, $tokenSecret);
|
||||
|
||||
if ($this->user_uuid === false) {
|
||||
$this->apiOutput(401, ['error' => 'Unauthorized, invalid or expired token.']);
|
||||
}
|
||||
|
||||
$api_token_last_used_timestamp = time();
|
||||
$stmt = $this->conn->prepare("UPDATE vc_api_tokens SET api_token_last_used_timestamp = ? WHERE api_token_uuid = ?");
|
||||
$stmt->bind_param("is", $api_token_last_used_timestamp, $tokenId);
|
||||
$stmt->execute();
|
||||
}
|
||||
|
||||
public function validateSingleData($value, $rules)
|
||||
{
|
||||
if (!$this->validateField($value, $rules)) {
|
||||
$this->apiOutput(400, ['error' => "Invalid value: $value"]);
|
||||
}
|
||||
|
||||
return $this->sanitizeData($value, $rules['type']);
|
||||
}
|
||||
|
||||
public function validateData($requiredFields, $optionalFields = [])
|
||||
{
|
||||
$inputData = $this->postedData;
|
||||
|
||||
$this->requiredFields = $requiredFields;
|
||||
$this->optionalFields = $optionalFields;
|
||||
$sanitizedData = [];
|
||||
|
||||
foreach ($this->requiredFields as $field => $rules) {
|
||||
|
||||
if (!array_key_exists($field, $inputData)) {
|
||||
$this->apiOutput(400, ['error' => "Missing required field: $field"]);
|
||||
}
|
||||
|
||||
$value = $inputData[$field];
|
||||
|
||||
if (!$this->validateField($value, $rules)) {
|
||||
$this->apiOutput(400, ['error' => "Invalid value for $field"]);
|
||||
}
|
||||
|
||||
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
||||
}
|
||||
|
||||
|
||||
// Check optional fields
|
||||
foreach ($this->optionalFields as $field => $rules) {
|
||||
if (isset($inputData[$field])) {
|
||||
$value = $inputData[$field];
|
||||
|
||||
if (!$this->validateField($value, $rules)) {
|
||||
$this->apiOutput(422, ['error' => "Invalid value for optional field: $field"]);
|
||||
}
|
||||
|
||||
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
||||
}
|
||||
}
|
||||
|
||||
if (isset($_GET['builder']) && is_array($_GET['builder'])) {
|
||||
foreach ($_GET['builder'] as $builder) {
|
||||
if (!isset($builder['where']) || count($builder['where']) !== 2) {
|
||||
continue; // skip invalid builders
|
||||
}
|
||||
|
||||
$field = $builder['where'][0];
|
||||
$value = $builder['where'][1];
|
||||
|
||||
// Check if the field is allowed (in required or optional)
|
||||
$rules = $requiredFields[$field] ?? $optionalFields[$field] ?? null;
|
||||
if (!$rules) {
|
||||
$this->apiOutput(403, ['error' => "Field not allowed in query: $field"]);
|
||||
}
|
||||
|
||||
// Validate and sanitize
|
||||
if (!$this->validateField($value, $rules)) {
|
||||
$this->apiOutput(422, ['error' => "Invalid value for builder field: $field"]);
|
||||
}
|
||||
|
||||
$sanitizedData[$field] = $this->sanitizeData($value, $rules['type']);
|
||||
}
|
||||
}
|
||||
|
||||
$this->data = $sanitizedData;
|
||||
}
|
||||
|
||||
private function isValidLength($value, $rules)
|
||||
{
|
||||
$length = strlen($value);
|
||||
if (isset($rules['min']) && $length < $rules['min']) return false;
|
||||
if (isset($rules['max']) && $length > $rules['max']) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private function isValidNumberRange($value, $rules)
|
||||
{
|
||||
if (isset($rules['min']) && $value < $rules['min']) return false;
|
||||
if (isset($rules['max']) && $value > $rules['max']) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private function validateField($value, $rules)
|
||||
{
|
||||
switch ($rules['type']) {
|
||||
case 'string':
|
||||
if (!is_string($value)) return false;
|
||||
return $this->isValidLength($value, $rules);
|
||||
case 'slugify':
|
||||
if (!is_string($value) || !preg_match('/^[a-z0-9]+(-[a-z0-9]+)*$/', $value)) return false;
|
||||
return $this->isValidLength($value, $rules);
|
||||
|
||||
case 'boolean':
|
||||
if (is_bool($value)) return true;
|
||||
|
||||
if (is_string($value)) {
|
||||
$value = strtolower($value);
|
||||
return $value === 'true' || $value === 'false' || $value === '1' || $value === '0';
|
||||
}
|
||||
|
||||
if (is_int($value)) {
|
||||
return $value === 1 || $value === 0;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case 'email':
|
||||
if (!is_string($value)) return false;
|
||||
if (!filter_var($value, FILTER_VALIDATE_EMAIL)) return false;
|
||||
return $this->isValidLength($value, $rules);
|
||||
|
||||
case 'password':
|
||||
if (!is_string($value)) return false;
|
||||
return $this->isValidLength($value, $rules);
|
||||
|
||||
case 'html':
|
||||
if (!is_string($value)) return false;
|
||||
return $this->isValidLength($value, $rules);
|
||||
|
||||
case 'int':
|
||||
if (!is_int($value) && !ctype_digit($value)) return false;
|
||||
$value = (int)$value;
|
||||
return $this->isValidNumberRange($value, $rules);
|
||||
|
||||
case 'float':
|
||||
// Accept floats or numeric strings
|
||||
if (!is_float($value) && !is_numeric($value)) {
|
||||
return false;
|
||||
}
|
||||
$value = (float)$value;
|
||||
return $this->isValidNumberRange($value, $rules);
|
||||
|
||||
case 'timestamp':
|
||||
if (is_null($value)) return true;
|
||||
if (!is_int($value) && !ctype_digit($value)) return false;
|
||||
$value = (int)$value;
|
||||
if ($value < 0) return false;
|
||||
$min = $rules['min'] ?? 1;
|
||||
$max = $rules['max'] ?? 4102444800;
|
||||
return $value >= $min && $value <= $max;
|
||||
|
||||
case 'enum':
|
||||
if (!isset($rules['values']) || !in_array($value, $rules['values'], true)) return false;
|
||||
return true;
|
||||
|
||||
case 'uuid':
|
||||
if (!is_string($value)) return false;
|
||||
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value);
|
||||
|
||||
case 'base64':
|
||||
if (!is_string($value)) return false;
|
||||
return base64_encode(base64_decode($value, true)) === $value;
|
||||
|
||||
case 'uuid':
|
||||
if (!is_string($value)) return false;
|
||||
return preg_match('/^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i', $value);
|
||||
|
||||
case 'json':
|
||||
if (!is_string($value)) return false;
|
||||
json_decode($value);
|
||||
return json_last_error() === JSON_ERROR_NONE;
|
||||
|
||||
case 'array':
|
||||
if (!is_array($value)) return false;
|
||||
return $value;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private function sanitizeData($value, $type)
|
||||
{
|
||||
switch ($type) {
|
||||
case 'string':
|
||||
case 'enum':
|
||||
case 'uuid':
|
||||
// Remove HTML tags and encode special characters
|
||||
return htmlspecialchars(strip_tags($value), ENT_QUOTES, 'UTF-8');
|
||||
|
||||
case 'email':
|
||||
// Remove illegal characters from email address
|
||||
return filter_var($value, FILTER_SANITIZE_EMAIL);
|
||||
|
||||
case 'password':
|
||||
// Passwords may contain special characters; just trim spaces
|
||||
return trim($value);
|
||||
|
||||
case 'html':
|
||||
// Allow safe HTML, you can customize allowed tags
|
||||
return strip_tags($value, '<b><i><u><strong><em><p><br>');
|
||||
|
||||
case 'int':
|
||||
// Remove anything that's not a number
|
||||
return filter_var($value, FILTER_SANITIZE_NUMBER_INT);
|
||||
|
||||
case 'base64':
|
||||
// Only allow base64 valid characters
|
||||
return preg_replace('/[^a-zA-Z0-9\/\+=]/', '', $value);
|
||||
|
||||
case 'boolean':
|
||||
if (is_string($value)) {
|
||||
$value = strtolower(trim($value));
|
||||
return in_array($value, ['true', '1'], true) ? true : false;
|
||||
}
|
||||
|
||||
return (bool)$value;
|
||||
|
||||
default:
|
||||
// Return as-is if unknown type
|
||||
return $value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function checkPermissions($permission_name, $accessRightsRequired, $returnBoolean = false)
|
||||
{
|
||||
$accessLevels = [
|
||||
'NA' => 0, // No Access
|
||||
'RO' => 1, // Read Only
|
||||
'RW' => 2, // Read Write
|
||||
];
|
||||
$query = "SELECT
|
||||
vc_permissions.permission_name,
|
||||
vc_user_group_permissions_portal.permission_value
|
||||
FROM vc_user_group_permissions_portal
|
||||
INNER JOIN vc_permissions ON vc_user_group_permissions_portal.permission_uuid =vc_permissions.permission_uuid
|
||||
INNER JOIN vc_users ON vc_user_group_permissions_portal.user_group_uuid = vc_users.user_group_uuid
|
||||
WHERE user_uuid = ? AND permission_name = ?";
|
||||
|
||||
|
||||
$stmt = $this->conn->prepare($query);
|
||||
$stmt->bind_param("ss", $this->user_uuid, $permission_name);
|
||||
$stmt->execute();
|
||||
$result = $stmt->get_result()->fetch_assoc();
|
||||
|
||||
if (!$result) {
|
||||
if ($returnBoolean) {
|
||||
return false;
|
||||
}
|
||||
$this->apiOutput(500, ['error' => 'Did not find permission required']);
|
||||
}
|
||||
|
||||
$userAccess = $result['permission_value'];
|
||||
|
||||
if (!isset($accessLevels[$userAccess]) || !isset($accessLevels[$accessRightsRequired])) {
|
||||
if ($returnBoolean) {
|
||||
return false;
|
||||
}
|
||||
$this->apiOutput(500, ['error' => 'Server error.']);
|
||||
}
|
||||
|
||||
// Compare user's access level with the required access level
|
||||
if ($accessLevels[$userAccess] < $accessLevels[$accessRightsRequired]) {
|
||||
if ($returnBoolean) {
|
||||
return false;
|
||||
}
|
||||
$this->apiOutput(403, ['error' => 'Permission denied. You do not have the required access level.']);
|
||||
}
|
||||
|
||||
if ($returnBoolean) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
protected function setReturnUrl()
|
||||
{
|
||||
if ($this->user_type !== 'frontend') {
|
||||
return false;
|
||||
}
|
||||
|
||||
$method = $_SERVER['REQUEST_METHOD'];
|
||||
|
||||
if ($method === 'POST' && isset($_POST['_return'])) {
|
||||
return $_POST['_return'];
|
||||
}
|
||||
|
||||
if ($method === 'PUT') {
|
||||
parse_str(file_get_contents("php://input"), $putData);
|
||||
if (isset($putData['_return'])) {
|
||||
return $putData['_return'];
|
||||
}
|
||||
}
|
||||
if ($method === 'GET') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $_SERVER['HTTP_REFERER'];
|
||||
}
|
||||
|
||||
protected function checkRequestMethod()
|
||||
{
|
||||
$allowedMethods = ['GET', 'POST', 'PUT', 'DELETE'];
|
||||
$method = $_SERVER['REQUEST_METHOD'] ?? '';
|
||||
|
||||
if (!in_array($method, $allowedMethods)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
# Since browser doesnt allow DELETE or PUTs from the frontend forms (apart from some javascript/ajax fuckery)
|
||||
# we need to check the _method POST value.
|
||||
if ($this->user_type === 'frontend' && $method === 'POST' && isset($_POST['_method'])) {
|
||||
$overrideMethod = strtoupper($_POST['_method']);
|
||||
|
||||
if (in_array($overrideMethod, ['PUT', 'DELETE'])) {
|
||||
$this->request_method = $overrideMethod;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->request_method = $method;
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function checkJson()
|
||||
{
|
||||
$rawInput = file_get_contents('php://input');
|
||||
if (empty($rawInput)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
json_decode($rawInput, true);
|
||||
if (json_last_error() !== JSON_ERROR_NONE) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected function processPostedData()
|
||||
{
|
||||
if ($this->user_type === 'api') {
|
||||
return json_decode(file_get_contents("php://input"), true);
|
||||
}
|
||||
|
||||
switch ($this->request_method) {
|
||||
case 'GET':
|
||||
return $_GET;
|
||||
case 'POST':
|
||||
return $_POST;
|
||||
case 'PUT':
|
||||
case 'DELETE':
|
||||
# When an image is uploaded from the front end the data needs to be specified its in $_POST and not $_FILES
|
||||
if ($this->content_type === 'multipart/form-data') {
|
||||
return $_POST;
|
||||
} else {
|
||||
parse_str(file_get_contents("php://input"), $data);
|
||||
return $data;
|
||||
}
|
||||
|
||||
default:
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
protected function validateToken(string $tokenId, string $tokenSecret)
|
||||
{
|
||||
$stmt = $this->conn->prepare("SELECT user_uuid, api_token FROM vc_api_tokens WHERE api_token_uuid = ? AND api_token_expiration_timestamp > UNIX_TIMESTAMP()");
|
||||
$stmt->bind_param("s", $tokenId);
|
||||
$stmt->execute();
|
||||
|
||||
$row = $stmt->get_result()->fetch_assoc();
|
||||
|
||||
if (!$row) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!password_verify($tokenSecret, $row['api_token'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $row['user_uuid'];
|
||||
}
|
||||
|
||||
|
||||
protected function checkContentType()
|
||||
{
|
||||
# api will need to post with an application/json type Content type.
|
||||
# frontend will post with application/x-www-form-urlencoded content type but also is capable of application/json
|
||||
# frontend can also post multipart/form-data
|
||||
# GET requests dont have an content type
|
||||
|
||||
$contentType = $_SERVER['CONTENT_TYPE'] ?? '';
|
||||
|
||||
if ($this->request_method === 'GET') {
|
||||
$this->content_type = '';
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->user_type === 'api') {
|
||||
$this->content_type = 'application/json';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strpos($contentType, 'application/json') !== false) {
|
||||
$this->content_type = 'application/json';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strpos($contentType, 'application/x-www-form-urlencoded') !== false) {
|
||||
$this->content_type = 'application/x-www-form-urlencoded';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (strpos($contentType, 'multipart/form-data') !== false) {
|
||||
$this->content_type = 'multipart/form-data';
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUserUuid()
|
||||
{
|
||||
return $this->user_uuid;
|
||||
}
|
||||
|
||||
public function apiOutput($code = 200, $data = [], $frontendMessage = false)
|
||||
{
|
||||
if ($this->user_type === 'api') {
|
||||
http_response_code($code);
|
||||
header('Content-Type: application/json');
|
||||
if ($code === 200) {
|
||||
echo json_encode(reset($data));
|
||||
} else {
|
||||
echo json_encode($data);
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
|
||||
if ($this->user_type === 'frontend') {
|
||||
if (in_array($this->request_method, ['POST', 'PUT', 'DELETE'])) {
|
||||
http_response_code($code);
|
||||
|
||||
if ($this->return_url) { # sometimes the PUT doesnt need an return or response set (Think of js actions to api from frontend)
|
||||
$_SESSION['response'] = json_encode($data);
|
||||
|
||||
# When a request is successfull the api will recieve the data, the frontend needs a friendly message
|
||||
if ($frontendMessage) {
|
||||
$_SESSION['response'] = json_encode([key($data) => __($frontendMessage)]);
|
||||
}
|
||||
|
||||
header('Location: ' . $this->return_url);
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
header('Content-Type: application/json');
|
||||
echo json_encode($data);
|
||||
exit;
|
||||
}
|
||||
|
||||
exit;
|
||||
}
|
||||
|
||||
public function prepareStatement($query)
|
||||
{
|
||||
// Enable MySQLi to throw exceptions on errors
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
try {
|
||||
$stmt = $this->conn->prepare($query);
|
||||
|
||||
} catch (mysqli_sql_exception $e) {
|
||||
// If an error occurs during prepare, catch it and return a proper response
|
||||
$this->apiOutput(500, ['error' => 'Database error: ' . $e->getMessage()]);
|
||||
return null;
|
||||
}
|
||||
|
||||
return $stmt;
|
||||
|
||||
}
|
||||
|
||||
public function executeStatement($stmt)
|
||||
{
|
||||
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
|
||||
|
||||
try {
|
||||
$stmt->execute();
|
||||
return true;
|
||||
} catch (mysqli_sql_exception $e) {
|
||||
if ($e->getCode() === 1451) {
|
||||
$this->apiOutput(409, ['error' => 'Cannot delete record: dependent data exists.']);
|
||||
} else {
|
||||
$this->apiOutput(500, ['error' => 'Database error: ' . $e->getMessage()]);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public function isSuperuser()
|
||||
{
|
||||
$query = "SELECT * FROM vc_users WHERE vc_users.user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->user_uuid);
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
$user_data = $result->fetch_assoc();
|
||||
if ($user_data['user_email'] == 'superuser') {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected function buildDynamicQuery(string $tableName): array
|
||||
{
|
||||
$baseQuery = "SELECT * FROM " . $tableName;
|
||||
$whereClauses = [];
|
||||
$types = '';
|
||||
$values = [];
|
||||
|
||||
if (!isset($_GET['builder']) || !is_array($_GET['builder'])) {
|
||||
return [$baseQuery, $types, $values];
|
||||
}
|
||||
|
||||
foreach ($_GET['builder'] as $builder) {
|
||||
if (!isset($builder['where']) || !is_array($builder['where']) || count($builder['where']) !== 2) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$column = $builder['where'][0];
|
||||
$value = $builder['where'][1];
|
||||
|
||||
$whereClauses[] = "$column = ?";
|
||||
$types .= 's';
|
||||
$values[] = $value;
|
||||
}
|
||||
|
||||
if (!empty($whereClauses)) {
|
||||
$baseQuery .= " WHERE " . implode(" AND ", $whereClauses);
|
||||
}
|
||||
|
||||
return [$baseQuery, $types, $values];
|
||||
}
|
||||
|
||||
protected function generalGetFunction($query, $types, $params, $returnBoolean, $itemName)
|
||||
{
|
||||
$stmt = $this->prepareStatement($query);
|
||||
|
||||
if (!empty($params)) {
|
||||
$stmt->bind_param($types, ...$params);
|
||||
}
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
if (!$returnBoolean) {
|
||||
$this->apiOutput(404, ['error' => $itemName . ' not found.']);
|
||||
}
|
||||
}
|
||||
|
||||
$tokens = [];
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$tokens[] = $row;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function disableBuilder(): void
|
||||
{
|
||||
if (isset($_GET['builder'])) {
|
||||
unset($_GET['builder']);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
108
pub/api/classes/API_apitoken.php
Normal file
108
pub/api/classes/API_apitoken.php
Normal file
@@ -0,0 +1,108 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_apitoken extends API
|
||||
{
|
||||
public function getTokens()
|
||||
{
|
||||
$query = "SELECT * FROM vc_api_tokens WHERE vc_api_tokens.user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['user_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$result = $stmt->get_result();
|
||||
$tokens = [];
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$tokens[] = $row;
|
||||
}
|
||||
|
||||
return $tokens;
|
||||
}
|
||||
|
||||
public function createNewToken()
|
||||
{
|
||||
|
||||
$api_token = bin2hex(random_bytes(64 / 2));
|
||||
|
||||
|
||||
$api_token_hash = password_hash($api_token, PASSWORD_BCRYPT, ["cost" => 12]);
|
||||
$api_token_expiration_timestamp = strtotime('+1 year');
|
||||
$query = "INSERT INTO vc_api_tokens (api_token_uuid, user_uuid, api_token, api_token_expiration_timestamp, api_token_created_timestamp) VALUES (UUID(), ?, ?, ?, ?)";
|
||||
|
||||
$stmt = $this->prepareStatement($query);
|
||||
|
||||
$stmt->bind_param('ssii', $this->data['user_uuid'], $api_token_hash, $api_token_expiration_timestamp, time());
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$result = $this->getNewToken();
|
||||
$_SESSION['tmp_api_token'] = $api_token;
|
||||
|
||||
if ($result->num_rows > 0) {
|
||||
$api_token_data = $result->fetch_assoc();
|
||||
|
||||
$_SESSION['tmp_api_token'] = $api_token_data['api_token_uuid'] . '.' . $api_token;
|
||||
|
||||
$this->apiOutput(200, ['success' => $api_token_data], 'api_token_created');
|
||||
} else {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the token on the server.'], 'error_contact_support');
|
||||
}
|
||||
}
|
||||
|
||||
public function getNewToken()
|
||||
{
|
||||
$query = "SELECT * FROM vc_api_tokens WHERE user_uuid = ? ORDER BY api_token_created_timestamp DESC LIMIT 1";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['user_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
|
||||
public function getToken()
|
||||
{
|
||||
$query = "SELECT vc_users.user_email, vc_users.user_uuid FROM vc_api_tokens INNER JOIN vc_users ON vc_api_tokens.user_uuid = vc_users.user_uuid WHERE api_token_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['api_token_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
|
||||
if ($result->num_rows === 0) {
|
||||
$this->apiOutput(404, ['error' => 'API token not found.']);
|
||||
}
|
||||
|
||||
$api_token_data = $result->fetch_assoc();
|
||||
|
||||
return $api_token_data;
|
||||
}
|
||||
|
||||
public function deleteToken()
|
||||
{
|
||||
$query = "DELETE FROM vc_api_tokens WHERE api_token_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['api_token_uuid']);
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'API token deleted successfully.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function revokeToken()
|
||||
{
|
||||
|
||||
$api_token_revoked = ($this->data['api_token_revoked']) ? 1 : 0;
|
||||
|
||||
$query = "UPDATE vc_api_tokens SET api_token_revoked = ? WHERE api_token_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('is', $api_token_revoked, $this->data['api_token_uuid']);
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'API token ' . ($api_token_revoked ? 're' : 'en') . 'voked successfully.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
20
pub/api/classes/API_companies.php
Normal file
20
pub/api/classes/API_companies.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_companies extends API
|
||||
{
|
||||
public function updateCompanyState()
|
||||
{
|
||||
$query = "UPDATE companies SET company_state = ? WHERE company_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ss', $this->data['company_state'], $this->data['company_uuid']);
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'company state successfully updated']);
|
||||
}
|
||||
}
|
||||
}
|
||||
130
pub/api/classes/API_devices.php
Normal file
130
pub/api/classes/API_devices.php
Normal file
@@ -0,0 +1,130 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
use api\classes\imageProcessor;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_devices extends API
|
||||
{
|
||||
|
||||
public function getDevices($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_devices');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Device');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
private function getDeviceSlugify()
|
||||
{
|
||||
$query = "SELECT * FROM vc_devices WHERE device_slugify = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $this->data['device_slugify']);
|
||||
$this->executeStatement($stmt);
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
public function createDeviceImage($imageRestrictions)
|
||||
{
|
||||
try {
|
||||
# Main image
|
||||
$imageProcessor = new imageProcessor('device_image');
|
||||
$imageProcessor->imageRestrictions = $imageRestrictions;
|
||||
$imageProcessor->validateAndProcess();
|
||||
$ImageData = $imageProcessor->returnBase64image();
|
||||
} catch (Exception $e) {
|
||||
$this->apiOutput(401, ['error' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
return $ImageData;
|
||||
}
|
||||
|
||||
public function createDevice()
|
||||
{
|
||||
|
||||
if (isset($this->data['device_image'])) {
|
||||
$query = "INSERT INTO vc_devices (device_uuid, device_vendor_uuid, device_type, device_name, device_slugify, device_enabled, device_notes, device_eol, device_extensions, device_extra, device_create_timestamp, device_image, device_image_thumbnail)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssssisississ", $this->data['device_vendor_uuid'], $this->data['device_type'], $this->data['device_name'], $this->data['device_slugify'], $this->data['device_enabled'], $this->data['device_notes'], $this->data['device_eol'], $this->data['device_extensions'], $this->data['device_extra'], time(), $this->data['device_image'], $this->data['device_image_thumbnail']);
|
||||
} else {
|
||||
$query = "INSERT INTO vc_devices (device_uuid, device_vendor_uuid, device_type, device_name, device_slugify, device_enabled, device_notes, device_eol, device_extensions, device_extra, device_create_timestamp)
|
||||
VALUES (UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssssisissi", $this->data['device_vendor_uuid'], $this->data['device_type'], $this->data['device_name'], $this->data['device_slugify'], $this->data['device_enabled'], $this->data['device_notes'], $this->data['device_eol'], $this->data['device_extensions'], $this->data['device_extra'], time());
|
||||
}
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$stmt->close();
|
||||
|
||||
$result = $this->getDeviceSlugify();
|
||||
if ($result->num_rows === 0) {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the device.'], 'error_contact_support');
|
||||
}
|
||||
|
||||
$createDirsFailed = false;
|
||||
$dirsToCreate = array(
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/data/devices/" . $this->data['device_slugify'],
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/data/devices/" . $this->data['device_slugify'] . "/firmware",
|
||||
$_SERVER['DOCUMENT_ROOT'] . "/data/devices/" . $this->data['device_slugify'] . "/documents"
|
||||
);
|
||||
|
||||
foreach ($dirsToCreate as $dir) {
|
||||
if (!file_exists($dir)) {
|
||||
if (!mkdir($dir)) {
|
||||
$createDirsFailed = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($createDirsFailed) {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the device on the server.'], 'error_contact_support');
|
||||
}
|
||||
|
||||
$platform_data = $result->fetch_assoc();
|
||||
|
||||
$this->apiOutput(200, ['success' => $platform_data], 'item_added');
|
||||
}
|
||||
|
||||
public function updateDevice()
|
||||
{
|
||||
if (isset($this->data['device_image'])) {
|
||||
$query = "UPDATE vc_devices SET device_modified_timestamp = ?, device_vendor_uuid = ?, device_name = ?, device_enabled = ?, device_notes = ?, device_eol = ?, device_extensions = ?, device_extra = ?, device_image = ?, device_image_thumbnail = ? WHERE device_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("issisisssss", time(), $this->data['device_vendor_uuid'], $this->data['device_name'], $this->data['device_enabled'], $this->data['device_notes'], $this->data['device_eol'], $this->data['device_extensions'], $this->data['device_extra'], $this->data['device_image'], $this->data['device_image_thumbnail'], $this->data['device_uuid']);
|
||||
} else {
|
||||
$query = "UPDATE vc_devices SET device_modified_timestamp = ?, device_vendor_uuid = ?, device_name = ?, device_enabled = ?, device_notes = ?, device_eol = ?, device_extensions = ?, device_extra = ? WHERE device_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("issisisss", time(), $this->data['device_vendor_uuid'], $this->data['device_name'], $this->data['device_enabled'], $this->data['device_notes'], $this->data['device_eol'], $this->data['device_extensions'], $this->data['device_extra'], $this->data['device_uuid']);
|
||||
}
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Device updated successfully.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteDevice()
|
||||
{
|
||||
# check if the device exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'device_uuid', 1 => $this->data['device_uuid']]]];
|
||||
$device = $this->getDevices()[0];
|
||||
|
||||
# remove from database
|
||||
$query = "DELETE FROM vc_devices WHERE device_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $device['device_uuid']);
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
# Delete the device data folder.
|
||||
$dirsToDelete = $_SERVER['DOCUMENT_ROOT'] . "/data/devices/" . $device['device_slugify'];
|
||||
$this->RecursiveDeleteFolder($dirsToDelete);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Device removed successfully.']);
|
||||
}
|
||||
}
|
||||
478
pub/api/classes/API_inserve.php
Normal file
478
pub/api/classes/API_inserve.php
Normal file
@@ -0,0 +1,478 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_inserve extends API
|
||||
{
|
||||
|
||||
private $inserve_url;
|
||||
|
||||
private $inserve_token;
|
||||
|
||||
public $inserve_source_uuid;
|
||||
|
||||
private $ch;
|
||||
public $httpCode = false;
|
||||
|
||||
public $response = false;
|
||||
|
||||
private $cloudDistrubutor = 'digistate-servers';
|
||||
|
||||
public function setupConnection()
|
||||
{
|
||||
$query = "SELECT * FROM system_sources WHERE source_name = 'inserve'";
|
||||
$result = $this->conn->query($query)->fetch_assoc();
|
||||
|
||||
$this->inserve_url = $result['source_url'];
|
||||
$this->inserve_token = $result['source_auth_token'];
|
||||
$this->inserve_source_uuid = $result['source_uuid'];
|
||||
}
|
||||
|
||||
public function execCurl()
|
||||
{
|
||||
$this->response = curl_exec($this->ch);
|
||||
$this->httpCode = curl_getinfo($this->ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
curl_close($this->ch);
|
||||
}
|
||||
|
||||
public function returnResponse()
|
||||
{
|
||||
$this->apiOutput($this->httpCode, json_decode($this->response, true));
|
||||
}
|
||||
|
||||
public function authMe()
|
||||
{
|
||||
$this->ch = curl_init($this->inserve_url . 'auth/me');
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json"
|
||||
]
|
||||
]);
|
||||
$this->execCurl();
|
||||
}
|
||||
|
||||
public function getLinkedCompanies()
|
||||
{
|
||||
$this->ch = curl_init($this->inserve_url . 'cloud-distributors/digistate-servers/companies');
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json"
|
||||
]
|
||||
]);
|
||||
$this->execCurl();
|
||||
}
|
||||
|
||||
public function companies($page)
|
||||
{
|
||||
// Build array the way the API expects
|
||||
$params = [
|
||||
'b' => [
|
||||
['orderBy' => ['name', 'ASC']],
|
||||
['orderBy' => ['id', 'DESC']],
|
||||
['with' => ['operator', 'country']],
|
||||
['paginate' => 300],
|
||||
],
|
||||
'page' => $page
|
||||
];
|
||||
|
||||
$query = http_build_query($params);
|
||||
|
||||
$this->ch = curl_init($this->inserve_url . 'companies?' . $query);
|
||||
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json"
|
||||
]
|
||||
]);
|
||||
|
||||
$this->execCurl();
|
||||
|
||||
return json_decode($this->response, true);
|
||||
}
|
||||
|
||||
|
||||
public function syncCompaniesFromSentri()
|
||||
{
|
||||
# First retrieve all the active companies to sync to the Inserver cloud distributor
|
||||
$companies = [];
|
||||
|
||||
$sql = "SELECT company_source_id FROM companies WHERE company_state = 'active'";
|
||||
$stmt = $this->conn->query($sql);
|
||||
while ($row = $stmt->fetch_assoc()) {
|
||||
$id = (int)$row['company_source_id'];
|
||||
$companies[] = [
|
||||
'cloud_distribution_id' => (string)$id,
|
||||
'company_id' => $id
|
||||
];
|
||||
}
|
||||
|
||||
$url = $this->inserve_url . 'cloud-distributors/digistate-servers/companies';
|
||||
|
||||
$this->ch = curl_init($url);
|
||||
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($companies),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json",
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
]);
|
||||
|
||||
$this->execCurl();
|
||||
}
|
||||
|
||||
public function getCloudSubscriptions()
|
||||
{
|
||||
|
||||
$this->ch = curl_init($this->inserve_url . 'cloud-distribution-subscriptions/');
|
||||
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json"
|
||||
]
|
||||
]);
|
||||
|
||||
$this->execCurl();
|
||||
}
|
||||
|
||||
public function updateSubscription($subscriptionId = false, $payload = false)
|
||||
{
|
||||
$url = $this->inserve_url . 'cloud-distribution-subscriptions/' . $subscriptionId;
|
||||
|
||||
$this->ch = curl_init($url);
|
||||
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_CUSTOMREQUEST => 'PUT',
|
||||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json",
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
]);
|
||||
|
||||
$this->execCurl();
|
||||
}
|
||||
|
||||
private function getAllTypes($type)
|
||||
{
|
||||
$allowedColumns = [
|
||||
'server_licenses',
|
||||
'server_backup'
|
||||
];
|
||||
|
||||
if (!in_array($type, $allowedColumns, true)) {
|
||||
throw new Exception('Invalid column name');
|
||||
}
|
||||
|
||||
$query = "SELECT `$type` FROM servers";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$servers = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
array_push($servers, $row);
|
||||
}
|
||||
|
||||
$allTypes = [];
|
||||
foreach ($servers as $server) {
|
||||
if (!empty($server[$type])) {
|
||||
$types = json_decode($server[$type], true);
|
||||
if (is_array($types)) {
|
||||
foreach ($types as $item) {
|
||||
foreach ($item as $key => $value) {
|
||||
$allTypes[$key . '.' . $value] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return $allTypes;
|
||||
}
|
||||
|
||||
private function calculateTotalDiskUsage($diskJson)
|
||||
{
|
||||
$disks = json_decode($diskJson, true);
|
||||
$server_disks_count = 0;
|
||||
if (is_array($disks)) {
|
||||
foreach ($disks as $disk) {
|
||||
$server_disks_count += $disk['disk_space'];
|
||||
}
|
||||
}
|
||||
if (is_array($disks) && count($disks) > 0) {
|
||||
$sizes = array_column($disks, 'disk_space');
|
||||
$server_disks_count = array_sum($sizes);
|
||||
}
|
||||
return $server_disks_count;
|
||||
}
|
||||
|
||||
private function buildCountObject(string $serverUuid, string $key): array
|
||||
{
|
||||
return [
|
||||
'countSentri' => 0,
|
||||
'countInserve' => 0,
|
||||
'sentriCompanyId' => 0,
|
||||
'SentriStatus' => 0,
|
||||
'subscriptionInserveExists' => false,
|
||||
'subscriptionInserveId' => false,
|
||||
'subscriptionInserveCompanyId' => false,
|
||||
'subscriptionInserveName' => false,
|
||||
'subscriptionInserveStatus' => 0,
|
||||
'md5' => md5($serverUuid . ':' . $key),
|
||||
];
|
||||
}
|
||||
|
||||
private function transformTypes(array $types, string $serverUuid): array
|
||||
{
|
||||
$result = [];
|
||||
|
||||
foreach ($types as $key => $value) {
|
||||
$result[$key] = $this->buildCountObject($serverUuid, $key);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function buildCountArray($serverUuid)
|
||||
{
|
||||
$allBackupTypes = $this->getAllTypes('server_backup');
|
||||
$allLicenseTypes = $this->getAllTypes('server_licenses');
|
||||
|
||||
$backupCounts = $this->transformTypes($allBackupTypes, $serverUuid);
|
||||
$licenseCounts = $this->transformTypes($allLicenseTypes, $serverUuid);
|
||||
|
||||
return array_merge(
|
||||
[
|
||||
"server_CPU_count" => $this->buildCountObject($serverUuid, 'server_cpu_count'),
|
||||
"server_Memory_count" => $this->buildCountObject($serverUuid, 'server_memory_count'),
|
||||
"server_Disk_space_count" => $this->buildCountObject($serverUuid, 'server_disks_count'),
|
||||
],
|
||||
$licenseCounts,
|
||||
$backupCounts
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
public function syncServerLicencesToInserve()
|
||||
{
|
||||
# Get all the linked companies
|
||||
$this->getLinkedCompanies();
|
||||
$allCompanies = json_decode($this->response, true);
|
||||
$allCompaniesIds = array_column($allCompanies['matched'], 'id', 'company_id');
|
||||
|
||||
# first get the current subscriptions
|
||||
$this->getCloudSubscriptions();
|
||||
$allInserveSubscriptions = json_decode($this->response, true);
|
||||
|
||||
# Filter out all the none Sentri posted subscriptions based on the name for performance
|
||||
$allInserveSubscriptions = array_filter($allInserveSubscriptions, function ($subscription) {
|
||||
return isset($subscription['cloud_subscription_id']) && $subscription['cloud_subscription_id'] === 'sentri-servers';
|
||||
});
|
||||
|
||||
# Build lookup of existing Inserve subscriptions by cloud_distribution_id
|
||||
# this will be used later to lookup
|
||||
$inserveLookup = [];
|
||||
foreach ($allInserveSubscriptions as $subscription) {
|
||||
if (!empty($subscription['cloud_distribution_id'])) {
|
||||
$inserveLookup[$subscription['cloud_distribution_id']] = [
|
||||
'id' => (int)$subscription['id'],
|
||||
'quantity' => (int)$subscription['quantity'],
|
||||
'status' => (int)$subscription['status'],
|
||||
'cloud_distribution_company_id' => (int)$subscription['cloud_distribution_company_id'],
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
# get all the servers from Sentri
|
||||
$sql = "SELECT * FROM servers INNER JOIN companies ON servers.company_uuid = companies.company_uuid WHERE company_state = 'active' AND server_state != 'new' AND server_state != 'disabled' ";
|
||||
$stmt = $this->conn->query($sql);
|
||||
|
||||
while ($row = $stmt->fetch_assoc()) {
|
||||
# Create a count of all the Subscriptions possible with every count on 0
|
||||
$subscriptionCounts = $this->buildCountArray($row['server_uuid']);
|
||||
$totalDiskSpace = $this->calculateTotalDiskUsage($row['server_disks']);
|
||||
|
||||
# Inserve status codes are:
|
||||
# 0 = active, 1 = cancelled, 2 = pending, 3 = trial, 4 = on hold, 5 = removed
|
||||
$statusMap = [
|
||||
'active' => 0,
|
||||
'trial' => 3,
|
||||
'deleted' => 5,
|
||||
];
|
||||
|
||||
// if no states matched there is something terrifying wrong, call the ambulance!
|
||||
if (!isset($statusMap[$row['server_state']])) {
|
||||
exit;
|
||||
}
|
||||
$sentriStatus = $statusMap[$row['server_state']];
|
||||
|
||||
# Set all the server resource counts from Sentri into the $subscriptionCounts
|
||||
$subscriptionCounts['server_CPU_count']['countSentri'] = $row['server_cpu'];
|
||||
$subscriptionCounts['server_Memory_count']['countSentri'] = (int)ceil($row['server_memory'] / 1024);
|
||||
$subscriptionCounts['server_Disk_space_count']['countSentri'] = $totalDiskSpace;
|
||||
|
||||
$licenses = json_decode($row['server_licenses'], true);
|
||||
foreach ($licenses as $license) {
|
||||
foreach ($license as $key => $LicenseType) {
|
||||
$subscriptionCounts[$key . '.' . $LicenseType]['countSentri']++;
|
||||
}
|
||||
}
|
||||
|
||||
$backups = json_decode($row['server_backup'], true);
|
||||
foreach ($backups as $backup) {
|
||||
foreach ($backup as $key => $BackupType) {
|
||||
$subscriptionCounts[$key . '.' . $BackupType]['countSentri'] = $totalDiskSpace;
|
||||
}
|
||||
}
|
||||
|
||||
# Mark subscriptions that already exist in Inserve
|
||||
foreach ($subscriptionCounts as $key => &$item) {
|
||||
if (!is_array($item) || !isset($item['md5'])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$md5 = (string)$item['md5'];
|
||||
|
||||
if (isset($inserveLookup[$md5])) { # Subscription already exists in Inserve
|
||||
$item['SentriStatus'] = $sentriStatus;
|
||||
$item['sentriCompanyId'] = (int)$allCompaniesIds[$row['company_source_id']] ?? 0;
|
||||
$item['subscriptionInserveExists'] = true;
|
||||
$item['subscriptionInserveId'] = $inserveLookup[$item['md5']]['id'];
|
||||
$item['countInserve'] = $inserveLookup[$item['md5']]['quantity'];
|
||||
$item['subscriptionInserveCompanyId'] = $inserveLookup[$item['md5']]['cloud_distribution_company_id'];
|
||||
$item['subscriptionInserveStatus'] = $inserveLookup[$item['md5']]['status'];
|
||||
} else { # Subscription does not exists in Inserve
|
||||
$item['sentriCompanyId'] = (int)$allCompaniesIds[$row['company_source_id']] ?? 0;
|
||||
$item['subscriptionInserveExists'] = false;
|
||||
$item['subscriptionInserveId'] = false;
|
||||
$item['countInserve'] = 0;
|
||||
$item['subscriptionInserveCompanyId'] = false;
|
||||
}
|
||||
}
|
||||
unset($item);
|
||||
|
||||
// Make the subscriptions names look nice and dandy.
|
||||
foreach ($subscriptionCounts as $key => &$item) {
|
||||
// Set server name
|
||||
$serverName = $row['server_hostname'] ?? $row['server_vm_host_name'] ?? 'Unknown';
|
||||
|
||||
// remove server_ prefix and _count suffix
|
||||
$namePart = $key;
|
||||
if (str_starts_with($key, 'server_') && str_ends_with($key, '_count')) {
|
||||
$namePart = substr($key, 7, -6);
|
||||
$namePart = ucfirst($namePart);
|
||||
} // Handle keys with "."
|
||||
elseif (strpos($key, '.') !== false) {
|
||||
[$first, $second] = explode('.', $key, 2);
|
||||
if ($first === $second || strtolower($second) === 'yes') {
|
||||
$namePart = ucfirst($first);
|
||||
} else {
|
||||
$namePart = ucfirst($first) . ' - ' . $second;
|
||||
}
|
||||
} //Handle keys without . but with a space (expmale directadmin.Standard Discounted)
|
||||
elseif (strpos($key, ' ') !== false) {
|
||||
// explode on first .
|
||||
$parts = explode('.', $key, 2);
|
||||
if (count($parts) === 2) {
|
||||
$namePart = ucfirst($parts[0]) . ' - ' . $parts[1];
|
||||
} else {
|
||||
// Cap first word before first space
|
||||
$spacePos = strpos($key, ' ');
|
||||
$first = ucfirst(substr($key, 0, $spacePos));
|
||||
$rest = substr($key, $spacePos + 1);
|
||||
$namePart = $first . ' - ' . $rest;
|
||||
}
|
||||
}
|
||||
|
||||
$item['subscriptionInserveName'] = $serverName . ' - ' . $namePart;
|
||||
}
|
||||
unset($item);
|
||||
|
||||
foreach ($subscriptionCounts as $key => $item) {
|
||||
// if subscriptionInserveExists but the countInserve is null skip creation
|
||||
if ($item['subscriptionInserveExists'] === false && (int)$item['countSentri'] === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// if subscriptionInserveExists is false create a new subscription
|
||||
if ($item['subscriptionInserveExists'] === false) {
|
||||
$payload = [
|
||||
"cloud_distribution_id" => $item['md5'], #md5 hash based on the server_uuid from sentri and the subscription name (eg. server_cpu_count)
|
||||
"cloud_subscription_id" => "sentri-servers", # Mark all the sentri-servers subscriptions so we can filter the subscriptions better
|
||||
"name" => $item['subscriptionInserveName'],
|
||||
"quantity" => $item['countSentri'],
|
||||
"cloud_distribution_company_id" => $item['sentriCompanyId'], # this is generated by inserve (306 = digistate)
|
||||
"status" => $item['SentriStatus'],
|
||||
"period_type" => 0, # 0 = monthly, 1 = anual, 2 = one time cost
|
||||
"start_date" => date('Y-m-d')
|
||||
];
|
||||
|
||||
$this->createSubscription($payload);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// update the subscription if the countInserve and countSentri dont match
|
||||
// Or when sentriCompanyId and subscriptionInserveCompanyId dont match
|
||||
if ((
|
||||
(int)$item['countInserve'] !== (int)$item['countSentri'] ||
|
||||
(int)$item['sentriCompanyId'] !== (int)$item['subscriptionInserveCompanyId'] ||
|
||||
(int)$item['SentriStatus'] !== (int)$item['subscriptionInserveStatus']
|
||||
)
|
||||
&& $item['subscriptionInserveExists'] !== false
|
||||
) {
|
||||
|
||||
|
||||
$payload = [
|
||||
"quantity" => (int)$item['countSentri'],
|
||||
"cloud_distribution_company_id" => (int)$item['sentriCompanyId'],
|
||||
"name" => $item['subscriptionInserveName'],
|
||||
"status" => $item['SentriStatus'],
|
||||
"quantity" => $item['countSentri']
|
||||
];
|
||||
$this->updateSubscription($item['subscriptionInserveId'], $payload);
|
||||
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function createSubscription($payload)
|
||||
{
|
||||
$url = $this->inserve_url . 'cloud-distribution-subscriptions';
|
||||
$this->ch = curl_init($url);
|
||||
|
||||
# I need to make this pay load:
|
||||
curl_setopt_array($this->ch, [
|
||||
CURLOPT_RETURNTRANSFER => true,
|
||||
CURLOPT_POST => true,
|
||||
CURLOPT_POSTFIELDS => json_encode($payload),
|
||||
CURLOPT_HTTPHEADER => [
|
||||
"X-Api-Key: $this->inserve_token",
|
||||
"Accept: application/json",
|
||||
"Content-Type: application/json"
|
||||
],
|
||||
]);
|
||||
|
||||
$this->execCurl();
|
||||
}
|
||||
}
|
||||
30
pub/api/classes/API_mailsettings.php
Normal file
30
pub/api/classes/API_mailsettings.php
Normal file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_mailsettings extends API
|
||||
{
|
||||
public function updateMailSettings($updatePassword)
|
||||
{
|
||||
if ($updatePassword) {
|
||||
$query = "UPDATE vc_portal_settings SET mail_from_name = ?, mail_from_address = ?, mail_smtp_host = ?, mail_smtp_secure = ?, mail_smtp_port = ?, mail_smtp_auth = ?, mail_smtp_user = ?, mail_smtp_pass = ? WHERE portal_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssiisss', $this->data['mail_from_name'], $this->data['mail_from_address'], $this->data['mail_smtp_host'], $this->data['mail_smtp_secure'], $this->data['mail_smtp_port'], $this->data['mail_smtp_auth'], $this->data['mail_smtp_user'], $this->data['mail_smtp_pass'], $this->data['portal_uuid']);
|
||||
|
||||
} else {
|
||||
$query = "UPDATE vc_portal_settings SET mail_from_name = ?, mail_from_address = ?, mail_smtp_host = ?, mail_smtp_secure = ?, mail_smtp_port = ?, mail_smtp_auth = ?, mail_smtp_user = ? WHERE portal_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssiiss', $this->data['mail_from_name'], $this->data['mail_from_address'], $this->data['mail_smtp_host'], $this->data['mail_smtp_secure'], $this->data['mail_smtp_port'], $this->data['mail_smtp_auth'], $this->data['mail_smtp_user'], $this->data['portal_uuid']);
|
||||
|
||||
}
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'mail settings updated successfully.']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
40
pub/api/classes/API_mfa.php
Normal file
40
pub/api/classes/API_mfa.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_mfa extends API
|
||||
{
|
||||
public function disableMFA()
|
||||
{
|
||||
# Users cannot, by default disable MFA of other users
|
||||
if ($this->getUserUuid() != $this->data['user_uuid']) {
|
||||
$this->checkPermissions('admin-access-admins-mfa', 'RW');
|
||||
}
|
||||
|
||||
$query = "UPDATE vc_users SET user_two_factor_enabled = 0, user_two_factor_secret = NULL WHERE user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $this->data['user_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'mfa is disabled']);
|
||||
}
|
||||
|
||||
public function enableMFA()
|
||||
{
|
||||
# Users cannot, create MFA of other users
|
||||
if ($this->getUserUuid() != $this->data['user_uuid']) {
|
||||
$this->apiOutput(401, ['error' => 'you are not allowed to enable mfa for others']);
|
||||
}
|
||||
|
||||
$query = "UPDATE vc_users SET user_two_factor_enabled = 1, user_two_factor_secret = ? WHERE user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ss", $this->data['user_two_factor_secret'], $this->data['user_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'mfa is enabled']);
|
||||
}
|
||||
}
|
||||
32
pub/api/classes/API_office_stompjes.php
Normal file
32
pub/api/classes/API_office_stompjes.php
Normal file
@@ -0,0 +1,32 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_office_stompjes extends API
|
||||
{
|
||||
public function addStomp()
|
||||
{
|
||||
$query = "INSERT INTO office_stompjes (stomp_uuid, user_uuid, stomp_timestamp) VALUES (UUID(), ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('si', $this->data['user_uuid'], time());
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Stomp added.']);
|
||||
}
|
||||
|
||||
public function deleteStomp()
|
||||
{
|
||||
$query = "DELETE FROM office_stompjes WHERE stomp_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['stomp_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Stomp removed.']);
|
||||
}
|
||||
}
|
||||
123
pub/api/classes/API_permissions.php
Normal file
123
pub/api/classes/API_permissions.php
Normal file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_permissions extends API
|
||||
{
|
||||
public function getPermission($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_permissions');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Permission');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function deletePermission()
|
||||
{
|
||||
$query = "DELETE FROM vc_permissions WHERE permission_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['permission_uuid']);
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Permission deleted successfully.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function createPermission()
|
||||
{
|
||||
# Check if permission slugify already exists
|
||||
$result = $this->getPermissionSlugify();
|
||||
if ($result->num_rows > 0) {
|
||||
$this->apiOutput(409, ['error' => 'Permission slugify already exists'], 'item_already_exists!');
|
||||
}
|
||||
|
||||
$query = "INSERT INTO vc_permissions (permission_uuid, permission_name, permission_slugify, permission_description, permission_create_timestamp, module_uuid) VALUES (UUID(), ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('sssis', $this->data['permission_name'], $this->data['permission_slugify'], $this->data['permission_description'], time(), $this->data['module_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$result = $this->getPermissionSlugify();
|
||||
if ($result->num_rows === 0) {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the platform on the server.'], 'error_contact_support');
|
||||
}
|
||||
|
||||
$permission_data = $result->fetch_assoc();
|
||||
|
||||
# Get all the groups so we can create the permissions for the group
|
||||
$user_groups = array();
|
||||
$sql = "SELECT * FROM vc_user_groups";
|
||||
$stmt = $this->conn->query($sql);
|
||||
while ($user_group = $stmt->fetch_assoc()) {
|
||||
array_push($user_groups, $user_group);
|
||||
}
|
||||
|
||||
# Update all the groups with the newly added permission
|
||||
foreach ($user_groups as $user_group) {
|
||||
$query = "INSERT INTO vc_user_group_permissions_portal (permission_uuid, user_group_uuid, permission_value) VALUES (?, ?, ?)";
|
||||
$permission_value = 'NA';
|
||||
if ($user_group['user_group_name'] == 'superuser') {
|
||||
$permission_value = 'RW';
|
||||
}
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("sss", $permission_data['permission_uuid'], $user_group['user_group_uuid'], $permission_value);
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
}
|
||||
|
||||
$this->apiOutput(200, ['success' => $permission_data], 'item_added');
|
||||
}
|
||||
|
||||
public function getPermissionSlugify()
|
||||
{
|
||||
$query = "SELECT * FROM vc_permissions WHERE permission_slugify = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $this->data['permission_slugify']);
|
||||
$this->executeStatement($stmt);
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
public function updatePermission()
|
||||
{
|
||||
$query = "UPDATE vc_permissions SET permission_name = ?, permission_description = ?, permission_modified_timestamp = ?, module_uuid = ? WHERE permission_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssiss', $this->data['permission_name'], $this->data['permission_description'], time(), $this->data['module_uuid'], $this->data['permission_uuid']);
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Permission updated successfully.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function updateAccessRights()
|
||||
{
|
||||
$query = "UPDATE vc_user_group_permissions_portal SET permission_value = ? WHERE permission_uuid = ? AND user_group_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('sss', $this->data['permission_value'], $this->data['permission_uuid'], $this->data['user_group_uuid']);
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Access rights changed successfully.']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getPermissionRights()
|
||||
{
|
||||
$query = "SELECT * FROM vc_permissions
|
||||
INNER JOIN vc_user_group_permissions_portal ON vc_permissions.permission_uuid = vc_user_group_permissions_portal.permission_uuid
|
||||
INNER JOIN vc_user_groups ON vc_user_group_permissions_portal.user_group_uuid = vc_user_groups.user_group_uuid
|
||||
WHERE vc_permissions.permission_uuid = ? ORDER BY vc_user_groups.user_group_weight ASC";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['permission_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$result = $stmt->get_result();
|
||||
$access_rights = [];
|
||||
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$access_rights[] = $row;
|
||||
}
|
||||
|
||||
return $access_rights;
|
||||
}
|
||||
}
|
||||
72
pub/api/classes/API_platforms.php
Normal file
72
pub/api/classes/API_platforms.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_platforms extends API
|
||||
{
|
||||
public function getPlatforms($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_platforms');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Platform');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
|
||||
public function createPlatforms()
|
||||
{
|
||||
if (isset($this->data['platform_image'])) {
|
||||
$query = "INSERT INTO vc_platforms (platform_uuid, platform_name, platform_slugify, platform_description, platform_enabled, platform_image, platform_create_timestamp) VALUES (UUID(), ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('sssssi', $this->data['platform_name'], $this->data['platform_slugify'], $this->data['platform_description'], $this->data['platform_enabled'], $this->data['platform_image'], time());
|
||||
} else {
|
||||
$query = "INSERT INTO vc_platforms (platform_uuid, platform_name, platform_slugify, platform_description, platform_enabled, platform_create_timestamp) VALUES (UUID(), ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssi', $this->data['platform_name'], $this->data['platform_slugify'], $this->data['platform_description'], $this->data['platform_enabled'], time());
|
||||
}
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$result = $this->getPlatformSlugify();
|
||||
if ($result->num_rows === 0) {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the platform on the server.'], 'error_contact_support');
|
||||
}
|
||||
|
||||
$platform_data = $result->fetch_assoc();
|
||||
|
||||
$this->apiOutput(200, ['success' => $platform_data], 'item_added');
|
||||
}
|
||||
|
||||
public function getPlatformSlugify()
|
||||
{
|
||||
$query = "SELECT * FROM vc_platforms WHERE platform_slugify = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $this->data['platform_slugify']);
|
||||
$this->executeStatement($stmt);
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
public function editPlatforms()
|
||||
{
|
||||
if (isset($this->data['platform_image'])) {
|
||||
$query = "UPDATE vc_platforms SET platform_name = ?, platform_description = ?, platform_enabled = ?, platform_image = ?, platform_modified_timestamp = ? WHERE platform_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssisis", $this->data['platform_name'], $this->data['platform_description'], $this->data['platform_enabled'], $this->data['platform_image'], time(), $this->data['platform_uuid']);
|
||||
} else {
|
||||
$query = "UPDATE vc_platforms SET platform_name = ?, platform_description = ?, platform_enabled = ?, platform_modified_timestamp = ? WHERE platform_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssiis", $this->data['platform_name'], $this->data['platform_description'], $this->data['platform_enabled'], time(), $this->data['platform_uuid']);
|
||||
}
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Platform updated successfully.']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
21
pub/api/classes/API_portalsettings.php
Normal file
21
pub/api/classes/API_portalsettings.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_portalsettings extends API
|
||||
{
|
||||
public function updatePortalSettings()
|
||||
{
|
||||
$query = "UPDATE vc_portal_settings SET portal_name = ?, portal_provider_name = ?, admin_auth_methods = ? WHERE portal_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssss", $this->data['portal_name'], $this->data['portal_provider_name'], $this->data['admin_auth_methods'], $this->data['portal_uuid']);
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'portal settings updated successfully.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
pub/api/classes/API_resetpassword.php
Normal file
26
pub/api/classes/API_resetpassword.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_resetpassword extends API
|
||||
{
|
||||
public function resetPassword()
|
||||
{
|
||||
|
||||
$query = "UPDATE vc_users SET user_password = ?, user_password_reset_token = ?, user_password_reset_expires = ? WHERE user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssis",
|
||||
$this->data['user_password'],
|
||||
$this->data['user_password_reset_token'],
|
||||
$this->data['user_password_reset_expires'],
|
||||
$this->data['user_uuid']
|
||||
);
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
}
|
||||
}
|
||||
269
pub/api/classes/API_servers.php
Normal file
269
pub/api/classes/API_servers.php
Normal file
@@ -0,0 +1,269 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
use JsonException;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_servers extends API
|
||||
{
|
||||
public function validateDiskData($disks)
|
||||
{
|
||||
foreach ($disks as $index => $disk) {
|
||||
|
||||
// Ensure $disk is an array
|
||||
if (!is_array($disk)) {
|
||||
$this->apiOutput(400, ['error' => "Disk entry is not an array"]);
|
||||
}
|
||||
|
||||
$requiredFields = ['disk_name', 'disk_space', 'disk_used', 'disk_location'];
|
||||
|
||||
foreach ($requiredFields as $field) {
|
||||
if (!array_key_exists($field, $disk)) {
|
||||
$this->apiOutput(400, ['error' => "Missing required field '$field' in disk information"]);
|
||||
}
|
||||
switch ($field) {
|
||||
case 'disk_used':
|
||||
case 'disk_space':
|
||||
$disks[$index][$field] = $this->validateSingleData($disk[$field], ['type' => 'float']);
|
||||
break;
|
||||
case 'disk_location':
|
||||
case 'disk_name':
|
||||
$disks[$index][$field] = $this->validateSingleData($disk[$field], ['type' => 'string']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
return json_encode($disks, JSON_THROW_ON_ERROR);
|
||||
} catch (JsonException $e) {
|
||||
$this->apiOutput(400, ['error' => "Failed to encode disk data to JSON: " . $e->getMessage()]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function updateServer()
|
||||
{
|
||||
|
||||
if (isset($this->data['company_uuid'])) {
|
||||
if (strlen($this->data['company_uuid']) == 0) {
|
||||
$this->data['company_uuid'] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
$fields = [
|
||||
'company_uuid',
|
||||
'server_vm_id',
|
||||
'server_vm_host_id',
|
||||
'server_vm_host_name',
|
||||
'server_power_state',
|
||||
'server_state',
|
||||
'server_hostname',
|
||||
'server_os',
|
||||
'server_cpu',
|
||||
'server_memory',
|
||||
'server_memory_demand',
|
||||
'server_disks',
|
||||
'server_ipv4',
|
||||
'server_ipv6',
|
||||
'server_vm_generation',
|
||||
'server_vm_snapshot',
|
||||
'server_licenses',
|
||||
'server_backup',
|
||||
'server_description'
|
||||
];
|
||||
|
||||
$insertFields = ['server_uuid'];
|
||||
$insertValues = ['UUID()'];
|
||||
$bindParams = [];
|
||||
|
||||
foreach ($fields as $field) {
|
||||
if (array_key_exists($field, $this->data)) {
|
||||
$insertFields[] = $field;
|
||||
$insertValues[] = ":$field";
|
||||
$bindParams[":$field"] = $this->data[$field]; // can be NULL
|
||||
}
|
||||
}
|
||||
|
||||
# Always include server_create_timestamp and server_modified_timestamp
|
||||
$insertFields[] = 'server_create_timestamp';
|
||||
$insertValues[] = ':server_create_timestamp';
|
||||
$bindParams[':server_create_timestamp'] = time();
|
||||
|
||||
$insertFields[] = 'server_modified_timestamp';
|
||||
$insertValues[] = ':server_modified_timestamp';
|
||||
$bindParams[':server_modified_timestamp'] = time();
|
||||
|
||||
$query = "INSERT INTO servers (" . implode(',', $insertFields) . ")
|
||||
VALUES (" . implode(',', $insertValues) . ")
|
||||
ON DUPLICATE KEY UPDATE ";
|
||||
|
||||
# Build the ON DUPLICATE KEY UPDATE, only foor fields that exist
|
||||
$updateParts = [];
|
||||
foreach ($insertFields as $field) {
|
||||
if (!in_array($field, ['server_create_timestamp', 'server_uuid'])) {
|
||||
$updateParts[] = "$field = VALUES($field)";
|
||||
}
|
||||
}
|
||||
$query .= implode(", ", $updateParts);
|
||||
|
||||
$stmt = $GLOBALS['pdo']->prepare($query);
|
||||
|
||||
if (!$stmt->execute($bindParams)) {
|
||||
$this->apiOutput(400, ['error' => "Failed to insert server into database"]);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private function validateLicenseData($server_vm_id, $server_licenses)
|
||||
{
|
||||
$server_vm_id = $this->validateSingleData($server_vm_id, ['type' => 'string']);
|
||||
$server_licenses_posted = $this->validateSingleData($server_licenses, ['type' => 'array']);
|
||||
|
||||
$query = "SELECT server_licenses FROM servers WHERE server_vm_id = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $server_vm_id);
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$server_licenses_db = $result->fetch_assoc();
|
||||
|
||||
$server_licenses_db = $server_licenses_db['server_licenses'] ?? null;
|
||||
$server_licenses_db_new = [];
|
||||
if (!empty($server_licenses_db)) {
|
||||
$decoded = json_decode($server_licenses_db, true);
|
||||
if (is_array($decoded)) {
|
||||
foreach ($decoded as $item) {
|
||||
foreach ($item as $key => $value) {
|
||||
$server_licenses_db_new[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($server_licenses_posted as $item) {
|
||||
foreach ($item as $rawKey => $value) {
|
||||
|
||||
$prefix = substr($rawKey, 0, 1);
|
||||
$license = substr($rawKey, 1);
|
||||
|
||||
if ($prefix === '+') {
|
||||
$server_licenses_db_new[$license] = $value;
|
||||
}
|
||||
|
||||
if ($prefix === '-') {
|
||||
unset($server_licenses_db_new[$license]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$server_licenses_db_new_final = [];
|
||||
foreach ($server_licenses_db_new as $key => $value) {
|
||||
$server_licenses_db_new_final[] = [$key => $value];
|
||||
}
|
||||
|
||||
return empty($server_licenses_db_new_final) ? '[]' : json_encode($server_licenses_db_new_final);
|
||||
}
|
||||
|
||||
private function validateBackupData($server_vm_id, $server_backup)
|
||||
{
|
||||
$server_vm_id = $this->validateSingleData($server_vm_id, ['type' => 'string']);
|
||||
$server_backup_posted = $this->validateSingleData($server_backup, ['type' => 'array']);
|
||||
|
||||
$query = "SELECT server_backup FROM servers WHERE server_vm_id = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $server_vm_id);
|
||||
$this->executeStatement($stmt);
|
||||
$result = $stmt->get_result();
|
||||
|
||||
$server_backup_db = $result->fetch_assoc();
|
||||
|
||||
$server_backup_db = $server_backup_db['server_backup'] ?? null;
|
||||
$server_backup_db_new = [];
|
||||
if (!empty($server_backup_db)) {
|
||||
$decoded = json_decode($server_backup_db, true);
|
||||
if (is_array($decoded)) {
|
||||
foreach ($decoded as $item) {
|
||||
foreach ($item as $key => $value) {
|
||||
$server_backup_db_new[$key] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($server_backup_posted as $item) {
|
||||
foreach ($item as $rawKey => $value) {
|
||||
|
||||
$prefix = substr($rawKey, 0, 1);
|
||||
$backup = substr($rawKey, 1);
|
||||
|
||||
if ($prefix === '+') {
|
||||
$server_backup_db_new[$backup] = $value;
|
||||
}
|
||||
|
||||
if ($prefix === '-') {
|
||||
unset($server_backup_db_new[$backup]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$server_backup_db_new_final = [];
|
||||
foreach ($server_backup_db_new as $key => $value) {
|
||||
$server_backup_db_new_final[] = [$key => $value];
|
||||
}
|
||||
|
||||
return empty($server_backup_db_new_final) ? '[]' : json_encode($server_backup_db_new_final);
|
||||
}
|
||||
|
||||
public function processServerData($server, $requiredFields, $optionalFields)
|
||||
{
|
||||
// since the disk data is sent as an array we need to check it seperatly from the other data validations
|
||||
|
||||
if (!empty($server['server_disks']) && is_array($server['server_disks'])) {
|
||||
$server['server_disks'] = $this->validateDiskData($server['server_disks']);
|
||||
} else {
|
||||
unset($server['server_disks']);
|
||||
}
|
||||
|
||||
if (!empty($server['server_licenses']) && is_array($server['server_licenses'])) {
|
||||
$server['server_licenses'] = $this->validateLicenseData($server['server_vm_id'], $server['server_licenses']);
|
||||
} else {
|
||||
unset($server['server_licenses']);
|
||||
}
|
||||
|
||||
if (!empty($server['server_backup']) && is_array($server['server_backup'])) {
|
||||
$server['server_backup'] = $this->validateBackupData($server['server_vm_id'], $server['server_backup']);
|
||||
} else {
|
||||
unset($server['server_backup']);
|
||||
}
|
||||
|
||||
foreach (['server_ipv4', 'server_ipv6'] as $key) {
|
||||
if (!empty($server[$key]) && is_array($server[$key])) {
|
||||
$server[$key] = json_encode($server[$key]);
|
||||
} else {
|
||||
unset($server[$key]);
|
||||
}
|
||||
}
|
||||
|
||||
$this->postedData = $server;
|
||||
|
||||
$this->validateData($requiredFields, $optionalFields);
|
||||
|
||||
$this->updateServer();
|
||||
}
|
||||
|
||||
public function deleteServer()
|
||||
{
|
||||
$query = "DELETE FROM servers WHERE server_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['server_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Server removed']);
|
||||
}
|
||||
}
|
||||
34
pub/api/classes/API_system_modules.php
Normal file
34
pub/api/classes/API_system_modules.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_system_modules extends API
|
||||
{
|
||||
public function getModules($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('system_modules');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Permission');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function enableModule()
|
||||
{
|
||||
|
||||
$module_uuid_enabled = ($this->data['module_enabled']) ? 0 : 1;
|
||||
|
||||
# Module 'system cannot be disabled'
|
||||
$query = "UPDATE system_modules SET module_enabled = ? WHERE module_uuid = ? AND module_slugify != 'system'";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('is', $module_uuid_enabled, $this->data['module_uuid']);
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Module ' . ($module_uuid_enabled ? 'enabled' : 'disabled') . ' successfully.']);
|
||||
}
|
||||
}
|
||||
}
|
||||
26
pub/api/classes/API_system_sources.php
Normal file
26
pub/api/classes/API_system_sources.php
Normal file
@@ -0,0 +1,26 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_system_sources extends API
|
||||
{
|
||||
public function inserveUpdate()
|
||||
{
|
||||
$query = "INSERT INTO system_sources (source_uuid, source_name, source_url, source_auth_username, source_auth_password, source_auth_token, source_create_timestamp, source_modified_timestamp)
|
||||
VALUES (UUID(), ?, ?, '', '', ?, ?, NULL)
|
||||
ON DUPLICATE KEY UPDATE
|
||||
source_url = VALUES(source_url),
|
||||
source_auth_token = VALUES(source_auth_token),
|
||||
source_modified_timestamp = VALUES(source_create_timestamp)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('sssi', $this->data['source_name'], $this->data['source_url'], $this->data['source_auth_token'], time());
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'Information modified'], 'Information updated successfully.');
|
||||
}
|
||||
}
|
||||
140
pub/api/classes/API_usergroups.php
Normal file
140
pub/api/classes/API_usergroups.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_usergroups extends API
|
||||
{
|
||||
public function getUsergroup($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_user_groups');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'User Group');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function createUsergroups()
|
||||
{
|
||||
# check if the user_group already exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_group_slugify', 1 => $this->data['user_group_slugify']]]];
|
||||
|
||||
if ($this->getUsergroup(true)) {
|
||||
$this->apiOutput(409, ['error' => 'Usergroup already exists.']);
|
||||
}
|
||||
|
||||
$this->data['user_group_weight'] = $this->get_next_available_user_group_weight($this->data['user_group_weight']);
|
||||
|
||||
if ($this->data['user_group_weight'] < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot make an group with an lower weight then yourself!']);
|
||||
}
|
||||
|
||||
$query = "INSERT INTO vc_user_groups (user_group_uuid, user_group_name, user_group_slugify, user_group_weight, user_group_type, user_group_create_timestamp) VALUES (UUID(), ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssisi", $this->data['user_group_name'], $this->data['user_group_slugify'], $this->data['user_group_weight'], $this->data['user_group_type'], time());
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
$user_group = $this->getUsergroup();
|
||||
$user_group_uuid = $user_group[0]['user_group_uuid'];
|
||||
|
||||
|
||||
# Get all the permission from the database and create the permission for the user group.
|
||||
$stmt = $this->conn->query("SELECT permission_uuid FROM vc_permissions");
|
||||
while ($row = $stmt->fetch_assoc()) {
|
||||
$permission_uuids[] = $row['permission_uuid'];
|
||||
}
|
||||
|
||||
|
||||
$values = [];
|
||||
foreach ($permission_uuids as $permission_uuid) {
|
||||
$permission_uuid_safe = $GLOBALS['conn']->real_escape_string($permission_uuid);
|
||||
$values[] = "('$permission_uuid_safe', '$user_group_uuid')";
|
||||
}
|
||||
|
||||
if (!empty($values)) {
|
||||
$values_sql = implode(", ", $values);
|
||||
|
||||
$query = "INSERT INTO vc_user_group_permissions_portal (permission_uuid, user_group_uuid) VALUES $values_sql";
|
||||
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User group created successfully']);
|
||||
} else {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the user-group.'], 'error_contact_support');
|
||||
}
|
||||
}
|
||||
|
||||
public function deleteUsergroup()
|
||||
{
|
||||
# check if the user group exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_group_uuid', 1 => $this->data['user_group_uuid']]]];
|
||||
$user_group = $this->getUsergroup();
|
||||
|
||||
# superuser cannot be deleted
|
||||
if ($user_group[0]['user_group_slufigy'] === 'superuser') {
|
||||
$this->apiOutput(400, ['error' => 'superuser cannot be deleted.']);
|
||||
}
|
||||
|
||||
if ($user_group[0]['user_group_weight'] < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'groups with an lower weight cannot be deleted.']);
|
||||
}
|
||||
|
||||
$query = "DELETE FROM vc_user_groups WHERE user_group_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['user_group_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User group created deleted']);
|
||||
}
|
||||
|
||||
private function get_next_available_user_group_weight($weight)
|
||||
{
|
||||
# checks what the next avail weight is if the weight is taken by another item
|
||||
$sql = "SELECT user_group_weight FROM vc_user_groups ORDER BY user_group_weight ASC";
|
||||
$result = $this->conn->query($sql);
|
||||
|
||||
// Store all existing weights in an array
|
||||
$existing_weights = [];
|
||||
while ($row = $result->fetch_assoc()) {
|
||||
$existing_weights[] = (int)$row['user_group_weight'];
|
||||
}
|
||||
|
||||
// If weight already exists, find the next available integer
|
||||
while (in_array($weight, $existing_weights)) {
|
||||
$weight++;
|
||||
}
|
||||
|
||||
return $weight;
|
||||
}
|
||||
|
||||
public function updateUserGroup()
|
||||
{
|
||||
# check if the user group exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_group_uuid', 1 => $this->data['user_group_uuid']]]];
|
||||
$user_group = $this->getUsergroup();
|
||||
|
||||
# superuser cannot be modified
|
||||
if ($user_group[0]['user_group_slufigy'] === 'superuser') {
|
||||
$this->apiOutput(500, ['error' => 'superuser cannot be modified']);
|
||||
}
|
||||
|
||||
if ($user_group[0]['user_group_weight'] != $this->data['user_group_weight']) {
|
||||
$this->data['user_group_weight'] = $this->get_next_available_user_group_weight($this->data['user_group_weight']);
|
||||
}
|
||||
|
||||
if ($this->data['user_group_weight'] < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot make an group with an lower weight then yourself!']);
|
||||
}
|
||||
|
||||
$query = "UPDATE vc_user_groups SET user_group_name = ?, user_group_weight = ?, user_group_modified_timestamp = ? WHERE user_group_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("siis", $this->data['user_group_name'], $this->data['user_group_weight'], time(), $this->data['user_group_uuid']);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User group created updated']);
|
||||
}
|
||||
}
|
||||
151
pub/api/classes/API_users.php
Normal file
151
pub/api/classes/API_users.php
Normal file
@@ -0,0 +1,151 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
use api\classes\API_usergroups;
|
||||
use bin\php\Classes\mailBuilder;
|
||||
|
||||
require_once 'API.php';
|
||||
require_once $_SERVER['DOCUMENT_ROOT'] . '/bin/php/Classes/mailBuilder.php';
|
||||
|
||||
class API_users extends API
|
||||
{
|
||||
public function getUser($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_users');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'User');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function createUser()
|
||||
{
|
||||
# check if the user already exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_email', 1 => $this->data['user_email']]]];
|
||||
|
||||
if ($this->getUser(true)) {
|
||||
$this->apiOutput(409, ['error' => 'user already exists.']);
|
||||
}
|
||||
|
||||
|
||||
if ($this->getUserGroupWeight() < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot make an user with an lower weight then yourself!']);
|
||||
}
|
||||
|
||||
$query = "INSERT INTO vc_users (
|
||||
user_uuid, user_group_uuid, user_email, user_first_name, user_last_name, user_full_name,
|
||||
user_phone_number, user_password, user_password_reset_token, user_password_reset_expires,
|
||||
user_two_factor_enabled, user_two_factor_secret, user_status,
|
||||
user_verified_email, user_verified_phone, user_create_timestamp, user_modified_timestamp,
|
||||
user_last_login_timestamp, user_login_attempts, user_pref_language, user_stompable
|
||||
) VALUES (
|
||||
UUID(), ?, ?, ?, ?, ?, ?, ?, ?, ?, 0, NULL, ?, 0, 0, ?, NULL, NULL, 0, ?, 0
|
||||
)";
|
||||
|
||||
$stmt = $this->prepareStatement($query);
|
||||
|
||||
$stmt->bind_param("ssssssssisis",
|
||||
$this->data['user_group_uuid'],
|
||||
$this->data['user_email'],
|
||||
$this->data['user_first_name'],
|
||||
$this->data['user_last_name'],
|
||||
$this->data['user_full_name'],
|
||||
$this->data['user_phone_number'],
|
||||
$this->data['user_password'],
|
||||
$this->data['user_password_reset_token'],
|
||||
$this->data['user_password_reset_expires'],
|
||||
$this->data['user_status'],
|
||||
time(),
|
||||
$this->data['user_pref_language'],
|
||||
);
|
||||
|
||||
# Sending an email to the user
|
||||
$host = $_SERVER['HTTP_HOST'];
|
||||
$verifyLink = "https://{$host}/login/verifyEmail.php?token={$this->data['user_password_reset_token']}";
|
||||
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$mail = new mailBuilder();
|
||||
$mail->subject = "Hello " . $this->data['user_first_name'] . ", your Sentri account is ready — set your password";
|
||||
$mail->addAddress($this->data['user_email'], $this->data['user_first_name']);
|
||||
$mail->mailText = '
|
||||
Hello ' . $this->data['user_first_name'] . ',<br><br>
|
||||
An account has been created for you in Sentri.<br>
|
||||
To activate your account, please verify your email address and set your password by clicking the link below:<br>
|
||||
<a href="' . $verifyLink . '" class="btn btn-primary">Activate My Account</a><br><br>
|
||||
Or copy and paste the following link into your browser: <br>' . $verifyLink . '<br><br>
|
||||
|
||||
This link is valid for 24 hours.<br>
|
||||
After that, you’ll need to request a new activation link.<br><br>
|
||||
|
||||
If you weren’t expecting this email or believe it was sent by mistake, you can safely ignore it.<br><br>
|
||||
|
||||
Best regards,<br><br>
|
||||
The Sentri gnomes';
|
||||
$mail->sendMail();
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User created successfully. mail has been sent']);
|
||||
}
|
||||
|
||||
private function getUserGroupWeight()
|
||||
{
|
||||
require_once 'API_usergroups.php';
|
||||
|
||||
$API_usergroups = new API_usergroups();
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_group_uuid', 1 => $this->data['user_group_uuid']]]];
|
||||
|
||||
return $API_usergroups->getUserGroup()[0]['user_group_weight'];
|
||||
}
|
||||
|
||||
public function updateUser()
|
||||
{
|
||||
# check if the user exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_uuid', 1 => $this->data['user_uuid']]]];
|
||||
$this->getUser();
|
||||
|
||||
if ($this->getUserGroupWeight() < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot edit a user with an lower weight then yourself!']);
|
||||
}
|
||||
|
||||
$query = "UPDATE vc_users SET user_group_uuid = ?, user_email = ?, user_first_name = ?, user_last_name = ?, user_full_name = ?, user_phone_number = ?, user_status = ?, user_pref_language = ?, user_modified_timestamp = ?, user_stompable = ? WHERE user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssssssiis', $this->data['user_group_uuid'], $this->data['user_email'], $this->data['user_first_name'], $this->data['user_last_name'], $this->data['user_full_name'], $this->data['user_phone_number'], $this->data['user_status'], $this->data['user_pref_language'], time(), $this->data['user_stompable'], $this->data['user_uuid']);
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User successfully updated.']);
|
||||
}
|
||||
|
||||
public function deleteUser()
|
||||
{
|
||||
# delete an user
|
||||
|
||||
# chect if the user exists
|
||||
$_GET['builder'] = [1 => ['where' => [0 => 'user_uuid', 1 => $this->data['user_uuid']]]];
|
||||
$user_data = $this->getUser()[0];
|
||||
|
||||
|
||||
$this->data['user_group_uuid'] = $user_data['user_group_uuid'];
|
||||
|
||||
# check group weigth
|
||||
if ($this->getUserGroupWeight() < $_SESSION['user']['user_group_weight']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot delete a user with an lower weight then yourself!']);
|
||||
}
|
||||
|
||||
if ($user_data['user_uuid'] == $_SESSION['user']['user_uuid']) {
|
||||
$this->apiOutput(400, ['error' => 'You cannot delete yourself, maybe some rope will do.']);
|
||||
}
|
||||
|
||||
|
||||
$query = "DELETE FROM vc_users WHERE user_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('s', $this->data['user_uuid']);
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
$this->apiOutput(200, ['success' => 'User successfully deleted']);
|
||||
}
|
||||
}
|
||||
45
pub/api/classes/API_usersavatar.php
Normal file
45
pub/api/classes/API_usersavatar.php
Normal file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
use api\classes\imageProcessor;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_usersavatar extends API
|
||||
{
|
||||
|
||||
public function createUserImage($imageRestrictions)
|
||||
{
|
||||
try {
|
||||
# Main image
|
||||
$imageProcessor = new imageProcessor('user_profile_picture');
|
||||
$imageProcessor->imageRestrictions = $imageRestrictions;
|
||||
$imageProcessor->validateAndProcess();
|
||||
$ImageData = $imageProcessor->returnBase64image();
|
||||
} catch (Exception $e) {
|
||||
$this->apiOutput(401, ['error' => 'Error: ' . $e->getMessage()]);
|
||||
}
|
||||
|
||||
return $ImageData;
|
||||
}
|
||||
|
||||
public function updateUserImage()
|
||||
{
|
||||
$query = "UPDATE vc_users SET
|
||||
user_profile_picture = ?,
|
||||
user_profile_picture_thumbnail = ?
|
||||
WHERE user_uuid = ?";
|
||||
|
||||
$stmt = $stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("sss",
|
||||
$this->data['user_profile_picture'],
|
||||
$this->data['user_profile_picture_thumbnail'],
|
||||
$this->data['user_uuid']
|
||||
);
|
||||
$this->executeStatement($stmt);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
71
pub/api/classes/API_vendors.php
Normal file
71
pub/api/classes/API_vendors.php
Normal file
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
namespace api\classes;
|
||||
|
||||
use api\classes\API;
|
||||
|
||||
require_once 'API.php';
|
||||
|
||||
class API_vendors extends API
|
||||
{
|
||||
public function getVendors($returnBoolean = false)
|
||||
{
|
||||
list($query, $types, $params) = $this->buildDynamicQuery('vc_vendors');
|
||||
|
||||
$items = $this->generalGetFunction($query, $types, $params, $returnBoolean, 'Vendor');
|
||||
|
||||
return $items;
|
||||
}
|
||||
|
||||
public function createVendor()
|
||||
{
|
||||
if (isset($this->data['vendor_image'])) {
|
||||
$query = "INSERT INTO vc_vendors (vendor_uuid, vendor_name, vendor_slugify, vendor_description, vendor_enabled, vendor_create_timestamp, vendor_image) VALUES (UUID(), ?, ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssis', $this->data['vendor_name'], $this->data['vendor_slugify'], $this->data['vendor_description'], $this->data['vendor_enabled'], time(), $this->data['vendor_image']);
|
||||
} else {
|
||||
$query = "INSERT INTO vc_vendors (vendor_uuid, vendor_name, vendor_slugify, vendor_description, vendor_enabled, vendor_create_timestamp) VALUES (UUID(), ?, ?, ?, ?, ?)";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param('ssssi', $this->data['vendor_name'], $this->data['vendor_slugify'], $this->data['vendor_description'], $this->data['vendor_enabled'], time());
|
||||
}
|
||||
|
||||
$this->executeStatement($stmt);
|
||||
$stmt->close();
|
||||
|
||||
$result = $this->getVendorSlugify();
|
||||
if ($result->num_rows === 0) {
|
||||
$this->apiOutput(500, ['error' => 'Something went wrong creating the vendor on the server.'], 'error_contact_support');
|
||||
}
|
||||
|
||||
$platform_data = $result->fetch_assoc();
|
||||
|
||||
$this->apiOutput(200, ['success' => $platform_data], 'item_added');
|
||||
}
|
||||
|
||||
public function getVendorSlugify()
|
||||
{
|
||||
$query = "SELECT * FROM vc_vendors WHERE vendor_slugify = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("s", $this->data['vendor_slugify']);
|
||||
$this->executeStatement($stmt);
|
||||
return $stmt->get_result();
|
||||
}
|
||||
|
||||
public function editVendor()
|
||||
{
|
||||
if (isset($this->data['vendor_image'])) {
|
||||
$query = "UPDATE vc_vendors SET vendor_name = ?, vendor_description = ?, vendor_enabled = ?, vendor_image = ?, vendor_modified_timestamp = ? WHERE vendor_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssisis", $this->data['vendor_name'], $this->data['vendor_description'], $this->data['vendor_enabled'], $this->data['vendor_image'], time(), $this->data['vendor_uuid']);
|
||||
} else {
|
||||
$query = "UPDATE vc_vendors SET vendor_name = ?, vendor_description = ?, vendor_enabled = ?, vendor_modified_timestamp = ? WHERE vendor_uuid = ?";
|
||||
$stmt = $this->prepareStatement($query);
|
||||
$stmt->bind_param("ssiis", $this->data['vendor_name'], $this->data['vendor_description'], $this->data['vendor_enabled'], time(), $this->data['vendor_uuid']);
|
||||
}
|
||||
|
||||
if ($this->executeStatement($stmt)) {
|
||||
$this->apiOutput(200, ['success' => 'Platform updated successfully.']);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
202
pub/api/classes/imageProcessor.php
Normal file
202
pub/api/classes/imageProcessor.php
Normal file
@@ -0,0 +1,202 @@
|
||||
<?php
|
||||
|
||||
|
||||
namespace api\classes;
|
||||
class imageProcessor
|
||||
{
|
||||
public $postedFile = null;
|
||||
private $imageTmpPath;
|
||||
private $imageInfo;
|
||||
|
||||
public $imageRestrictions = [
|
||||
'min_width' => 100,
|
||||
'max_width' => 600,
|
||||
'min_height' => 100,
|
||||
'max_height' => 600,
|
||||
'square' => false,
|
||||
'allowed_types' => ['image/png', 'image/jpeg', 'image/webp'],
|
||||
'max_size_kb' => 2048, // 2MB
|
||||
'transparent' => false
|
||||
];
|
||||
|
||||
private $finalImage = null;
|
||||
|
||||
public function __construct($imageName)
|
||||
{
|
||||
if (isset($_FILES[$imageName]) && $_FILES[$imageName]['error'] === UPLOAD_ERR_OK) {
|
||||
$this->postedFile = $_FILES[$imageName];
|
||||
$this->imageTmpPath = $this->postedFile['tmp_name'];
|
||||
$this->imageInfo = getimagesize($this->imageTmpPath);
|
||||
} elseif (isset($_POST['image_base64'])) {
|
||||
$base64 = $_POST['image_base64'];
|
||||
|
||||
if (preg_match('/^data:(image\/\w+);base64,/', $base64, $matches)) {
|
||||
$mimeType = $matches[1];
|
||||
$base64 = substr($base64, strpos($base64, ',') + 1);
|
||||
} else {
|
||||
throw new Exception('Invalid image data.');
|
||||
}
|
||||
|
||||
$imageData = base64_decode($base64);
|
||||
if ($imageData === false) {
|
||||
throw new Exception('Invalid base64 image data.');
|
||||
}
|
||||
|
||||
# Create image directly from string (no file)
|
||||
$srcImage = imagecreatefromstring($imageData);
|
||||
if (!$srcImage) {
|
||||
throw new Exception('Failed to create image from string.');
|
||||
}
|
||||
|
||||
# Now you can get dimensions directly
|
||||
$width = imagesx($srcImage);
|
||||
$height = imagesy($srcImage);
|
||||
|
||||
# Store $srcImage in a class property, continue processing in-memory
|
||||
$this->imageResource = $srcImage;
|
||||
$this->imageInfo = [
|
||||
'mime' => $mimeType,
|
||||
'width' => $width,
|
||||
'height' => $height,
|
||||
'size' => strlen($imageData)
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
public function validateAndProcess()
|
||||
{
|
||||
if (!$this->postedFile) {
|
||||
return true;
|
||||
}
|
||||
$width = $this->imageInfo[0];
|
||||
$height = $this->imageInfo[1];
|
||||
$mime = $this->imageInfo['mime'];
|
||||
$fileSizeKB = filesize($this->imageTmpPath) / 1024;
|
||||
|
||||
if (!in_array($mime, $this->imageRestrictions['allowed_types'])) {
|
||||
throw new Exception("Invalid image type: $mime");
|
||||
}
|
||||
|
||||
if ($fileSizeKB > $this->imageRestrictions['max_size_kb']) {
|
||||
throw new Exception("Image exceeds max file size.");
|
||||
}
|
||||
|
||||
# Resize to fit within min/max bounds
|
||||
$resizedImage = $this->resizeToFitRestrictions($mime, $width, $height);
|
||||
|
||||
# Optionally square it
|
||||
if ($this->imageRestrictions['square']) {
|
||||
$resizedImage = $this->makeImageSquare($resizedImage, $mime);
|
||||
}
|
||||
|
||||
ob_start();
|
||||
switch ($mime) {
|
||||
case 'image/jpeg':
|
||||
imagejpeg($resizedImage);
|
||||
break;
|
||||
case 'image/png':
|
||||
imagepng($resizedImage);
|
||||
break;
|
||||
case 'image/webp':
|
||||
imagewebp($resizedImage);
|
||||
break;
|
||||
}
|
||||
$this->finalImage = ob_get_clean();
|
||||
|
||||
imagedestroy($resizedImage);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function mimeSupportsTransparency($mime)
|
||||
{
|
||||
return in_array($mime, ['image/png', 'image/webp']);
|
||||
}
|
||||
|
||||
private function setTransparentCanvas($image, $width, $height)
|
||||
{
|
||||
// Enable alpha blending and preserve alpha channel
|
||||
imagealphablending($image, false);
|
||||
imagesavealpha($image, true);
|
||||
|
||||
// Fill the image with a fully transparent color
|
||||
$transparent = imagecolorallocatealpha($image, 0, 0, 0, 127);
|
||||
imagefilledrectangle($image, 0, 0, $width, $height, $transparent);
|
||||
|
||||
return $image;
|
||||
}
|
||||
|
||||
private function resizeToFitRestrictions($mime, $width, $height)
|
||||
{
|
||||
$minW = $this->imageRestrictions['min_width'];
|
||||
$maxW = $this->imageRestrictions['max_width'];
|
||||
$minH = $this->imageRestrictions['min_height'];
|
||||
$maxH = $this->imageRestrictions['max_height'];
|
||||
|
||||
$srcImage = match ($mime) {
|
||||
'image/jpeg' => imagecreatefromjpeg($this->imageTmpPath),
|
||||
'image/png' => imagecreatefrompng($this->imageTmpPath),
|
||||
'image/webp' => imagecreatefromwebp($this->imageTmpPath),
|
||||
default => throw new Exception("Unsupported image type.")
|
||||
};
|
||||
|
||||
# Determine new size
|
||||
$newWidth = $width;
|
||||
$newHeight = $height;
|
||||
|
||||
if ($width < $minW || $width > $maxW || $height < $minH || $height > $maxH) {
|
||||
# Calculate scale factor based on limits
|
||||
$widthScale = ($width < $minW) ? $minW / $width : ($width > $maxW ? $maxW / $width : 1);
|
||||
$heightScale = ($height < $minH) ? $minH / $height : ($height > $maxH ? $maxH / $height : 1);
|
||||
|
||||
# Use the smallest scale to ensure both dimensions fit
|
||||
$scale = min($widthScale, $heightScale);
|
||||
|
||||
$newWidth = round($width * $scale);
|
||||
$newHeight = round($height * $scale);
|
||||
}
|
||||
|
||||
$resizedImage = imagecreatetruecolor($newWidth, $newHeight);
|
||||
|
||||
# keep transparent
|
||||
if ($this->mimeSupportsTransparency($mime)) {
|
||||
$this->setTransparentCanvas($resizedImage, $newWidth, $newHeight);
|
||||
}
|
||||
|
||||
imagecopyresampled($resizedImage, $srcImage, 0, 0, 0, 0, $newWidth, $newHeight, $width, $height);
|
||||
imagedestroy($srcImage);
|
||||
return $resizedImage;
|
||||
}
|
||||
|
||||
private function makeImageSquare($srcImage, $mime)
|
||||
{
|
||||
$width = imagesx($srcImage);
|
||||
$height = imagesy($srcImage);
|
||||
|
||||
$size = min($width, $height);
|
||||
$x = ($width - $size) / 2;
|
||||
$y = ($height - $size) / 2;
|
||||
|
||||
$squareImage = imagecreatetruecolor($size, $size);
|
||||
|
||||
# keep transparent
|
||||
if ($this->mimeSupportsTransparency($mime)) {
|
||||
$this->setTransparentCanvas($squareImage, $size, $size);
|
||||
}
|
||||
|
||||
imagecopyresampled($squareImage, $srcImage, 0, 0, $x, $y, $size, $size, $size, $size);
|
||||
imagedestroy($srcImage);
|
||||
|
||||
return $squareImage;
|
||||
}
|
||||
|
||||
public function returnBase64image()
|
||||
{
|
||||
if ($this->finalImage) {
|
||||
return base64_encode($this->finalImage);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user