1
0
Fork 0
mirror of synced 2024-06-02 10:54:44 +12:00
appwrite/app/app.php

897 lines
36 KiB
PHP
Raw Normal View History

2019-05-09 18:54:39 +12:00
<?php
// Init
2019-10-25 06:53:37 +13:00
require_once __DIR__.'/init.php';
global $env, $utopia, $request, $response, $register, $consoleDB, $project, $domain, $version, $service;
2019-05-09 18:54:39 +12:00
use Utopia\App;
use Utopia\Request;
use Utopia\Response;
2019-10-25 06:53:37 +13:00
use Utopia\Validator\Host;
use Utopia\Validator\Range;
use Utopia\View;
use Utopia\Exception;
2019-05-09 18:54:39 +12:00
use Auth\Auth;
2019-12-16 07:56:29 +13:00
use Database\Database;
2019-05-09 18:54:39 +12:00
use Database\Document;
use Database\Validator\Authorization;
2019-10-25 06:53:37 +13:00
use Event\Event;
use Utopia\Validator\WhiteList;
2019-05-09 18:54:39 +12:00
2019-10-01 17:57:41 +13:00
/*
2019-10-25 06:53:37 +13:00
* Configuration files
2019-05-09 18:54:39 +12:00
*/
2019-10-25 06:53:37 +13:00
$roles = include __DIR__.'/config/roles.php'; // User roles and scopes
$services = include __DIR__.'/config/services.php'; // List of services
2019-10-25 06:53:37 +13:00
$webhook = new Event('v1-webhooks', 'WebhooksV1');
$audit = new Event('v1-audits', 'AuditsV1');
$usage = new Event('v1-usage', 'UsageV1');
2019-10-01 17:57:41 +13:00
2019-10-25 06:53:37 +13:00
/**
* Get All verified client URLs for both console and current projects
* + Filter for duplicated entries
2019-10-25 06:53:11 +13:00
*/
2019-10-25 06:53:37 +13:00
$clientsConsole = array_map(function ($node) {
return $node['url'];
}, array_filter($console->getAttribute('platforms', []), function ($node) {
if (isset($node['type']) && $node['type'] === 'web' && isset($node['url']) && !empty($node['url'])) {
return true;
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
return false;
}));
$clients = array_unique(array_merge($clientsConsole, array_map(function ($node) {
return $node['url'];
}, array_filter($project->getAttribute('platforms', []), function ($node) {
if (isset($node['type']) && $node['type'] === 'web' && isset($node['url']) && !empty($node['url'])) {
return true;
2019-05-09 18:54:39 +12:00
}
2019-10-25 06:53:37 +13:00
return false;
}))));
2019-11-30 07:23:29 +13:00
$utopia->init(function () use ($utopia, $request, $response, &$user, $project, $roles, $webhook, $audit, $usage, $domain, $clients) {
2019-10-25 06:53:37 +13:00
$route = $utopia->match($request);
$referrer = $request->getServer('HTTP_REFERER', '');
$origin = $request->getServer('HTTP_ORIGIN', parse_url($referrer, PHP_URL_SCHEME).'://'.parse_url($referrer, PHP_URL_HOST));
$refDomain = (in_array($origin, $clients))
? $origin : 'http://localhost';
/*
* Security Headers
*
* As recommended at:
* @see https://www.owasp.org/index.php/List_of_useful_HTTP_headers
*/
$response
->addHeader('Server', 'Appwrite')
->addHeader('X-XSS-Protection', '1; mode=block; report=/v1/xss?url='.urlencode($request->getServer('REQUEST_URI')))
//->addHeader('X-Frame-Options', ($refDomain == 'http://localhost') ? 'SAMEORIGIN' : 'ALLOW-FROM ' . $refDomain)
->addHeader('X-Content-Type-Options', 'nosniff')
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-SDK-Version')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true')
;
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
/*
* Validate Client Domain - Check to avoid CSRF attack
* Adding Appwrite API domains to allow XDOMAIN communication
2020-01-29 23:49:18 +13:00
* Skip this check for non-web platforms which are not requiredto send an origin header
2019-10-25 06:53:37 +13:00
*/
$hostValidator = new Host($clients);
$origin = $request->getServer('HTTP_ORIGIN', $request->getServer('HTTP_REFERER', ''));
if (!empty($origin) && !$hostValidator->isValid($origin)
2019-10-25 06:53:37 +13:00
&& in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
&& empty($request->getHeader('X-Appwrite-Key', ''))) {
throw new Exception('Access from this client host is forbidden. '.$hostValidator->getDescription(), 403);
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
/*
* ACL Check
*/
$role = ($user->isEmpty()) ? Auth::USER_ROLE_GUEST : Auth::USER_ROLE_MEMBER;
// Add user roles
$membership = $user->search('teamId', $project->getAttribute('teamId', null), $user->getAttribute('memberships', []));
if ($membership) {
foreach ($membership->getAttribute('roles', []) as $memberRole) {
switch ($memberRole) {
case 'owner':
$role = Auth::USER_ROLE_OWNER;
break;
case 'admin':
$role = Auth::USER_ROLE_ADMIN;
break;
case 'developer':
$role = Auth::USER_ROLE_DEVELOPER;
break;
}
}
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
2020-01-12 10:53:57 +13:00
2019-10-25 06:53:37 +13:00
// Check if given key match project API keys
$key = $project->search('secret', $request->getHeader('X-Appwrite-Key', ''), $project->getAttribute('keys', []));
2020-01-12 10:53:57 +13:00
2019-10-25 06:53:37 +13:00
/*
* Try app auth when we have project key and no user
* Mock user to app and grant API key scopes in addition to default app scopes
*/
if (null !== $key && $user->isEmpty()) {
$user = new Document([
2020-02-17 20:16:11 +13:00
'$id' => 0,
2019-10-25 06:53:37 +13:00
'status' => Auth::USER_STATUS_ACTIVATED,
2020-02-17 20:16:11 +13:00
'email' => 'app.'.$project->getId().'@service.'.$domain,
2019-10-25 06:53:37 +13:00
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$role = Auth::USER_ROLE_APP;
2020-01-13 04:20:51 +13:00
$scopes = array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
2019-10-25 06:53:37 +13:00
2020-02-11 19:17:40 +13:00
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
2019-10-25 06:53:37 +13:00
}
2019-05-09 18:54:39 +12:00
2020-02-17 20:16:11 +13:00
Authorization::setRole('user:'.$user->getId());
2019-10-25 06:53:37 +13:00
Authorization::setRole('role:'.$role);
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
array_map(function ($node) {
if (isset($node['teamId']) && isset($node['roles'])) {
Authorization::setRole('team:'.$node['teamId']);
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
foreach ($node['roles'] as $nodeRole) { // Set all team roles
Authorization::setRole('team:'.$node['teamId'].'/'.$nodeRole);
}
}
}, $user->getAttribute('memberships', []));
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
// TDOO Check if user is god
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
if (!in_array($scope, $scopes)) {
2020-02-17 20:16:11 +13:00
if (empty($project->getId()) || Database::SYSTEM_COLLECTION_PROJECTS !== $project->getCollection()) { // Check if permission is denied because project is missing
throw new Exception('Project not found', 404);
}
2019-10-25 06:53:37 +13:00
throw new Exception($user->getAttribute('email', 'Guest').' (role: '.strtolower($roles[$role]['label']).') missing scope ('.$scope.')', 401);
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
if (Auth::USER_STATUS_BLOCKED == $user->getAttribute('status')) { // Account has not been activated
throw new Exception('Invalid credentials. User is blocked', 401); // User is in status blocked
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
if ($user->getAttribute('reset')) {
throw new Exception('Password reset is required', 412);
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
/*
* Background Jobs
*/
$webhook
2020-02-17 20:16:11 +13:00
->setParam('projectId', $project->getId())
2019-10-25 06:53:37 +13:00
->setParam('event', $route->getLabel('webhook', ''))
->setParam('payload', [])
;
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$audit
2020-02-17 20:16:11 +13:00
->setParam('projectId', $project->getId())
->setParam('userId', $user->getId())
2019-10-25 06:53:37 +13:00
->setParam('event', '')
->setParam('resource', '')
->setParam('userAgent', $request->getServer('HTTP_USER_AGENT', ''))
->setParam('ip', $request->getIP())
->setParam('data', [])
;
2019-10-25 06:53:37 +13:00
$usage
2020-02-17 20:16:11 +13:00
->setParam('projectId', $project->getId())
2019-10-25 06:53:37 +13:00
->setParam('url', $request->getServer('HTTP_HOST', '').$request->getServer('REQUEST_URI', ''))
->setParam('method', $request->getServer('REQUEST_METHOD', 'UNKNOWN'))
->setParam('request', 0)
->setParam('response', 0)
->setParam('storage', 0)
;
});
2019-05-09 18:54:39 +12:00
$utopia->shutdown(function () use ($response, $request, $webhook, $audit, $usage, $mode, $project, $utopia) {
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
/*
* Trigger Events for background jobs
*/
if (!empty($webhook->getParam('event'))) {
$webhook->trigger();
}
2020-01-12 02:58:02 +13:00
2019-10-25 06:53:37 +13:00
if (!empty($audit->getParam('event'))) {
$audit->trigger();
}
$route = $utopia->match($request);
2019-05-09 18:54:39 +12:00
2020-02-17 20:16:11 +13:00
if($project->getId()
&& $mode !== APP_MODE_ADMIN
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage and admin mode
$usage
->setParam('request', $request->getSize())
->setParam('response', $response->getSize())
->trigger()
;
}
2019-10-25 06:53:37 +13:00
});
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$utopia->options(function () use ($request, $response, $domain, $project) {
$origin = $request->getServer('HTTP_ORIGIN');
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$response
->addHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, PATCH, DELETE')
->addHeader('Access-Control-Allow-Headers', 'Origin, Cookie, Set-Cookie, X-Requested-With, Content-Type, Access-Control-Allow-Origin, Access-Control-Request-Headers, Accept, X-Appwrite-Project, X-Appwrite-Key, X-Appwrite-Locale, X-Appwrite-Mode, X-SDK-Version')
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
->send();
});
2019-05-09 18:54:39 +12:00
2019-11-29 20:35:26 +13:00
$utopia->error(function ($error /* @var $error Exception */) use ($request, $response, $utopia, $project, $env, $version) {
2019-10-25 06:53:37 +13:00
switch ($error->getCode()) {
case 400: // Error allowed publicly
case 401: // Error allowed publicly
case 402: // Error allowed publicly
case 403: // Error allowed publicly
case 404: // Error allowed publicly
2020-01-04 10:01:09 +13:00
case 409: // Error allowed publicly
2019-10-25 06:53:37 +13:00
case 412: // Error allowed publicly
case 429: // Error allowed publicly
$code = $error->getCode();
$message = $error->getMessage();
break;
default:
$code = 500; // All other errors get the generic 500 server error status code
$message = 'Server Error';
}
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
$output = ((App::ENV_TYPE_DEVELOPMENT == $env)) ? [
'message' => $error->getMessage(),
'code' => $error->getCode(),
'file' => $error->getFile(),
'line' => $error->getLine(),
'trace' => $error->getTrace(),
'version' => $version,
] : [
'message' => $message,
'code' => $code,
'version' => $version,
];
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Expires', '0')
->addHeader('Pragma', 'no-cache')
->setStatusCode($code)
2019-10-25 06:53:11 +13:00
;
2019-05-09 18:54:39 +12:00
2019-10-25 06:53:37 +13:00
$route = $utopia->match($request);
$template = ($route) ? $route->getLabel('error', null) : null;
if ($template) {
$layout = new View(__DIR__.'/views/layouts/default.phtml');
$comp = new View($template);
$comp
->setParam('projectName', $project->getAttribute('name'))
->setParam('projectURL', $project->getAttribute('url'))
->setParam('message', $error->getMessage())
->setParam('code', $code)
;
$layout
->setParam('title', $project->getAttribute('name').' - Error')
->setParam('description', 'No Description')
->setParam('body', $comp)
->setParam('version', $version)
->setParam('litespeed', false)
;
$response->send($layout->render());
2019-10-25 06:53:11 +13:00
}
2019-10-25 06:53:37 +13:00
$response
->json($output)
;
});
$utopia->get('/manifest.json')
->desc('Progressive app manifest file')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$response->json([
'name' => APP_NAME,
'short_name' => APP_NAME,
'start_url' => '.',
'url' => 'https://appwrite.io/',
'display' => 'standalone',
'background_color' => '#fff',
'theme_color' => '#f02e65',
'description' => 'End to end backend server for frontend and mobile apps. 👩‍💻👨‍💻',
'icons' => [
[
'src' => 'images/favicon.png',
'sizes' => '256x256',
'type' => 'image/png',
],
],
]);
}
);
$utopia->get('/robots.txt')
->desc('Robots.txt File')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$template = new View(__DIR__.'/views/general/robots.phtml');
$response->text($template->render(false));
2019-10-25 06:53:37 +13:00
}
);
$utopia->get('/humans.txt')
->desc('Humans.txt File')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response) {
$template = new View(__DIR__.'/views/general/humans.phtml');
$response->text($template->render(false));
2019-10-25 06:53:37 +13:00
}
);
2020-02-19 11:13:18 +13:00
$utopia->get('/.well-known/acme-challenge')
->desc('SSL Verification')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($request, $response) {
2020-02-23 21:55:57 +13:00
$base = realpath(APP_STORAGE_CERTIFICATES);
2020-02-19 11:13:18 +13:00
$path = str_replace('/.well-known/acme-challenge/', '', $request->getParam('q'));
2020-02-23 21:55:57 +13:00
$absolute = realpath($base.'/.well-known/acme-challenge/'.$path);
if(!$base) {
throw new Exception('Storage error', 500);
}
2020-02-19 11:13:18 +13:00
if(!$absolute) {
2020-02-23 21:55:57 +13:00
throw new Exception('Unknown path', 404);
2020-02-19 11:13:18 +13:00
}
if(!substr($absolute, 0, strlen($base)) === $base) {
2020-02-23 21:55:57 +13:00
throw new Exception('Invalid path', 401);
}
2020-02-24 06:45:51 +13:00
if(!file_exists($absolute)) {
throw new Exception('Unknown path', 404);
}
2020-02-23 21:55:57 +13:00
$content = @file_get_contents($absolute);
if(!$content) {
throw new Exception('Failed to get contents', 500);
2020-02-19 11:13:18 +13:00
}
2020-02-23 21:55:57 +13:00
$response->text($content);
2020-02-19 11:13:18 +13:00
}
);
2020-02-23 22:07:01 +13:00
$utopia->get('/v1/info') // This is only visible to the gods
2020-01-12 02:58:02 +13:00
->label('scope', 'god')
2019-10-25 06:53:37 +13:00
->label('docs', false)
->action(
function () use ($response, $user, $project, $version, $env) {
2019-10-25 06:53:37 +13:00
$response->json([
'name' => 'API',
'version' => $version,
'environment' => $env,
'time' => date('Y-m-d H:i:s', time()),
'user' => [
2020-02-17 20:16:11 +13:00
'id' => $user->getId(),
2019-10-25 06:53:37 +13:00
'name' => $user->getAttribute('name', ''),
],
'project' => [
2020-02-17 20:16:11 +13:00
'id' => $project->getId(),
2019-10-25 06:53:37 +13:00
'name' => $project->getAttribute('name', ''),
],
]);
}
);
$utopia->get('/v1/xss')
->desc('Log XSS errors reported by browsers using X-XSS-Protection header')
->label('scope', 'public')
->label('docs', false)
->action(
function () {
throw new Exception('XSS detected and reported by a browser client', 500);
}
);
$utopia->get('/v1/proxy')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response, $console, $clients) {
$view = new View(__DIR__.'/views/proxy.phtml');
$view
->setParam('routes', '')
->setParam('clients', array_merge($clients, $console->getAttribute('clients', [])))
;
$response
->setContentType(Response::CONTENT_TYPE_HTML)
->removeHeader('X-Frame-Options')
->send($view->render());
}
);
2020-01-12 02:58:02 +13:00
$utopia->get('/v1/open-api-2.json')
2019-10-25 06:53:37 +13:00
->label('scope', 'public')
->label('docs', false)
2020-01-31 05:18:46 +13:00
->param('platform', 'client', function () {return new WhiteList([APP_PLATFORM_CLIENT, APP_PLATFORM_SERVER, APP_PLATFORM_CONSOLE]);}, 'Choose target platform.', true)
2019-10-25 06:53:37 +13:00
->param('extensions', 0, function () {return new Range(0, 1);}, 'Show extra data.', true)
->param('tests', 0, function () {return new Range(0, 1);}, 'Include only test services.', true)
->action(
function ($platform, $extensions, $tests) use ($response, $request, $utopia, $domain, $services) {
function fromCamelCase($input)
{
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
$ret = $matches[0];
foreach ($ret as &$match) {
$match = $match == strtoupper($match) ? strtolower($match) : lcfirst($match);
}
return implode('_', $ret);
}
function fromCamelCaseToDash($input)
{
return str_replace([' ', '_'], '-', strtolower(preg_replace('/([a-zA-Z])(?=[A-Z])/', '$1-', $input)));
}
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
if($tests && !$service['tests']) {
continue;
}
if (!$tests && !$service['sdk']) {
continue;
}
2019-12-17 08:35:33 +13:00
2019-10-25 06:53:37 +13:00
/** @noinspection PhpIncludeInspection */
include_once $service['controller'];
}
$security = [
2020-01-31 05:18:46 +13:00
APP_PLATFORM_CLIENT => ['Project' => []],
APP_PLATFORM_SERVER => ['Project' => [], 'Key' => []],
APP_PLATFORM_CONSOLE => ['Project' => [], 'Key' => []],
2019-10-25 06:53:37 +13:00
];
2020-01-29 01:56:09 +13:00
$platforms = [
'client' => APP_PLATFORM_CLIENT,
'server' => APP_PLATFORM_SERVER,
2020-01-31 05:18:46 +13:00
'all' => APP_PLATFORM_CONSOLE,
2020-01-29 01:56:09 +13:00
];
2019-10-25 06:53:37 +13:00
/*
* Specifications (v3.0.0):
* https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.0.md
*/
$output = [
'swagger' => '2.0',
'info' => [
'version' => APP_VERSION_STABLE,
'title' => APP_NAME,
'description' => 'Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https://appwrite.io/docs](https://appwrite.io/docs)',
'termsOfService' => 'https://appwrite.io/policy/terms',
'contact' => [
'name' => 'Appwrite Team',
'url' => 'https://appwrite.io/support',
'email' => APP_EMAIL_TEAM,
],
'license' => [
'name' => 'BSD-3-Clause',
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE',
],
],
'host' => parse_url($request->getServer('_APP_HOME', $domain), PHP_URL_HOST),
'basePath' => '/v1',
'schemes' => ['https'],
'consumes' => ['application/json', 'multipart/form-data'],
'produces' => ['application/json'],
'securityDefinitions' => [
'Project' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Project',
2020-02-16 07:22:34 +13:00
'description' => 'Your project ID',
2019-10-25 06:53:37 +13:00
'in' => 'header',
],
'Key' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Key',
2020-02-16 07:22:34 +13:00
'description' => 'Your secret API key',
2019-10-25 06:53:37 +13:00
'in' => 'header',
],
'Locale' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Locale',
'description' => '',
'in' => 'header',
],
'Mode' => [
'type' => 'apiKey',
'name' => 'X-Appwrite-Mode',
'description' => '',
'in' => 'header',
],
],
'paths' => [],
'definitions' => [
2019-12-06 06:43:16 +13:00
// 'Pet' => [
// 'required' => ['id', 'name'],
// 'properties' => [
// 'id' => [
// 'type' => 'integer',
// 'format' => 'int64',
// ],
// 'name' => [
// 'type' => 'string',
// ],
// 'tag' => [
// 'type' => 'string',
// ],
// ],
// ],
// 'Pets' => array(
// 'type' => 'array',
// 'items' => array(
// '$ref' => '#/definitions/Pet',
// ),
// ),
2019-10-25 06:53:37 +13:00
'Error' => array(
'required' => array(
0 => 'code',
1 => 'message',
),
'properties' => array(
'code' => array(
'type' => 'integer',
'format' => 'int32',
),
'message' => array(
'type' => 'string',
),
),
),
],
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials',
'url' => $request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/docs',
],
];
2020-01-29 01:56:09 +13:00
if ($extensions) {
$output['securityDefinitions']['Project']['extensions'] = ['demo' => '5df5acd0d48c2'];
$output['securityDefinitions']['Key']['extensions'] = ['demo' => '919c2d18fb5d4...a2ae413da83346ad2'];
$output['securityDefinitions']['Locale']['extensions'] = ['demo' => 'en'];
$output['securityDefinitions']['Mode']['extensions'] = ['demo' => ''];
}
2019-10-25 06:53:37 +13:00
foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /* @var $route \Utopia\Route */
if (!$route->getLabel('docs', true)) {
continue;
}
if (empty($route->getLabel('sdk.namespace', null))) {
continue;
}
2020-01-31 05:18:46 +13:00
if($platform !== APP_PLATFORM_CONSOLE && !in_array($platforms[$platform], $route->getLabel('sdk.platform', []))) {
2020-01-29 01:56:09 +13:00
continue;
}
2019-10-25 06:53:37 +13:00
$url = str_replace('/v1', '', $route->getURL());
$scope = $route->getLabel('scope', '');
$hide = $route->getLabel('sdk.hide', false);
$consumes = ['application/json'];
if ($hide) {
continue;
}
2019-12-13 18:51:03 +13:00
$desc = (!empty($route->getLabel('sdk.description', ''))) ? realpath(__DIR__ . '/..' . $route->getLabel('sdk.description', '')) : null;
2019-10-25 06:53:37 +13:00
$temp = [
'summary' => $route->getDesc(),
'operationId' => $route->getLabel('sdk.method', uniqid()),
'consumes' => [],
'tags' => [$route->getLabel('sdk.namespace', 'default')],
'description' => ($desc) ? file_get_contents($desc) : '',
2019-12-14 17:14:40 +13:00
// 'responses' => [
// 200 => [
// 'description' => 'An paged array of pets',
// 'schema' => [
// '$ref' => '#/definitions/Pet',
// ],
// ],
// ],
2019-10-25 06:53:37 +13:00
];
if ($extensions) {
2020-02-08 12:08:14 +13:00
$platformList = $route->getLabel('sdk.platform', []);
if(in_array(APP_PLATFORM_CLIENT, $platformList)) {
$platformList = array_merge($platformList, [
APP_PLATFORM_WEB,
APP_PLATFORM_IOS,
APP_PLATFORM_ANDROID,
APP_PLATFORM_FLUTTER,
]);
}
2019-10-25 06:53:37 +13:00
$temp['extensions'] = [
'weight' => $route->getOrder(),
2019-12-14 17:14:40 +13:00
'cookies' => $route->getLabel('sdk.cookies', false),
2019-10-25 06:53:37 +13:00
'location' => $route->getLabel('sdk.location', false),
'demo' => 'docs/examples/'.fromCamelCaseToDash($route->getLabel('sdk.namespace', 'default')).'/'.fromCamelCaseToDash($temp['operationId']).'.md',
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route->getLabel('sdk.description', ''),
'rate-limit' => $route->getLabel('abuse-limit', 0),
'rate-time' => $route->getLabel('abuse-time', 3600),
'scope' => $route->getLabel('scope', ''),
2020-02-08 12:08:14 +13:00
'platforms' => $platformList,
2019-10-25 06:53:37 +13:00
];
}
if ((!empty($scope))) { // && 'public' != $scope
2019-10-25 06:53:37 +13:00
$temp['security'][] = $route->getLabel('sdk.security', $security[$platform]);
}
$requestBody = [
'content' => [
'application/x-www-form-urlencoded' => [
'schema' => [
'type' => 'object',
'properties' => [],
],
'required' => [],
],
],
];
foreach ($route->getParams() as $name => $param) {
$validator = (is_callable($param['validator'])) ? $param['validator']() : $param['validator']; /* @var $validator \Utopia\Validator */
$node = [
'name' => $name,
'description' => $param['description'],
'required' => !$param['optional'],
];
switch ((!empty($validator)) ? get_class($validator) : '') {
case 'Utopia\Validator\Text':
$node['type'] = 'string';
$node['x-example'] = '['.strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Database\Validator\UID':
$node['type'] = 'string';
$node['x-example'] = '['.strtoupper(fromCamelCase($node['name'])).']';
break;
case 'Utopia\Validator\Email':
$node['type'] = 'string';
$node['format'] = 'email';
$node['x-example'] = 'email@example.com';
break;
case 'Utopia\Validator\URL':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\JSON':
case 'Utopia\Validator\Mock':
2020-02-08 12:08:14 +13:00
case 'Utopia\Validator\Assoc':
$node['type'] = 'object';
2019-10-25 06:53:37 +13:00
$node['type'] = 'object';
$node['x-example'] = '{}';
//$node['format'] = 'json';
break;
case 'Storage\Validators\File':
$consumes = ['multipart/form-data'];
$node['type'] = 'file';
break;
case 'Utopia\Validator\ArrayList':
$node['type'] = 'array';
$node['collectionFormat'] = 'multi';
$node['items'] = [
'type' => 'string',
];
break;
case 'Auth\Validator\Password':
$node['type'] = 'string';
$node['format'] = 'format';
$node['x-example'] = 'password';
break;
case 'Utopia\Validator\Range': /* @var $validator \Utopia\Validator\Range */
$node['type'] = 'integer';
$node['format'] = 'int32';
$node['x-example'] = $validator->getMin();
break;
case 'Utopia\Validator\Numeric':
$node['type'] = 'integer';
$node['format'] = 'int32';
break;
case 'Utopia\Validator\Length':
$node['type'] = 'string';
break;
case 'Utopia\Validator\Host':
$node['type'] = 'string';
$node['format'] = 'url';
$node['x-example'] = 'https://example.com';
break;
case 'Utopia\Validator\WhiteList': /* @var $validator \Utopia\Validator\WhiteList */
$node['type'] = 'string';
$node['x-example'] = $validator->getList()[0];
break;
default:
$node['type'] = 'string';
break;
}
if ($param['optional'] && !is_null($param['default'])) { // Param has default value
$node['default'] = $param['default'];
}
if (false !== strpos($url, ':'.$name)) { // Param is in URL path
$node['in'] = 'path';
$temp['parameters'][] = $node;
} elseif ($key == 'GET') { // Param is in query
$node['in'] = 'query';
$temp['parameters'][] = $node;
} else { // Param is in payload
$node['in'] = 'formData';
$temp['parameters'][] = $node;
$requestBody['content']['application/x-www-form-urlencoded']['schema']['properties'][] = $node;
if (!$param['optional']) {
$requestBody['content']['application/x-www-form-urlencoded']['required'][] = $name;
}
}
$url = str_replace(':'.$name, '{'.$name.'}', $url);
}
$temp['consumes'] = $consumes;
$output['paths'][$url][strtolower($route->getMethod())] = $temp;
}
}
/*foreach ($consoleDB->getMocks() as $mock) {
var_dump($mock['name']);
}*/
ksort($output['paths']);
2019-12-17 08:35:33 +13:00
$response
->json($output);
2019-10-25 06:53:37 +13:00
}
);
2020-01-05 04:45:59 +13:00
$utopia->get('/v1/debug')
->label('scope', 'public')
->label('docs', false)
->action(
function () use ($response, $request, $utopia, $domain, $services) {
$output = [
2020-01-12 02:58:02 +13:00
'scopes' => [],
2020-01-05 04:45:59 +13:00
'webhooks' => [],
'methods' => [],
'routes' => [],
2020-01-06 00:28:44 +13:00
'docs' => [],
2020-01-05 04:45:59 +13:00
];
foreach ($services as $service) { /* @noinspection PhpIncludeInspection */
/** @noinspection PhpIncludeInspection */
if($service['tests']) {
continue;
}
include_once $service['controller'];
}
$i = 0;
foreach ($utopia->getRoutes() as $key => $method) {
foreach ($method as $route) { /* @var $route \Utopia\Route */
if (!$route->getLabel('docs', true)) {
continue;
}
if (empty($route->getLabel('sdk.namespace', null))) {
continue;
}
2020-01-12 02:58:02 +13:00
if ($route->getLabel('scope', false)) {
$output['scopes'][$route->getLabel('scope', false)] = $route->getMethod().' '.$route->getURL();
}
2020-01-06 00:28:44 +13:00
if ($route->getLabel('sdk.description', false)) {
2020-01-06 12:22:02 +13:00
if(!realpath(__DIR__.'/../'.$route->getLabel('sdk.description', false))) {
throw new Exception('Docs file ('.$route->getLabel('sdk.description', false).') is missing', 500);
}
2020-01-06 00:28:44 +13:00
if(array_key_exists($route->getLabel('sdk.description', false), $output['docs'])) {
throw new Exception('Docs file ('.$route->getLabel('sdk.description', false).') is already in use by another route', 500);
}
$output['docs'][$route->getLabel('sdk.description', false)] = $route->getMethod().' '.$route->getURL();
}
2020-01-05 04:45:59 +13:00
if ($route->getLabel('webhook', false)) {
if(array_key_exists($route->getLabel('webhook', false), $output['webhooks'])) {
2020-01-06 00:28:44 +13:00
//throw new Exception('Webhook ('.$route->getLabel('webhook', false).') is already in use by another route', 500);
2020-01-05 04:45:59 +13:00
}
$output['webhooks'][$route->getLabel('webhook', false)] = $route->getMethod().' '.$route->getURL();
}
if ($route->getLabel('sdk.namespace', false)) {
$method = $route->getLabel('sdk.namespace', false).'->'.$route->getLabel('sdk.method', false).'()';
if(array_key_exists($method, $output['methods'])) {
throw new Exception('Method ('.$method.') is already in use by another route', 500);
}
$output['methods'][$method] = $route->getMethod().' '.$route->getURL();
}
$output['routes'][$route->getURL().' ('.$route->getMethod().')'] = [];
$i++;
}
}
2020-01-12 02:58:02 +13:00
ksort($output['scopes']);
2020-01-05 04:45:59 +13:00
ksort($output['webhooks']);
ksort($output['methods']);
ksort($output['routes']);
2020-01-06 00:28:44 +13:00
ksort($output['docs']);
2020-01-05 04:45:59 +13:00
$response
->json($output);
}
);
2019-10-25 06:53:37 +13:00
$name = APP_NAME;
if (array_key_exists($service, $services)) { /** @noinspection PhpIncludeInspection */
include_once $services[$service]['controller'];
$name = APP_NAME.' '.ucfirst($services[$service]['name']);
} else {
/** @noinspection PhpIncludeInspection */
include_once $services['/']['controller'];
2019-05-09 18:54:39 +12:00
}
2019-10-25 06:53:37 +13:00
$utopia->run($request, $response);