2019-05-09 18:54:39 +12:00
< ? php
// Init
2019-10-01 17:57:41 +13:00
require_once __DIR__ . '/init.php' ;
2019-05-09 18:54:39 +12:00
2019-09-04 08:37:34 +12:00
global $env , $utopia , $request , $response , $register , $consoleDB , $project , $domain , $sentry , $version , $service , $providers ;
2019-05-09 18:54:39 +12:00
use Utopia\App ;
use Utopia\Request ;
use Utopia\Response ;
use Utopia\Validator\Host ;
use Utopia\Validator\Range ;
use Utopia\View ;
use Utopia\Exception ;
use Utopia\Abuse\Abuse ;
use Utopia\Abuse\Adapters\TimeLimit ;
use Auth\Auth ;
use Database\Document ;
use Database\Validator\Authorization ;
use Event\Event ;
2019-09-21 06:44:32 +12:00
use Utopia\Validator\WhiteList ;
2019-05-09 18:54:39 +12:00
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Configuration files
*/
2019-10-01 17:57:41 +13:00
$roles = include __DIR__ . '/config/roles.php' ; // User roles and scopes
$sdks = include __DIR__ . '/config/sdks.php' ; // List of SDK clients
$services = include __DIR__ . '/config/services.php' ; // List of SDK clients
2019-05-09 18:54:39 +12:00
2019-10-01 17:57:41 +13:00
$webhook = new Event ( 'v1-webhooks' , 'WebhooksV1' );
$audit = new Event ( 'v1-audits' , 'AuditsV1' );
$usage = new Event ( 'v1-usage' , 'UsageV1' );
2019-05-09 18:54:39 +12:00
2019-09-30 19:13:40 +13:00
$clientsConsole = array_map ( function ( $node ) {
2019-08-11 03:24:47 +12:00
return $node [ 'url' ];
2019-09-30 19:13:40 +13:00
}, array_filter ( $console -> getAttribute ( 'platforms' , []), function ( $node ) {
if ( isset ( $node [ 'type' ]) && $node [ 'type' ] === 'web' && isset ( $node [ 'url' ]) && ! empty ( $node [ 'url' ])) {
2019-08-11 03:24:47 +12:00
return true ;
}
2019-10-01 17:57:41 +13:00
2019-08-11 03:24:47 +12:00
return false ;
}));
2019-09-30 19:13:40 +13:00
$clients = array_merge ( $clientsConsole , array_map ( function ( $node ) {
2019-08-26 05:18:06 +12:00
return $node [ 'url' ];
2019-09-30 19:13:40 +13:00
}, array_filter ( $project -> getAttribute ( 'platforms' , []), function ( $node ) {
if ( isset ( $node [ 'type' ]) && $node [ 'type' ] === 'web' && isset ( $node [ 'url' ]) && ! empty ( $node [ 'url' ])) {
2019-08-26 05:18:06 +12:00
return true ;
}
2019-10-01 17:57:41 +13:00
2019-08-26 05:18:06 +12:00
return false ;
})));
2019-09-30 19:13:40 +13:00
$utopia -> init ( function () use ( $utopia , $request , $response , $register , & $user , $project , $roles , $webhook , $audit , $usage , $domain , $clients ) {
2019-05-09 18:54:39 +12:00
$route = $utopia -> match ( $request );
2019-10-01 17:57:41 +13:00
$referrer = $request -> getServer ( 'HTTP_REFERER' , '' );
$origin = $request -> getServer ( 'HTTP_ORIGIN' , parse_url ( $referrer , PHP_URL_SCHEME ) . '://' . parse_url ( $referrer , PHP_URL_HOST ));
2019-05-09 18:54:39 +12:00
2019-08-11 03:24:47 +12:00
$refDomain = ( in_array ( $origin , $clients ))
2019-05-09 18:54:39 +12:00
? $origin : 'http://localhost' ;
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Security Headers
*
* As recommended at :
* @ see https :// www . owasp . org / index . php / List_of_useful_HTTP_headers
*/
$response
-> addHeader ( 'Server' , 'Appwrite' )
2019-10-01 17:57:41 +13:00
-> addHeader ( 'X-XSS-Protection' , '1; mode=block; report=/v1/xss?url=' . urlencode ( $request -> getServer ( 'REQUEST_URI' )))
2019-05-09 18:54:39 +12:00
//->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' )
2019-08-24 19:30:12 +12:00
-> addHeader ( 'Access-Control-Allow-Headers' , 'Origin, 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-SDK-Version' )
2019-05-09 18:54:39 +12:00
-> addHeader ( 'Access-Control-Allow-Origin' , $refDomain )
-> addHeader ( 'Access-Control-Allow-Credentials' , 'true' )
;
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Validate Client Domain - Check to avoid CSRF attack
* Adding appwrite api domains to allow XDOMAIN communication
*/
2019-08-24 19:30:12 +12:00
$hostValidator = new Host ( $clients );
2019-05-09 18:54:39 +12:00
2019-09-30 19:13:40 +13:00
if ( ! $hostValidator -> isValid ( $request -> getServer ( 'HTTP_ORIGIN' , $request -> getServer ( 'HTTP_REFERER' , '' )))
2019-05-09 18:54:39 +12:00
&& in_array ( $request -> getMethod (), [ Request :: METHOD_POST , Request :: METHOD_PUT , Request :: METHOD_PATCH , Request :: METHOD_DELETE ])
&& empty ( $request -> getHeader ( 'X-Appwrite-Key' , '' ))) {
2019-10-01 17:57:41 +13:00
throw new Exception ( 'Access from this client host is forbidden. ' . $hostValidator -> getDescription (), 403 );
2019-05-09 18:54:39 +12:00
}
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12: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' , []));
2019-09-30 19:13:40 +13:00
if ( $membership ) {
2019-05-09 18:54:39 +12:00
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-10-01 17:57:41 +13:00
$scope = $route -> getLabel ( 'scope' , 'none' ); // Allowed scope for chosen route
$scopes = $roles [ $role ][ 'scopes' ]; // Allowed scopes for user role
2019-05-09 18:54:39 +12:00
// Check if given key match project API keys
$key = $project -> search ( 'secret' , $request -> getHeader ( 'X-Appwrite-Key' , '' ), $project -> getAttribute ( 'keys' , []));
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12: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
*/
2019-09-30 19:13:40 +13:00
if ( null !== $key && $user -> isEmpty ()) {
2019-05-09 18:54:39 +12:00
$user = new Document ([
2019-10-01 17:57:41 +13:00
'$uid' => 0 ,
'status' => Auth :: USER_STATUS_ACTIVATED ,
'email' => 'app.' . $project -> getUid () . '@service.' . $domain ,
'password' => '' ,
'name' => $project -> getAttribute ( 'name' , 'Untitled' ),
2019-05-09 18:54:39 +12:00
]);
2019-10-01 17:57:41 +13:00
$role = Auth :: USER_ROLE_APP ;
2019-05-09 18:54:39 +12:00
$scopes = array_merge ( $roles [ $role ][ 'scopes' ], $key -> getAttribute ( 'scopes' , []));
Authorization :: disable (); // Cancel security segmentation for API keys.
}
2019-10-01 17:57:41 +13:00
Authorization :: setRole ( 'user:' . $user -> getUid ());
Authorization :: setRole ( 'role:' . $role );
2019-05-09 18:54:39 +12:00
array_map ( function ( $node ) {
2019-09-30 19:13:40 +13:00
if ( isset ( $node [ 'teamId' ]) && isset ( $node [ 'roles' ])) {
2019-10-01 17:57:41 +13:00
Authorization :: setRole ( 'team:' . $node [ 'teamId' ]);
2019-05-09 18:54:39 +12:00
foreach ( $node [ 'roles' ] as $nodeRole ) { // Set all team roles
2019-10-01 17:57:41 +13:00
Authorization :: setRole ( 'team:' . $node [ 'teamId' ] . '/' . $nodeRole );
2019-05-09 18:54:39 +12:00
}
}
}, $user -> getAttribute ( 'memberships' , []));
2019-09-30 19:13:40 +13:00
if ( ! in_array ( $scope , $scopes )) {
2019-10-01 17:57:41 +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-09-30 19:13:40 +13:00
if ( Auth :: USER_STATUS_BLOCKED == $user -> getAttribute ( 'status' )) { // Account has not been activated
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Invalid credentials. User is blocked' , 401 ); // User is in status blocked
}
2019-09-30 19:13:40 +13:00
if ( $user -> getAttribute ( 'reset' )) {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'Password reset is required' , 412 );
}
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Background Jobs
*/
$webhook
-> setParam ( 'projectId' , $project -> getUid ())
-> setParam ( 'event' , $route -> getLabel ( 'webhook' , '' ))
-> setParam ( 'payload' , [])
;
$audit
-> setParam ( 'projectId' , $project -> getUid ())
-> setParam ( 'userId' , $user -> getUid ())
-> setParam ( 'event' , '' )
-> setParam ( 'resource' , '' )
-> setParam ( 'userAgent' , $request -> getServer ( 'HTTP_USER_AGENT' , '' ))
-> setParam ( 'ip' , $request -> getIP ())
-> setParam ( 'data' , [])
;
$usage
-> setParam ( 'projectId' , $project -> getUid ())
2019-10-01 17:57:41 +13:00
-> setParam ( 'url' , $request -> getServer ( 'HTTP_HOST' , '' ) . $request -> getServer ( 'REQUEST_URI' , '' ))
2019-05-09 18:54:39 +12:00
-> setParam ( 'method' , $request -> getServer ( 'REQUEST_METHOD' , 'UNKNOWN' ))
-> setParam ( 'request' , 0 )
-> setParam ( 'response' , 0 )
-> setParam ( 'storage' , 0 )
;
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Abuse Check
*/
2019-09-30 19:13:40 +13:00
$timeLimit = new TimeLimit ( $route -> getLabel ( 'abuse-key' , 'url:{url},ip:{ip}' ), $route -> getLabel ( 'abuse-limit' , 0 ), $route -> getLabel ( 'abuse-time' , 3600 ), function () use ( $register ) {
return $register -> get ( 'db' );
});
2019-10-01 17:57:41 +13:00
$timeLimit -> setNamespace ( 'app_' . $project -> getUid ());
2019-05-09 18:54:39 +12:00
$timeLimit
-> setParam ( '{userId}' , $user -> getUid ())
-> setParam ( '{userAgent}' , $request -> getServer ( 'HTTP_USER_AGENT' , '' ))
-> setParam ( '{ip}' , $request -> getIP ())
2019-10-01 17:57:41 +13:00
-> setParam ( '{url}' , $request -> getServer ( 'HTTP_HOST' , '' ) . $route -> getURL ())
2019-05-09 18:54:39 +12:00
;
2019-06-09 01:13:19 +12:00
//TODO make sure we get array here
2019-09-30 19:13:40 +13:00
foreach ( $request -> getParams () as $key => $value ) { // Set request params as potential abuse keys
2019-10-01 17:57:41 +13:00
$timeLimit -> setParam ( '{param-' . $key . '}' , ( is_array ( $value )) ? json_encode ( $value ) : $value );
2019-05-09 18:54:39 +12:00
}
$abuse = new Abuse ( $timeLimit );
2019-09-30 19:13:40 +13:00
if ( $timeLimit -> limit ()) {
2019-08-24 23:35:30 +12:00
$response
-> addHeader ( 'X-RateLimit-Limit' , $timeLimit -> limit ())
-> addHeader ( 'X-RateLimit-Remaining' , $timeLimit -> remaining ())
-> addHeader ( 'X-RateLimit-Reset' , $timeLimit -> time () + $route -> getLabel ( 'abuse-time' , 3600 ))
;
}
2019-10-01 17:57:41 +13:00
2019-09-30 19:13:40 +13:00
if ( $abuse -> check () && $request -> getServer ( '_APP_OPTIONS_ABUSE' , 'enabled' ) !== 'disabled' ) {
2019-09-15 00:47:47 +12:00
throw new Exception ( 'Too many requests' , 429 );
2019-05-09 18:54:39 +12:00
}
});
$utopia -> shutdown ( function () use ( $response , $request , $webhook , $audit , $usage ) {
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Trigger Events for background jobs
*/
2019-09-30 19:13:40 +13:00
if ( ! empty ( $webhook -> getParam ( 'event' ))) {
2019-05-09 18:54:39 +12:00
$webhook -> trigger ();
}
2019-09-30 19:13:40 +13:00
if ( ! empty ( $audit -> getParam ( 'event' ))) {
2019-05-09 18:54:39 +12:00
$audit -> trigger ();
}
$usage
-> setParam ( 'request' , $request -> getSize ())
-> setParam ( 'response' , $response -> getSize ())
-> trigger ()
;
});
2019-09-30 19:13:40 +13:00
$utopia -> options ( function () use ( $request , $response , $domain , $project ) {
2019-07-06 21:48:57 +12:00
$origin = $request -> getServer ( 'HTTP_ORIGIN' );
2019-05-09 18:54:39 +12:00
$response
-> addHeader ( 'Access-Control-Allow-Methods' , 'GET, POST, PUT, PATCH, DELETE' )
2019-08-24 19:30:12 +12:00
-> addHeader ( 'Access-Control-Allow-Headers' , 'Origin, 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-SDK-Version' )
2019-05-09 18:54:39 +12:00
-> addHeader ( 'Access-Control-Allow-Origin' , $origin )
-> addHeader ( 'Access-Control-Allow-Credentials' , 'true' )
-> send ();
});
2019-09-30 19:13:40 +13:00
$utopia -> error ( function ( $error /* @var $error Exception */ ) use ( $request , $response , $utopia , $project , $env , $version , $sentry , $user ) {
switch ( $error -> getCode ()) {
2019-05-09 18:54:39 +12:00
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
case 412 : // Error allowed publicly
case 429 : // Error allowed publicly
2019-10-01 17:57:41 +13:00
$code = $error -> getCode ();
$message = $error -> getMessage ();
2019-05-09 18:54:39 +12:00
break ;
default :
2019-10-01 17:57:41 +13:00
$code = 500 ; // All other errors get the generic 500 server error status code
$message = 'Server Error' ;
2019-05-09 18:54:39 +12:00
}
$_SERVER = []; // Reset before reporting to error log to avoid keys being compromised
2019-08-11 18:15:23 +12:00
$output = (( App :: ENV_TYPE_DEVELOPMENT == $env )) ? [
2019-10-01 17:57:41 +13:00
'message' => $error -> getMessage (),
'code' => $error -> getCode (),
'file' => $error -> getFile (),
'line' => $error -> getLine (),
'trace' => $error -> getTrace (),
'version' => $version ,
2019-05-09 18:54:39 +12:00
] : [
'message' => $message ,
2019-10-01 17:57:41 +13:00
'code' => $code ,
'version' => $version ,
2019-05-09 18:54:39 +12:00
];
$response
-> addHeader ( 'Cache-Control' , 'no-cache, no-store, must-revalidate' )
-> addHeader ( 'Expires' , '0' )
-> addHeader ( 'Pragma' , 'no-cache' )
-> setStatusCode ( $code )
;
2019-10-01 17:57:41 +13:00
$route = $utopia -> match ( $request );
$template = ( $route ) ? $route -> getLabel ( 'error' , null ) : null ;
2019-05-09 18:54:39 +12:00
2019-09-30 19:13:40 +13:00
if ( $template ) {
2019-10-01 17:57:41 +13:00
$layout = new View ( __DIR__ . '/views/layouts/default.phtml' );
$comp = new View ( $template );
2019-05-09 18:54:39 +12:00
$comp
-> setParam ( 'projectName' , $project -> getAttribute ( 'name' ))
-> setParam ( 'projectURL' , $project -> getAttribute ( 'url' ))
-> setParam ( 'message' , $error -> getMessage ())
-> setParam ( 'code' , $code )
;
$layout
2019-10-01 17:57:41 +13:00
-> setParam ( 'title' , $project -> getAttribute ( 'name' ) . ' - Error' )
2019-05-09 18:54:39 +12:00
-> setParam ( 'description' , 'No Description' )
-> setParam ( 'body' , $comp )
-> setParam ( 'version' , $version )
-> setParam ( 'litespeed' , false )
;
$response -> send ( $layout -> render ());
}
$response
-> json ( $output )
;
});
2019-07-30 19:54:12 +12:00
$utopia -> get ( '/manifest.json' )
-> desc ( 'Progressive app manifest file' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> action (
2019-09-30 19:13:40 +13:00
function () use ( $response ) {
2019-07-30 19:54:12 +12:00
$response -> json ([
2019-08-03 17:58:44 +12:00
'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' => [
2019-07-30 19:54:12 +12:00
[
'src' => 'images/favicon.png' ,
'sizes' => '256x256' ,
2019-10-01 17:57:41 +13:00
'type' => 'image/png' ,
],
],
2019-07-30 19:54:12 +12:00
]);
}
);
$utopia -> get ( '/robots.txt' )
-> desc ( 'Robots.txt File' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> action (
2019-09-30 19:13:40 +13:00
function () use ( $response ) {
2019-10-01 17:57:41 +13:00
$response -> text ( ' # robotstxt.org/
2019-07-30 19:54:12 +12:00
User - agent : *
2019-10-01 17:57:41 +13:00
' );
2019-07-30 19:54:12 +12:00
}
);
$utopia -> get ( '/humans.txt' )
-> desc ( 'Humans.txt File' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> action (
2019-09-30 19:13:40 +13:00
function () use ( $response ) {
2019-10-01 17:57:41 +13:00
$response -> text ( ' # humanstxt.org/
2019-07-30 19:54:12 +12:00
# The humans responsible & technology colophon
# TEAM
< name > -- < role > -- < twitter >
# THANKS
2019-10-01 17:57:41 +13:00
< name > ' );
2019-07-30 19:54:12 +12:00
}
);
2019-05-09 18:54:39 +12:00
$utopia -> get ( '/v1/info' ) // This is only visible to gods
2019-08-03 17:58:44 +12:00
-> label ( 'scope' , 'god' )
2019-05-09 18:54:39 +12:00
-> label ( 'docs' , false )
-> action (
2019-09-30 19:13:40 +13:00
function () use ( $response , $user , $project , $version , $env ) { //TODO CONSIDER BLOCKING THIS ACTION TO ROLE GOD
2019-05-09 18:54:39 +12:00
$response -> json ([
2019-10-01 17:57:41 +13:00
'name' => 'API' ,
'version' => $version ,
'environment' => $env ,
'time' => date ( 'Y-m-d H:i:s' , time ()),
'user' => [
2019-05-09 18:54:39 +12:00
'id' => $user -> getUid (),
'name' => $user -> getAttribute ( 'name' , '' ),
],
2019-10-01 17:57:41 +13:00
'project' => [
2019-05-09 18:54:39 +12:00
'id' => $project -> getUid (),
'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 (
2019-09-30 19:13:40 +13:00
function () {
2019-05-09 18:54:39 +12:00
throw new Exception ( 'XSS detected and reported by a browser client' , 500 );
}
);
$utopia -> get ( '/v1/proxy' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
-> action (
2019-09-30 19:13:40 +13:00
function () use ( $response , $console , $clients ) {
2019-10-01 17:57:41 +13:00
$view = new View ( __DIR__ . '/views/proxy.phtml' );
2019-05-09 18:54:39 +12:00
$view
-> setParam ( 'routes' , '' )
2019-08-11 03:24:47 +12:00
-> setParam ( 'clients' , array_merge ( $clients , $console -> getAttribute ( 'clients' , [])))
2019-05-09 18:54:39 +12:00
;
$response
-> setContentType ( Response :: CONTENT_TYPE_HTML )
-> removeHeader ( 'X-Frame-Options' )
-> send ( $view -> render ());
}
);
$utopia -> get ( '/v1/open-api-2.json' )
-> label ( 'scope' , 'public' )
-> label ( 'docs' , false )
2019-10-04 10:59:35 +13:00
-> param ( 'platform' , 'client' , function () { return new WhiteList ([ 'client' , 'server' ]);}, 'Choose target platform.' , true )
-> param ( 'extensions' , 0 , function () { return new Range ( 0 , 1 );}, 'Show extra data.' , true )
2019-05-09 18:54:39 +12:00
-> action (
2019-10-04 18:47:23 +13:00
function ( $platform , $extensions ) use ( $response , $request , $utopia , $domain , $services ) {
2019-09-30 19:13:40 +13:00
function fromCamelCase ( $input )
{
2019-05-09 18:54:39 +12:00
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 );
}
2019-10-01 17:57:41 +13:00
2019-05-09 18:54:39 +12:00
return implode ( '_' , $ret );
}
2019-09-30 19:13:40 +13:00
function fromCamelCaseToDash ( $input )
{
2019-08-20 08:55:41 +12:00
return str_replace ([ ' ' , '_' ], '-' , strtolower ( preg_replace ( '/([a-zA-Z])(?=[A-Z])/' , '$1-' , $input )));
2019-05-09 18:54:39 +12:00
}
2019-10-01 17:57:41 +13:00
foreach ( $services as $service ) { /* @noinspection PhpIncludeInspection */
2019-09-30 19:13:40 +13:00
if ( ! $service [ 'sdk' ]) {
2019-05-09 18:54:39 +12:00
continue ;
}
/** @noinspection PhpIncludeInspection */
include_once $service [ 'controller' ];
}
2019-09-21 07:31:46 +12:00
$security = [
'client' => [ 'Project' => []],
'server' => [ 'Project' => [], 'Key' => []],
];
2019-10-01 17:57:41 +13:00
/*
2019-05-09 18:54:39 +12:00
* Specifications ( v3 . 0.0 ) :
* https :// github . com / OAI / OpenAPI - Specification / blob / master / versions / 3.0 . 0. md
*/
$output = [
'swagger' => '2.0' ,
'info' => [
2019-10-04 18:47:23 +13:00
'version' => APP_VERSION_STABLE ,
2019-05-09 18:54:39 +12:00
'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' => [
2019-06-10 17:44:55 +12:00
'name' => 'BSD-3-Clause' ,
2019-06-09 20:51:10 +12:00
'url' => 'https://raw.githubusercontent.com/appwrite/appwrite/master/LICENSE' ,
2019-05-09 18:54:39 +12:00
],
],
2019-10-09 16:53:58 +13:00
'host' => parse_url ( $request -> getServer ( '_APP_HOME' , $domain ), PHP_URL_HOST ),
2019-05-09 18:54:39 +12:00
'basePath' => '/v1' ,
'schemes' => [ 'https' ],
2019-08-14 23:52:40 +12:00
'consumes' => [ 'application/json' , 'multipart/form-data' ],
2019-05-09 18:54:39 +12:00
'produces' => [ 'application/json' ],
2019-09-21 07:31:46 +12:00
'securityDefinitions' => [
'Project' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Project' ,
2019-10-10 02:58:22 +13:00
'description' => 'Your Appwrite project ID' ,
2019-09-21 07:31:46 +12:00
'in' => 'header' ,
],
'Key' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Key' ,
2019-10-10 02:58:22 +13:00
'description' => 'Your Appwrite project secret key' ,
2019-09-21 07:31:46 +12:00
'in' => 'header' ,
],
'Locale' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Locale' ,
'description' => '' ,
'in' => 'header' ,
],
'Mode' => [
'type' => 'apiKey' ,
'name' => 'X-Appwrite-Mode' ,
'description' => '' ,
'in' => 'header' ,
],
],
2019-05-09 18:54:39 +12:00
'paths' => [],
2019-09-21 06:44:32 +12:00
'definitions' => [
'Pet' => [
'required' => [ 'id' , 'name' ],
'properties' => [
'id' => [
'type' => 'integer' ,
'format' => 'int64' ,
],
'name' => [
'type' => 'string' ,
],
'tag' => [
'type' => 'string' ,
],
],
],
2019-10-01 17:57:41 +13:00
'Pets' => array (
2019-08-03 17:58:44 +12:00
'type' => 'array' ,
2019-10-01 17:57:41 +13:00
'items' => array (
2019-08-03 17:58:44 +12:00
'$ref' => '#/definitions/Pet' ,
),
),
2019-10-01 17:57:41 +13:00
'Error' => array (
'required' => array (
2019-08-03 17:58:44 +12:00
0 => 'code' ,
1 => 'message' ,
),
2019-10-01 17:57:41 +13:00
'properties' => array (
'code' => array (
2019-08-03 17:58:44 +12:00
'type' => 'integer' ,
'format' => 'int32' ,
),
2019-10-01 17:57:41 +13:00
'message' => array (
2019-08-03 17:58:44 +12:00
'type' => 'string' ,
),
),
),
2019-09-21 06:44:32 +12:00
],
2019-06-09 20:51:10 +12:00
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials' ,
2019-10-01 17:57:41 +13:00
'url' => $request -> getServer ( 'REQUEST_SCHEME' , 'https' ) . '://' . $domain . '/docs' ,
],
2019-05-09 18:54:39 +12:00
];
foreach ( $utopia -> getRoutes () as $key => $method ) {
foreach ( $method as $route ) { /* @var $route \Utopia\Route */
2019-09-30 19:13:40 +13:00
if ( ! $route -> getLabel ( 'docs' , true )) {
2019-05-09 18:54:39 +12:00
continue ;
}
2019-09-30 19:13:40 +13:00
if ( empty ( $route -> getLabel ( 'sdk.namespace' , null ))) {
2019-05-09 18:54:39 +12:00
continue ;
}
2019-10-01 17:57:41 +13:00
$url = str_replace ( '/v1' , '' , $route -> getURL ());
$scope = $route -> getLabel ( 'scope' , '' );
$hide = $route -> getLabel ( 'sdk.hide' , false );
$consumes = [];
2019-05-09 18:54:39 +12:00
2019-09-30 19:13:40 +13:00
if ( $hide ) {
2019-05-09 18:54:39 +12:00
continue ;
}
$temp = [
'summary' => $route -> getDesc (),
'operationId' => $route -> getLabel ( 'sdk.method' , uniqid ()),
2019-08-14 23:52:40 +12:00
'consumes' => [],
2019-05-09 18:54:39 +12:00
'tags' => [ $route -> getLabel ( 'sdk.namespace' , 'default' )],
2019-10-10 08:31:48 +13:00
'description' => file_get_contents ( realpath ( __DIR__ . '/..' . $route -> getLabel ( 'sdk.description' , '' ))),
2019-05-09 18:54:39 +12:00
'responses' => [
200 => [
'description' => 'An paged array of pets' ,
'schema' => [
2019-10-01 17:57:41 +13:00
'$ref' => '#/definitions/Pet' ,
2019-05-09 18:54:39 +12:00
],
],
],
];
2019-09-30 19:13:40 +13:00
if ( $extensions ) {
2019-05-09 18:54:39 +12:00
$temp [ 'extensions' ] = [
'weight' => $route -> getOrder (),
'cookies' => $route -> getLabel ( 'sdk.cookies' , false ),
2019-09-29 22:42:06 +13:00
'location' => $route -> getLabel ( 'sdk.location' , false ),
2019-10-01 17:57:41 +13:00
'demo' => 'docs/examples/' . fromCamelCaseToDash ( $route -> getLabel ( 'sdk.namespace' , 'default' )) . '/' . fromCamelCaseToDash ( $temp [ 'operationId' ]) . '.md' ,
2019-10-09 21:24:13 +13:00
'edit' => 'https://github.com/appwrite/appwrite/edit/master' . $route -> getLabel ( 'sdk.description' , '' ),
2019-05-09 18:54:39 +12:00
];
}
2019-09-30 19:13:40 +13:00
if (( ! empty ( $scope ) && 'public' != $scope )) {
2019-09-21 07:31:46 +12:00
$temp [ 'security' ][] = $route -> getLabel ( 'sdk.security' , $security [ $platform ]);
2019-05-09 18:54:39 +12:00
}
$requestBody = [
'content' => [
'application/x-www-form-urlencoded' => [
'schema' => [
'type' => 'object' ,
'properties' => [],
],
'required' => [],
2019-10-01 17:57:41 +13:00
],
],
2019-05-09 18:54:39 +12:00
];
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' ;
2019-10-01 17:57:41 +13:00
$node [ 'x-example' ] = '[' . strtoupper ( fromCamelCase ( $node [ 'name' ])) . ']' ;
2019-05-09 18:54:39 +12:00
break ;
case 'Database\Validator\UID' :
$node [ 'type' ] = 'string' ;
2019-10-01 17:57:41 +13:00
$node [ 'x-example' ] = '[' . strtoupper ( fromCamelCase ( $node [ 'name' ])) . ']' ;
2019-05-09 18:54:39 +12:00
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' :
$node [ 'type' ] = 'object' ;
$node [ 'type' ] = 'string' ;
$node [ 'x-example' ] = '{}' ;
//$node['format'] = 'json';
break ;
case 'Storage\Validators\File' :
2019-09-21 06:44:32 +12:00
$consumes [] = 'multipart/form-data' ;
2019-05-09 18:54:39 +12:00
$node [ 'type' ] = 'file' ;
break ;
case 'Utopia\Validator\ArrayList' :
$node [ 'type' ] = 'array' ;
$node [ 'collectionFormat' ] = 'multi' ;
$node [ 'items' ] = [
2019-10-01 17:57:41 +13:00
'type' => 'string' ,
2019-05-09 18:54:39 +12:00
];
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' ] = rand ( $validator -> getMin (), $validator -> getMax ());
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 ;
}
2019-09-30 19:13:40 +13:00
if ( $param [ 'optional' ] && ! is_null ( $param [ 'default' ])) { // Param has default value
2019-05-09 18:54:39 +12:00
$node [ 'default' ] = $param [ 'default' ];
}
2019-10-01 17:57:41 +13:00
if ( false !== strpos ( $url , ':' . $name )) { // Param is in URL path
2019-05-09 18:54:39 +12:00
$node [ 'in' ] = 'path' ;
$temp [ 'parameters' ][] = $node ;
2019-09-30 19:13:40 +13:00
} elseif ( $key == 'GET' ) { // Param is in query
2019-05-09 18:54:39 +12:00
$node [ 'in' ] = 'query' ;
$temp [ 'parameters' ][] = $node ;
2019-09-30 19:13:40 +13:00
} else { // Param is in payload
2019-05-09 18:54:39 +12:00
$node [ 'in' ] = 'formData' ;
$temp [ 'parameters' ][] = $node ;
$requestBody [ 'content' ][ 'application/x-www-form-urlencoded' ][ 'schema' ][ 'properties' ][] = $node ;
2019-09-30 19:13:40 +13:00
if ( ! $param [ 'optional' ]) {
2019-05-09 18:54:39 +12:00
$requestBody [ 'content' ][ 'application/x-www-form-urlencoded' ][ 'required' ][] = $name ;
}
}
2019-10-01 17:57:41 +13:00
$url = str_replace ( ':' . $name , '{' . $name . '}' , $url );
2019-05-09 18:54:39 +12:00
}
2019-08-14 23:52:40 +12:00
$temp [ 'consumes' ] = $consumes ;
2019-05-09 18:54:39 +12:00
$output [ 'paths' ][ $url ][ strtolower ( $route -> getMethod ())] = $temp ;
}
}
/* foreach ( $consoleDB -> getMocks () as $mock ) {
var_dump ( $mock [ 'name' ]);
} */
ksort ( $output [ 'paths' ]);
$response -> json ( $output );
}
);
$name = APP_NAME ;
2019-09-30 19:13:40 +13:00
if ( array_key_exists ( $service , $services )) { /** @noinspection PhpIncludeInspection */
2019-08-03 17:58:44 +12:00
include_once $services [ $service ][ 'controller' ];
2019-10-01 17:57:41 +13:00
$name = APP_NAME . ' ' . ucfirst ( $services [ $service ][ 'name' ]);
2019-09-30 19:13:40 +13:00
} else {
2019-05-09 18:54:39 +12:00
/** @noinspection PhpIncludeInspection */
2019-08-03 17:58:44 +12:00
include_once $services [ '/' ][ 'controller' ];
2019-05-09 18:54:39 +12:00
}
if ( extension_loaded ( 'newrelic' )) {
2019-10-01 17:57:41 +13:00
$route = $utopia -> match ( $request );
$url = ( ! empty ( $route )) ? $route -> getURL () : '/error' ;
2019-05-09 18:54:39 +12:00
newrelic_set_appname ( $name );
2019-10-01 17:57:41 +13:00
newrelic_name_transaction ( $request -> getServer ( 'REQUEST_METHOD' , 'UNKNOWN' ) . ': ' . $url );
2019-05-09 18:54:39 +12:00
}
2019-09-30 19:13:40 +13:00
$utopia -> run ( $request , $response );