فهرست منبع

add new api v2 routes

CauseFX 5 سال پیش
والد
کامیت
26ed7127e8

+ 147 - 0
api/v2/index.php

@@ -0,0 +1,147 @@
+<?php
+/**
+ * @OA\Info(title="Organizr API", description="Organizr - Accept no others", version="2.0")
+ * @OA\Server(url=API_HOST,description="This Organizr Install")
+ * @OA\Server(url="https://demo.organizr.app",description="Organizr Demo API")
+ * @OA\Server(url="{schema}://{hostPath}",description="Custom Organizr API",
+ *      @OA\ServerVariable(
+ *          serverVariable="schema",
+ *          enum={"https", "http"},
+ *          default="http"
+ *      ),
+ *     @OA\ServerVariable(
+ *          serverVariable="hostPath",
+ *          description="Your Organizr URL",
+ *          default="localhost"
+ *      )
+ * )
+ * @OA\SecurityScheme(
+ *   securityScheme="api_key",
+ *   type="apiKey",
+ *   in="header",
+ *   name="Token"
+ * )
+ */
+require_once '../functions.php';
+
+use Psr\Http\Message\ResponseInterface as Response;
+use Psr\Http\Message\ServerRequestInterface as Request;
+use Psr\Http\Message\StreamInterface;
+use Slim\Factory\AppFactory;
+use Psr\Http\Server\MiddlewareInterface;
+use Psr\Http\Server\RequestHandlerInterface as RequestHandler;
+
+class JsonBodyParserMiddleware implements MiddlewareInterface
+{
+	public function process(Request $request, RequestHandler $handler): Response
+	{
+		$contentType = $request->getHeaderLine('Content-Type');
+		if (strstr($contentType, 'application/json')) {
+			$contents = json_decode(file_get_contents('php://input'), true);
+			if (json_last_error() === JSON_ERROR_NONE) {
+				$request = $request->withParsedBody($contents);
+			}
+		}
+		return $handler->handle($request);
+	}
+}
+
+class Lowercase implements MiddlewareInterface
+{
+	/**
+	 * @var ResponseFactoryInterface
+	 */
+	private $responseFactory;
+	
+	/*
+	 * Whether returns a 301 response to the new path.
+	 */
+	public function redirect(ResponseFactoryInterface $responseFactory): self
+	{
+		$this->responseFactory = $responseFactory;
+		return $this;
+	}
+	
+	/*
+	 * Process a request and return a response.
+	 */
+	public function process(Request $request, RequestHandler $handler): Response
+	{
+		$uri = $request->getUri();
+		$path = strtolower($uri->getPath());
+		if ($this->responseFactory && ($uri->getPath() !== $path)) {
+			return $this->responseFactory->createResponse(301)
+				->withHeader('Location', (string)$uri->withPath($path));
+		}
+		return $handler->handle($request->withUri($uri->withPath($path)));
+	}
+}
+
+$GLOBALS['api'] = array(
+	'response' => array(
+		'result' => 'success',
+		'message' => null,
+		'data' => null
+	)
+);
+$GLOBALS['bypass'] = array(
+	'/api/v2/upgrade',
+	'/api/v2/update',
+	'/api/v2/force',
+	'/api/v2/auth',
+	'/api/v2/wizard',
+	'/api/v2/login',
+	'/api/v2/wizard/path',
+	'/api/v2/login/api',
+	'/api/v2/plex/register'
+);
+$GLOBALS['responseCode'] = 200;
+function jsonE($json)
+{
+	return safe_json_encode($json, JSON_HEX_QUOT | JSON_HEX_TAG | JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES);
+}
+
+// Instantiate App
+$app = AppFactory::create();
+// Add error middleware
+$app->addRoutingMiddleware();
+$app->addErrorMiddleware(true, true, true);
+$app->setBasePath('/api/v2');
+$app->add(function ($request, $handler) {
+	// add the organizr to your request as [READ-ONLY]
+	$Organizr = new Organizr();
+	$request = $request->withAttribute('Organizr', $Organizr);
+	return $handler->handle($request);
+});
+$app->add(new Lowercase());
+/*
+ * Include all routes
+ */
+foreach (glob(__DIR__ . DIRECTORY_SEPARATOR . 'routes' . DIRECTORY_SEPARATOR . '*.php') as $filename) {
+	require_once $filename;
+}
+/*
+ * Include all Plugin routes
+ */
+foreach (glob(dirname(__DIR__, 2) . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . 'plugins' . DIRECTORY_SEPARATOR . 'api' . DIRECTORY_SEPARATOR . "*.php") as $filename) {
+	require_once $filename;
+}
+/*
+ *
+ *  This is the last defined api endpoint to catch all undefined endpoints
+ *
+ */
+$app->any('{route:.*}', function ($request, $response) {
+	$GLOBALS['api']['response']['data'] = array(
+		'endpoint' => $request->getUri()->getPath(),
+		'method' => $request->getMethod(),
+	);
+	$GLOBALS['api']['response']['result'] = 'error';
+	$GLOBALS['api']['response']['message'] = 'Endpoint Not Found or Defined';
+	$GLOBALS['responseCode'] = 404;
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->run();

+ 142 - 0
api/v2/routes/2fa.php

@@ -0,0 +1,142 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="2fa",
+ *     description="Two Form Authentication"
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="submit-2fa-verify",
+ *     type="object",
+ *     @OA\Property(
+ *      property="secret",
+ *      type="string",
+ *      example="OX1R4GA3425GSDF"
+ *     ),
+ *     @OA\Property(
+ *      property="code",
+ *      type="string",
+ *      example="145047"
+ *     ),
+ *     @OA\Property(
+ *      property="type",
+ *      type="string",
+ *      example="google"
+ *     ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="submit-2fa-save",
+ *     type="object",
+ *     @OA\Property(
+ *      property="secret",
+ *      type="string",
+ *      example="OX1R4GA3425GSDF"
+ *     ),
+ *     @OA\Property(
+ *      property="type",
+ *      type="string",
+ *      example="google"
+ *     ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="submit-2fa-create",
+ *     type="object",
+ *     @OA\Property(
+ *      property="type",
+ *      type="string",
+ *      example="google"
+ *     ),
+ * )
+ */
+$app->post('/2fa', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"2fa"},
+	 *     path="/api/v2/2fa",
+	 *     summary="Verify 2FA code",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-verify")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$data = $Organizr->apiData($request);
+		$GLOBALS['api']['response']['data'] = $Organizr->verify2FA($data['secret'], $data['code'], $data['type']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->put('/2fa', function ($request, $response, $args) {
+	/**
+	 * @OA\Put(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"2fa"},
+	 *     path="/api/v2/2fa",
+	 *     summary="Save 2FA code",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-save")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$data = $Organizr->apiData($request);
+		$Organizr->save2FA($data['secret'], $data['type']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->post('/2fa/{type}', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     tags={"2fa"},
+	 *     path="/api/v2/2fa/{type}",
+	 *     summary="Create 2FA code",
+	 *     @OA\Parameter(name="type",description="The type of 2FA",@OA\Schema(type="string"),in="path",required=true,example="google"),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message"))
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->create2FA($args['type']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/2fa', function ($request, $response, $args) {
+	/**
+	 * @OA\Delete(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"2fa"},
+	 *     path="/api/v2/2fa",
+	 *     summary="Delete 2FA code",
+	 *     @OA\Response(response="204",description="Success"),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$Organizr->remove2FA();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 136 - 0
api/v2/routes/categories.php

@@ -0,0 +1,136 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="categories",
+ *     description="Category Information"
+ * )
+ */
+$app->get('/categories', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"categories"},
+	 *     path="/api/v2/categories",
+	 *     summary="Get all categories",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getAllTabs();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/categories', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"categories"},
+	 *     path="/api/v2/categories",
+	 *     summary="Add new category",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-verify")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->addCategory($Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->put('/categories', function ($request, $response, $args) {
+	/**
+	 * @OA\Put(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"categories"},
+	 *     path="/api/v2/categories",
+	 *     summary="Update category order",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-verify")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->updateCategoryOrder($Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->put('/categories/{id}', function ($request, $response, $args) {
+	/**
+	 * @OA\Put(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"categories"},
+	 *     path="/api/v2/categories/{id}",
+	 *     summary="Update category",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-verify")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->updateCategory($args['id'], $Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/categories/{id}', function ($request, $response, $args) {
+	/**
+	 * @OA\Delete(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"categories"},
+	 *     path="/api/v2/categories/{id}",
+	 *     summary="Delete category",
+	 *     @OA\RequestBody(description="Success",required=true,@OA\JsonContent(ref="#/components/schemas/submit-2fa-verify")),
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->deleteCategory($args['id']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 46 - 0
api/v2/routes/config.php

@@ -0,0 +1,46 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="config",
+ *     description="Organizr Configuration Items"
+ * )
+ */
+$app->get('/config', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->config;
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->put('/config', function ($request, $response, $args) {
+	/**
+	 * @OA\Put(
+	 *     tags={"config"},
+	 *     path="/api/v2/config",
+	 *     summary="Update Organizr Coniguration Item(s)",
+	 *     @OA\RequestBody(
+	 *      description="Success",
+	 *      required=true,
+	 *      @OA\JsonContent(ref="#/components/schemas/config-items-example"),
+	 *     ),
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/success-message"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized"),
+	 *     security={{ "api_key":{} }}
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->updateConfigItems($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 464 - 0
api/v2/routes/connectionTester.php

@@ -0,0 +1,464 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="test connection",
+ *     description="Test Connections"
+ * )
+ */
+$app->post('/test/iframe', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/iframe",
+	 *     summary="Test if URL can be iFramed",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="409",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->frameTest($Organizr->apiData($request)['url']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/path', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/path",
+	 *     summary="Test if path has correct permissions",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->testWizardPath($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/plex', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/plex",
+	 *     summary="Test connection to Plex",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionPlex($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/emby', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/emby",
+	 *     summary="Test connection to Emby",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionEmby($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/sabnzbd', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/sabnzbd",
+	 *     summary="Test connection to SabNZBd",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionSabNZBd($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/pihole', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/pihole",
+	 *     summary="Test connection to PiHole",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionPihole($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/rtorrent', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/rtorrent",
+	 *     summary="Test connection to rTorrent",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionRTorrent($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/sonarr', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/sonarr",
+	 *     summary="Test connection to Sonarr",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionSonarr($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/radarr', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/radarr",
+	 *     summary="Test connection to Radarr",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionRadarr($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/lidarr', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/lidarr",
+	 *     summary="Test connection to Lidarr",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionLidarr($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/sickrage', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/sickrage",
+	 *     summary="Test connection to SickRage",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionSickRage($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/ombi', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/ombi",
+	 *     summary="Test connection to Ombi",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionOmbi($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/nzbget', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/nzbget",
+	 *     summary="Test connection to NzbGet",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionNZBGet($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/deluge', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/deluge",
+	 *     summary="Test connection to Deluge",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionDeluge($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/jdownloader', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/jdownloader",
+	 *     summary="Test connection to jDownloader",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionJDownloader($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/transmission', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/transmission",
+	 *     summary="Test connection to Transmission",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionTransmission($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/qbittorrent', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/qbittorrent",
+	 *     summary="Test connection to qBittorrent",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionQBittorrent($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/unifi', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/unifi",
+	 *     summary="Test connection to Unifi",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionUnifi($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/unifi/site', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/unifi/site",
+	 *     summary="Test connection to Unifi Sites",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->getUnifiSiteName($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/test/tautulli', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"test connection"},
+	 *     path="/api/v2/test/tautulli",
+	 *     summary="Test connection to Tautulli",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->testConnectionTautulli($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 29 - 0
api/v2/routes/emby.php

@@ -0,0 +1,29 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="emby"
+ * )
+ */
+$app->post('/emby/register', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     tags={"emby"},
+	 *     path="/api/v2/emby/register",
+	 *     summary="Register a user using Emby API",
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/status"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized")
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		embyJoinAPI($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 60 - 0
api/v2/routes/groups.php

@@ -0,0 +1,60 @@
+<?php
+$app->get('/groups', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getAllGroups();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/groups/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getGroupById($args['id']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/groups', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->addGroup($Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->put('/groups/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->updateGroup($args['id'], $Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/groups/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->deleteGroup($args['id']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 447 - 0
api/v2/routes/homepage.php

@@ -0,0 +1,447 @@
+<?php
+$app->get('/homepage/image', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getHomepageMediaImage();
+	// Don't need below as we kill script... maybe i should clean up correctly
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/plex/streams', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPlexHomepageStreams();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/emby/streams', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getEmbyHomepageStreams();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/plex/recent', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPlexHomepageRecent();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/emby/recent', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getEmbyHomepageRecent();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/jellyfin/recent', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getEmbyHomepageRecent();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/plex/playlists', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPlexHomepagePlaylists();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/plex/metadata', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPlexHomepageMetadata($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/emby/metadata', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getEmbyHomepageMetadata($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/jellyfin/metadata', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getEmbyHomepageMetadata($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/plex/search/{query}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPlexHomepageSearch($args['query']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/pihole/stats', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getPiholeHomepageStats();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/rtorrent/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getRTorrentHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/sonarr/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getSonarrQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/sonarr/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getSonarrCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/radarr/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getRadarrQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/radarr/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getRadarrCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/lidarr/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getLidarrCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/couchpotato/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getCouchPotatoCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/sickrage/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getSickRageCalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/ical/calendar', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getICalendar();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/deluge/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getDelugeHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/transmission/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getTransmissionHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/qbittorrent/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getQBittorrentHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/jdownloader/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getJdownloaderHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/nzbget/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getNzbgetHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/sabnzbd/queue', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getSabNZBdHomepageQueue();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/sabnzbd/queue/resume', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->resumeSabNZBdQueue($Organizr->apiData($request)['target']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/sabnzbd/queue/pause', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->pauseSabNZBdQueue($Organizr->apiData($request)['target']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/unifi/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getUnifiHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/tautulli/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getTautulliHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/netdata/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getNetdataHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/monitorr/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getMonitorrHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/speedtest/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getSpeedtestHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/octoprint/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getOctoprintHomepageData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/weather/data', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getWeatherAndAirData();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/weather/coordinates', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->searchCityForCoordinates($Organizr->apiData($request)['query']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/healthchecks', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getHealthChecks();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/healthchecks/{tags}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getHealthChecks($args['tags']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/ombi/requests', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->getOmbiRequests();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/ombi/requests/{type}/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->addOmbiRequest($args['id'], $args['type']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/ombi/requests/{type}/{id}/available', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->actionOmbiRequest($args['id'], $args['type'], 'available');
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/ombi/requests/{type}/{id}/unavailable', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->actionOmbiRequest($args['id'], $args['type'], 'unavailable');
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/ombi/requests/{type}/{id}/approve', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->actionOmbiRequest($args['id'], $args['type'], 'approve');
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->put('/homepage/ombi/requests/{type}/{id}/deny', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->actionOmbiRequest($args['id'], $args['type'], 'deny');
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->delete('/homepage/ombi/requests/{type}/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->actionOmbiRequest($args['id'], $args['type'], 'delete');
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/homepage/youtube/{query}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->youtubeSearch($args['query']);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/homepage/scrape', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->scrapePage($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 34 - 0
api/v2/routes/image.php

@@ -0,0 +1,34 @@
+<?php
+$app->get('/image', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getImages();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->delete('/image/{image}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->removeImage($args['image']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/image', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->uploadImage();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 25 - 0
api/v2/routes/log.php

@@ -0,0 +1,25 @@
+<?php
+$app->get('/log/{log}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$GLOBALS['api']['response']['data'] = $Organizr->getLog($args['log']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/log/{log}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->purgeLog($args['log']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 9 - 0
api/v2/routes/login.php

@@ -0,0 +1,9 @@
+<?php
+$app->post('/login', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->login($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 9 - 0
api/v2/routes/logout.php

@@ -0,0 +1,9 @@
+<?php
+$app->get('/logout', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->logout();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 99 - 0
api/v2/routes/pages.php

@@ -0,0 +1,99 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="page",
+ *     description="HTML for Organizr Pages"
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="get-html",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success message or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example="\r\n\u003Cscript\u003E\r\n    (function() {\r\n        updateCheck();\r\n        authDebugCheck();\r\n        sponsorLoad();\r\n        newsLoad();\r\n        checkCommitLoad();\r\n        [].slice.call(document.querySelectorAll('.sttabs-main-settings-div')).forEach(function(el) {\r\n            new CBPFWTabs(el);\r\n        });\r\n    })();\r\n\u003C/script\u003E\r\n"
+ *     )
+ *    ),
+ *  ),
+ * )
+ */
+$app->get('/page/{page}', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     tags={"page"},
+	 *     path="/api/v2/page/{page}",
+	 *     summary="Get HTML for Organizr Pages",
+	 *     @OA\Parameter(
+	 *      name="page",
+	 *      description="Page to get",
+	 *      @OA\Schema(
+	 *          type="string"
+	 *      ),
+	 *      in="path",
+	 *      required=true
+	 *      ),
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/get-html"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized"),
+	 *     security={{ "api_key":{} }}
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$page = $Organizr->getPage($args['page']);
+		if ($page) {
+			$GLOBALS['api']['response']['data'] = $page;
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/page', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     tags={"page"},
+	 *     path="/api/v2/page",
+	 *     summary="Get list of all Organizr Pages",
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/get-html"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized"),
+	 *     security={{ "api_key":{} }}
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$page = $Organizr->getPageList();
+		if ($page) {
+			$GLOBALS['api']['response']['data'] = $page;
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 37 - 0
api/v2/routes/ping.php

@@ -0,0 +1,37 @@
+<?php
+$app->get('/ping', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     path="/api/v2/ping",
+	 *     summary="Ping the Organizr API",
+	 *     @OA\Response(
+	 *         response="200",
+	 *         description="Success",
+	 *         @OA\JsonContent(ref="#/components/schemas/ping"),
+	 *     ),
+	 *   @OA\Response(response="401",description="Unauthorized"),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$GLOBALS['api']['response']['data'] = 'pong';
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json');
+	
+});
+$app->post('/ping', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$GLOBALS['api']['response']['data'] = $Organizr->ping($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/ping/{ping}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$GLOBALS['api']['response']['data'] = $Organizr->ping(array('list' => $args['ping']));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 53 - 0
api/v2/routes/plex.php

@@ -0,0 +1,53 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="plex"
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="plexRegister",
+ *     type="object",
+ *     @OA\Property(
+ *      property="username",
+ *      type="string",
+ *      example="causefx"
+ *  ),@OA\Property(
+ *      property="email",
+ *      type="string",
+ *      example="causefx@organizr.app"
+ *  ),@OA\Property(
+ *      property="password",
+ *      type="string",
+ *      example="iCanHazPa$$w0Rd"
+ *  ),
+ * )
+ */
+$app->post('/plex/register', function ($request, $response, $args) {
+	/**
+	 * @OA\Post(
+	 *     tags={"plex"},
+	 *     path="/api/v2/plex/register",
+	 *     summary="Register a user using Plex API",
+	 *     @OA\RequestBody(
+	 *      description="Success",
+	 *      required=true,
+	 *      @OA\JsonContent(ref="#/components/schemas/plexRegister"),
+	 *     ),
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/status"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized")
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$Organizr->plexJoinAPI($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 36 - 0
api/v2/routes/plugins.php

@@ -0,0 +1,36 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="plugins"
+ * )
+ */
+$app->get('/plugins', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getPlugins();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->post('/plugins/manage/{plugin}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->installPlugin($args['plugin']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/plugins/manage/{plugin}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->removePlugin($args['plugin']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 117 - 0
api/v2/routes/root.php

@@ -0,0 +1,117 @@
+<?php
+/* Forward root to /status */
+$app->get('', function ($request, $response, $args) {
+	return $response
+		->withHeader('Location', '/api/v2/status')
+		->withStatus(302);
+});
+$app->get('/', function ($request, $response, $args) {
+	return $response
+		->withHeader('Location', '/api/v2/status')
+		->withStatus(302);
+});
+$app->get('/status', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     path="/api/v2/status",
+	 *     summary="Query Organizr API to perform a Status Check",
+	 *     @OA\Response(
+	 *      response="200",
+	 *      description="Success",
+	 *      @OA\JsonContent(ref="#/components/schemas/status"),
+	 *     ),
+	 *     @OA\Response(response="401",description="Unauthorized")
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$GLOBALS['api']['response']['data'] = array(
+			'status' => 'ok',
+			'api_version' => '2.0',
+			'organizr_version' => $Organizr->version
+		);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/auth', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     path="/api/v2/auth",
+	 *     summary="Nginx auth_request",
+	 * @OA\Parameter(
+	 *   name="group",
+	 *   description="The id of the group allowed",
+	 *   @OA\Schema(
+	 *     type="integer",
+	 *     format="int64",
+	 *   ),
+	 *   in="query",
+	 *   required=false
+	 * ),
+	 * @OA\Parameter(
+	 *   name="whitelist",
+	 *   description="Whitelisted Ip's",
+	 *   @OA\Schema(
+	 *     type="array",
+	 *     @OA\Items(
+	 *      type="string",
+	 *     ),
+	 *   ),
+	 *   in="query",
+	 *   explode=false,
+	 *   required=false
+	 * ),
+	 * @OA\Parameter(
+	 *   name="blacklist",
+	 *   description="Blacklisted Ip's",
+	 *   @OA\Schema(
+	 *     type="array",
+	 *     @OA\Items(
+	 *      type="string",
+	 *     ),
+	 *   ),
+	 *   in="query",
+	 *   explode=false,
+	 *   required=false
+	 * ),
+	 * @OA\Response(
+	 *  response="200",
+	 *  description="Success",
+	 *  ),
+	 *  @OA\Response(response="401",description="Unauthorized"),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->auth();
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/launch', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$tabInfo = $Organizr->getUserTabsAndCategories();
+	$GLOBALS['api']['response']['data']['categories'] = $tabInfo['categories'];
+	$GLOBALS['api']['response']['data']['tabs'] = $tabInfo['tabs'];
+	$GLOBALS['api']['response']['data']['user'] = $Organizr->user;
+	$GLOBALS['api']['response']['data']['branch'] = $Organizr->config['branch'];
+	$GLOBALS['api']['response']['data']['theme'] = $Organizr->config['theme'];
+	$GLOBALS['api']['response']['data']['style'] = $Organizr->config['style'];
+	$GLOBALS['api']['response']['data']['version'] = $Organizr->version;
+	$GLOBALS['api']['response']['data']['settings'] = $Organizr->organizrSpecialSettings();
+	$GLOBALS['api']['response']['data']['plugins'] = $Organizr->pluginGlobalList();
+	$GLOBALS['api']['response']['data']['appearance'] = $Organizr->loadAppearance();
+	$GLOBALS['api']['response']['data']['status'] = $Organizr->status();
+	$GLOBALS['api']['response']['data']['sso'] = array(
+		'myPlexAccessToken' => isset($_COOKIE['mpt']) ? $_COOKIE['mpt'] : false,
+		'id_token' => isset($_COOKIE['Auth']) ? $_COOKIE['Auth'] : false
+	);
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 10 - 0
api/v2/routes/rox.php

@@ -0,0 +1,10 @@
+<?php
+$app->get('/rox', function ($request, $response, $args) {
+	$GLOBALS['api']['response']['message'] = 'rox in socks!';
+	$GLOBALS['api']['response']['data'] = 'https://www.roxinsocks.com/';
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->redirect('/roxinsocks', 'https://roxinsocks.com', 301);

+ 41 - 0
api/v2/routes/settings.php

@@ -0,0 +1,41 @@
+<?php
+$app->get('/settings/appearance', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getCustomizeAppearance();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/settings/main', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getSettingsMain();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/settings/sso', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getSettingsSSO();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->get('/settings/homepage', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getSettingsHomepage();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 34 - 0
api/v2/routes/socks.php

@@ -0,0 +1,34 @@
+<?php
+$app->any('/socks/sonarr/{route:.*}', function ($request, $response) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$new = str_ireplace('/api/v2/socks/sonarr', '', $request->getUri()->getPath());
+	$getParams = ($_GET) ? '?' . http_build_query($_GET) : '';
+	$url = $Organizr->qualifyURL($Organizr->config['sonarrURL']) . $new . $getParams;
+	$url = $Organizr->cleanPath($url);
+	$options = ($Organizr->localURL($url)) ? array('verify' => false) : array();
+	$headers = [];
+	if ($request->hasHeader('X-Api-Key')) {
+		$headerKey = $request->getHeaderLine('X-Api-Key');
+		$headers['X-Api-Key'] = $headerKey;
+	}
+	switch ($request->getMethod()) {
+		case 'GET':
+			$call = Requests::get($url, $headers, $options);
+			break;
+		case 'POST':
+			$call = Requests::post($url, $headers, $Organizr->apiData($request), $options);
+			break;
+		case 'DELETE':
+			$call = Requests::delete($url, $headers, $options);
+			break;
+		case 'PUT':
+			$call = Requests::put($url, $headers, $Organizr->apiData($request), $options);
+			break;
+		default:
+			$call = Requests::get($url, $headers, $options);
+	}
+	$response->getBody()->write($call->body);
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 65 - 0
api/v2/routes/tabs.php

@@ -0,0 +1,65 @@
+<?php
+$app->get('/tabs', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getAllTabs();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/tabs', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->addTab($Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->put('/tabs', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->updateTabOrder($Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->put('/tabs/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->updateTab($args['id'], $Organizr->apiData($request));
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/tabs/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->deleteTab($args['id']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});/*$GLOBALS['api']['response']['json'] = json_decode(file_get_contents('php://input', 'r'), true);
+	$GLOBALS['api']['response']['post'] = $_POST;
+	$GLOBALS['api']['response']['body'] = $request->getBody();
+	$GLOBALS['api']['response']['parsed'] = $request->getParsedBody();
+	$GLOBALS['api']['response']['request'] = $request*/;

+ 21 - 0
api/v2/routes/themes.php

@@ -0,0 +1,21 @@
+<?php
+$app->post('/themes/manage/{theme}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->installTheme($args['theme']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->delete('/themes/manage/{theme}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->removeTheme($args['theme']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 11 - 0
api/v2/routes/token.php

@@ -0,0 +1,11 @@
+<?php
+$app->delete('/token/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$Organizr->revokeTokenByIdCurrentUser($args['id']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 151 - 0
api/v2/routes/update.php

@@ -0,0 +1,151 @@
+<?php
+/**
+ * @OA\Tag(
+ *     name="update",
+ *     description="Organizr Update"
+ * )
+ */
+$app->get('/update/download/{branch}', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/download/{branch}",
+	 *     summary="Download Organizr Update Files",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->upgradeInstall($args['branch'], '1');
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/update/unzip/{branch}', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/unzip/{branch}",
+	 *     summary="Unzip Organizr Update Files",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->upgradeInstall($args['branch'], '2');
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/update/move/{branch}', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/move/{branch}",
+	 *     summary="Move Organizr Update Files",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->upgradeInstall($args['branch'], '3');
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/update/cleanup/{branch}', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/cleanup/{branch}",
+	 *     summary="Cleanup Organizr Update Files",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->upgradeInstall($args['branch'], '4');
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/update/docker', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/docker",
+	 *     summary="Update Organizr install using Docker Container script",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->dockerUpdate();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/update/windows', function ($request, $response, $args) {
+	/**
+	 * @OA\Get(
+	 *     security={{ "api_key":{} }},
+	 *     tags={"update"},
+	 *     path="/api/v2/update/windows",
+	 *     summary="Update Organizr install using Windows script",
+	 *     @OA\Response(response="200",description="Success",@OA\JsonContent(ref="#/components/schemas/success-message")),
+	 *     @OA\Response(response="401",description="Unauthorized",@OA\JsonContent(ref="#/components/schemas/unauthorized-message")),
+	 *     @OA\Response(response="404",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="422",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 *     @OA\Response(response="500",description="Error",@OA\JsonContent(ref="#/components/schemas/error-message")),
+	 * )
+	 */
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->windowsUpdate();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 130 - 0
api/v2/routes/users.php

@@ -0,0 +1,130 @@
+<?php
+$app->get('/users', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getAllUsers();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->addUser($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->get('/users/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->getUserById($args['id']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->put('/users/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$Organizr->updateUser($args['id'], $Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->delete('/users/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		if ($Organizr->qualifyRequest(1, true)) {
+			$Organizr->deleteUser($args['id']);
+		}
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json')
+		->withStatus($GLOBALS['responseCode']);
+});
+$app->post('/users/lock', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$Organizr->lockCurrentUser();
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/unlock', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(998, true)) {
+		$Organizr->unlockCurrentUser($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/lock/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->lockUser($args['id']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/unlock/{id}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->unlockUser($args['id']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/import/{type}', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->qualifyRequest(1, true)) {
+		$Organizr->importUsersType($args['type']);
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/register', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->register($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});
+$app->post('/users/recover', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	$Organizr->recover($Organizr->apiData($request));
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+	
+});

+ 11 - 0
api/v2/routes/wizard.php

@@ -0,0 +1,11 @@
+<?php
+$app->post('/wizard', function ($request, $response, $args) {
+	$Organizr = ($request->getAttribute('Organizr')) ?? new Organizr();
+	if ($Organizr->checkRoute($request)) {
+		$GLOBALS['api']['response']['data'] = $Organizr->wizardConfig($Organizr->apiData($request));
+	}
+	$response->getBody()->write(jsonE($GLOBALS['api']));
+	return $response
+		->withHeader('Content-Type', 'application/json;charset=UTF-8')
+		->withStatus($GLOBALS['responseCode']);
+});

+ 280 - 0
api/v2/schemas.php

@@ -0,0 +1,280 @@
+<?php
+/**
+ * @OA\Schema(
+ *     schema="ping",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example="pong",
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="status",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="object",
+ *          @OA\Property(
+ *              property="status",
+ *              description="success or error",
+ *              type="string",
+ *              example="ok",
+ *          ),
+ *          @OA\Property(
+ *              property="api_version",
+ *              type="string",
+ *              example="2.0",
+ *          ),
+ *          @OA\Property(
+ *              property="organizr_version",
+ *              type="string",
+ *              example="2.0.650",
+ *          )
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="pluginSettingsPage",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="object",
+ *          @OA\Property(
+ *              property="settingsPageObjectItem1",
+ *              type="object",
+ *          ),
+ *          @OA\Property(
+ *              property="settingsPageObjectItem2",
+ *              type="object",
+ *          ),
+ *          @OA\Property(
+ *              property="settingsPageObjectItem3",
+ *              type="object",
+ *          )
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="successNullData",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success message or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="success-message",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success message or error message",
+ *          type="string",
+ *          example="Successful message here",
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="error-message",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="error",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success message or error message",
+ *          type="string",
+ *          example="Error message here",
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="unauthorized-message",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="error",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success message or error message",
+ *          type="string",
+ *          example="User is not authorized",
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="php-mailer-email-list",
+ *     type="object",
+ *     @OA\Property(
+ *      property="response",
+ *      type="object",
+ *      @OA\Property(
+ *          property="result",
+ *          description="success or error",
+ *          type="string",
+ *          example="success",
+ *      ),
+ *      @OA\Property(
+ *          property="message",
+ *          description="success or error message",
+ *          type="string",
+ *          example=null,
+ *      ),
+ *      @OA\Property(
+ *          property="data",
+ *          description="data from api",
+ *          type="array",
+ *          @OA\Items({
+ *          @OA\Property(
+ *              property="causefx",
+ *              type="string",
+ *              example="causefx@organizr.app",
+ *          )
+ * })
+ *      ),
+ *  ),
+ * )
+ */
+/**
+ * @OA\Schema(
+ *     schema="config-items-example",
+ *     type="object",
+ *     description="list of config items to update",
+ *      @OA\Property(
+ *          property="branch",
+ *          description="config item name",
+ *          type="string",
+ *          example="v2-master",
+ *      ),
+ *      @OA\Property(
+ *          property="hideRegistration",
+ *          type="boolean",
+ *          example=false,
+ *      ),
+ *      @OA\Property(
+ *          property="homepageUnifiAuth",
+ *          type="string",
+ *          example="1",
+ *      ),
+ * )
+ */