1
0
Fork 0
mirror of synced 2024-05-21 05:02:37 +12:00

Added protocol var and cookie fallback

This commit is contained in:
Eldad Fux 2020-02-27 11:17:09 +02:00
parent 6db39ca0b6
commit d8bc3f03e8
8 changed files with 62 additions and 37 deletions

View file

@ -3,7 +3,7 @@
// Init
require_once __DIR__.'/init.php';
global $env, $utopia, $request, $response, $register, $consoleDB, $project, $domain, $version, $service;
global $env, $utopia, $request, $response, $register, $consoleDB, $project, $domain, $version, $service, $protocol;
use Utopia\App;
use Utopia\Request;
@ -76,6 +76,7 @@ $utopia->init(function () use ($utopia, $request, $response, &$user, $project, $
->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-Expose-Headers', 'X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $refDomain)
->addHeader('Access-Control-Allow-Credentials', 'true')
;
@ -234,7 +235,8 @@ $utopia->options(function () use ($request, $response, $domain, $project) {
$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-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, X-Fallback-Cookies')
->addHeader('Access-Control-Expose-Headers', 'X-Fallback-Cookies')
->addHeader('Access-Control-Allow-Origin', $origin)
->addHeader('Access-Control-Allow-Credentials', 'true')
->send();
@ -451,7 +453,7 @@ $utopia->get('/v1/open-api-2.json')
->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 ($platform, $extensions, $tests) use ($response, $request, $utopia, $domain, $services, $protocol) {
function fromCamelCase($input)
{
preg_match_all('!([A-Z][A-Z0-9]*(?=$|[A-Z][a-z0-9])|[A-Za-z][a-z0-9]+)!', $input, $matches);
@ -586,7 +588,7 @@ $utopia->get('/v1/open-api-2.json')
],
'externalDocs' => [
'description' => 'Full API docs, specs and tutorials',
'url' => $request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/docs',
'url' => $protocol.'://'.$domain.'/docs',
],
];

View file

@ -1,7 +1,7 @@
<?php
global $utopia, $register, $request, $response, $user, $audit,
$webhook, $project, $domain, $projectDB, $providers, $clients;
$webhook, $project, $domain, $projectDB, $providers, $clients, $protocol;
use Utopia\Exception;
use Utopia\Response;
@ -150,7 +150,7 @@ $utopia->post('/v1/account/sessions')
->param('email', '', function () { return new Email(); }, 'User email.')
->param('password', '', function () { return new Password(); }, 'User password.')
->action(
function ($email, $password) use ($response, $request, $projectDB, $audit, $webhook) {
function ($email, $password) use ($response, $request, $projectDB, $audit, $webhook, $protocol) {
$profile = $projectDB->getCollection([ // Get user by email address
'limit' => 1,
'first' => true,
@ -212,8 +212,9 @@ $utopia->post('/v1/account/sessions')
;
$response
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($profile->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($profile->getId(), $secret)]))
->setStatusCode(Response::STATUS_CODE_CREATED)
->json($session->getArrayCopy(['$id', 'type', 'expire']))
;
@ -237,8 +238,8 @@ $utopia->get('/v1/account/sessions/oauth2/:provider')
->param('success', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt.')
->param('failure', '', function () use ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt.')
->action(
function ($provider, $success, $failure) use ($response, $request, $project) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
function ($provider, $success, $failure) use ($response, $request, $project, $protocol) {
$callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
$appId = $project->getAttribute('usersOauth2'.ucfirst($provider).'Appid', '');
$appSecret = $project->getAttribute('usersOauth2'.ucfirst($provider).'Secret', '{}');
@ -275,8 +276,8 @@ $utopia->get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'Login state params.', true)
->action(
function ($projectId, $provider, $code, $state) use ($response, $request, $domain) {
$response->redirect($request->getServer('REQUEST_SCHEME', 'https').'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
function ($projectId, $provider, $code, $state) use ($response, $request, $domain, $protocol) {
$response->redirect($protocol.'://'.$domain.'/v1/account/sessions/oauth2/'.$provider.'/redirect?'
.http_build_query(['project' => $projectId, 'code' => $code, 'state' => $state]));
}
);
@ -293,8 +294,8 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
->param('code', '', function () { return new Text(1024); }, 'OAuth2 code.')
->param('state', '', function () { return new Text(2048); }, 'OAuth2 state params.', true)
->action(
function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit) {
$callback = $request->getServer('REQUEST_SCHEME', 'https').'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
function ($provider, $code, $state) use ($response, $request, $user, $projectDB, $project, $audit, $protocol) {
$callback = $protocol.'://'.$request->getServer('HTTP_HOST').'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$validateURL = new URL();
@ -443,8 +444,9 @@ $utopia->get('/v1/account/sessions/oauth2/:provider/redirect')
;
$response
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
->redirect($state['success'])
;
}
@ -810,7 +812,7 @@ $utopia->delete('/v1/account')
->label('sdk.method', 'delete')
->label('sdk.description', '/docs/references/account/delete.md')
->action(
function () use ($response, $request, $user, $projectDB, $audit, $webhook) {
function () use ($response, $user, $projectDB, $audit, $webhook, $protocol) {
$user = $projectDB->updateDocument(array_merge($user->getArrayCopy(), [
'status' => Auth::USER_STATUS_BLOCKED,
]));
@ -842,8 +844,9 @@ $utopia->delete('/v1/account')
;
$response
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([]))
->noContent()
;
}
@ -860,7 +863,7 @@ $utopia->delete('/v1/account/sessions/:sessionId')
->label('abuse-limit', 100)
->param('sessionId', null, function () { return new UID(); }, 'Session unique ID. Use the string \'current\' to delete the current device session.')
->action(
function ($sessionId) use ($response, $request, $user, $projectDB, $webhook, $audit) {
function ($sessionId) use ($response, $user, $projectDB, $webhook, $audit, $protocol) {
$sessionId = ($sessionId === 'current')
? Auth::tokenVerify($user->getAttribute('tokens'), Auth::TOKEN_TYPE_LOGIN, Auth::$secret)
: $sessionId;
@ -888,8 +891,9 @@ $utopia->delete('/v1/account/sessions/:sessionId')
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$response
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([]))
;
}
@ -911,7 +915,7 @@ $utopia->delete('/v1/account/sessions')
->label('sdk.description', '/docs/references/account/delete-sessions.md')
->label('abuse-limit', 100)
->action(
function () use ($response, $request, $user, $projectDB, $audit, $webhook) {
function () use ($response, $user, $projectDB, $audit, $webhook, $protocol) {
$tokens = $user->getAttribute('tokens', []);
foreach ($tokens as $token) { /* @var $token Document */
@ -934,8 +938,9 @@ $utopia->delete('/v1/account/sessions')
if ($token->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
$response
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, '', time() - 3600, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([]))
;
}
}

View file

@ -426,7 +426,7 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
->param('userId', '', function () { return new UID(); }, 'User unique ID.')
->param('secret', '', function () { return new Text(256); }, 'Secret key.')
->action(
function ($teamId, $inviteId, $userId, $secret) use ($response, $request, $user, $audit, $projectDB) {
function ($teamId, $inviteId, $userId, $secret) use ($response, $request, $user, $audit, $projectDB, $protocol) {
$membership = $projectDB->getDocument($inviteId);
if (empty($membership->getId()) || Database::SYSTEM_COLLECTION_MEMBERSHIPS != $membership->getCollection()) {
@ -521,8 +521,9 @@ $utopia->patch('/v1/teams/:teamId/memberships/:inviteId/status')
;
$response
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $request->getServer('REQUEST_SCHEME', 'https')), true, COOKIE_SAMESITE)
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, null)
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', COOKIE_DOMAIN, ('https' == $protocol), true, COOKIE_SAMESITE)
->addHeader('X-Fallback-Cookies', json_encode([Auth::$cookieName => Auth::encodeSession($user->getId(), $secret)]))
->json(array_merge($membership->getArrayCopy([
'$id',
'userId',

View file

@ -3,6 +3,8 @@
use Utopia\View;
use Utopia\Locale\Locale;
global $protocol;
Locale::$exceptions = false;
$roles = [
@ -20,7 +22,7 @@ if (!empty($request->getQuery('version', ''))) {
$layout
->setParam('title', APP_NAME)
->setParam('protocol', $request->getServer('REQUEST_SCHEME', 'https'))
->setParam('protocol', $protocol)
->setParam('domain', $domain)
->setParam('home', $request->getServer('_APP_HOME'))
->setParam('setup', $request->getServer('_APP_SETUP'))

View file

@ -55,8 +55,8 @@ $collections = include __DIR__.'/../app/config/collections.php'; // Collections
$redisHost = $request->getServer('_APP_REDIS_HOST', '');
$redisPort = $request->getServer('_APP_REDIS_PORT', '');
$utopia = new App('Asia/Tel_Aviv', $env);
$scheme = $request->getServer('REQUEST_SCHEME', '');
$port = (string) parse_url($scheme.'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT);
$protocol = $request->getServer('HTTP_X_FORWARDED_PROTO', $request->getServer('REQUEST_SCHEME', 'https'));
$port = (string) parse_url($protocol.'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_PORT);
Resque::setBackend($redisHost.':'.$redisPort);
@ -67,7 +67,7 @@ define('COOKIE_DOMAIN',
(filter_var($request->getServer('HTTP_HOST', null), FILTER_VALIDATE_IP) !== false)
)
? null
: '.'.parse_url($scheme.'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_HOST));
: '.'.parse_url($protocol.'://'.$request->getServer('HTTP_HOST', ''), PHP_URL_HOST));
define('COOKIE_SAMESITE', Response::COOKIE_SAMESITE_NONE);
/*
@ -240,7 +240,18 @@ if (APP_MODE_ADMIN === $mode) {
$session = Auth::decodeSession(
$request->getCookie(Auth::$cookieName, // Get sessions
$request->getCookie(Auth::$cookieName.'_legacy', // Get fallback session from old clients (no SameSite support)
$request->getHeader('X-Appwrite-Key', '')))); // Get API Key
$request->getHeader('X-Appwrite-Key', '')))); // Get API Key
// Get fallback session from clients who block 3rd-party cookies
$response->addHeader('X-Debug-Fallback', 'false');
if(empty($session['id']) && empty($session['secret'])) {
$response->addHeader('X-Debug-Fallback', 'true');
$fallback = $request->getHeader('X-Fallback-Cookies', null);
$fallback = json_decode($fallback, true);
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
}
Auth::$unique = $session['id'];
Auth::$secret = $session['secret'];

View file

@ -9,12 +9,14 @@ return str.join("&");};let addGlobalHeader=function(key,value){globalHeaders[key
if(typeof path!=='string'){throw new Error('var path must be of type string');}
if(typeof headers!=='object'){throw new Error('var headers must be of type object');}
for(i=0;i<globalParams.length;i++){path=addParam(path,globalParams[i].key,globalParams[i].value);}
if(window.localStorage&&window.localStorage.getItem('cookieFallback')){headers['X-Fallback-Cookies']=window.localStorage.getItem('cookieFallback');}
for(let key in globalHeaders){if(globalHeaders.hasOwnProperty(key)){if(!headers[globalHeaders[key].key]){headers[globalHeaders[key].key]=globalHeaders[key].value;}}}
if(method==='GET'){for(let param in params){if(param.hasOwnProperty(key)){path=addParam(path,key+(Array.isArray(param)?'[]':''),params[key]);}}}
switch(headers['content-type']){case'application/json':params=JSON.stringify(params);break;case'multipart/form-data':let formData=new FormData();Object.keys(params).forEach(function(key){let param=params[key];formData.append(key+(Array.isArray(param)?'[]':''),param);});params=formData;break;}
return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key;request.withCredentials=true;request.open(method,path,true);for(key in headers){if(headers.hasOwnProperty(key)){if(key==='content-type'&&headers[key]==='multipart/form-data'){continue;}
request.setRequestHeader(key,headers[key]);}}
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;}
let cookieFallback=this.getResponseHeader('X-Fallback-Cookies')||'';if(window.localStorage&&cookieFallback){window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');window.localStorage.setItem('cookieFallback',cookieFallback);}
resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);}
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let iframe=function(method,url,params){let form=document.createElement('form');form.setAttribute('method',method);form.setAttribute('action',config.endpoint+url);for(let key in params){if(params.hasOwnProperty(key)){let hiddenField=document.createElement("input");hiddenField.setAttribute("type","hidden");hiddenField.setAttribute("name",key);hiddenField.setAttribute("value",params[key]);form.appendChild(hiddenField);}}
document.body.appendChild(form);return form.submit();};let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload);},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"');}
@ -2405,7 +2407,7 @@ children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(ind
container.set('$index',originalIndex,true,false);container.set('$prefix',originalPrefix,true,false);container.set('$as',originalAs,true,false);element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:true,controller:function(element,view,http,expression,document){let template=expression.parse(element.getAttribute('data-ls-template'));let type=element.getAttribute('data-type')||'url';element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(template);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}
else{element.innerHTML='<span style="color: red">Missing template "'+template+'"</span>';}
return;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){return;window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject('console').setLocale(APP_ENV.LOCALE);return sdk;},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
@ -2581,7 +2583,7 @@ if("/console"===router.getCurrent().path){document.body.classList.add("index");}
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}}).add({selector:"data-ls-ui-chart",controller:function(element,container,date,document){let child=document.createElement("canvas");child.width=500;child.height=175;let stats=container.get("usage");if(!stats||!stats["requests"]||!stats["requests"]["data"]){return;}
let config={type:"line",data:{labels:[],datasets:[{label:"Requests",backgroundColor:"rgba(230, 248, 253, 0.3)",borderColor:"#29b5d9",borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<stats["requests"]["data"].length;i++){config.data.datasets[0].data[i]=stats["requests"]["data"][i].value;config.data.labels[i]=date.format("d F Y",stats["requests"]["data"][i].date);}
let chart=container.get("chart");if(chart){}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=element.dataset["event"];let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
console.log("%c[service init]: "+action+" ("+service+")","color:red");let callbacks={reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}

View file

@ -9,12 +9,14 @@ return str.join("&");};let addGlobalHeader=function(key,value){globalHeaders[key
if(typeof path!=='string'){throw new Error('var path must be of type string');}
if(typeof headers!=='object'){throw new Error('var headers must be of type object');}
for(i=0;i<globalParams.length;i++){path=addParam(path,globalParams[i].key,globalParams[i].value);}
if(window.localStorage&&window.localStorage.getItem('cookieFallback')){headers['X-Fallback-Cookies']=window.localStorage.getItem('cookieFallback');}
for(let key in globalHeaders){if(globalHeaders.hasOwnProperty(key)){if(!headers[globalHeaders[key].key]){headers[globalHeaders[key].key]=globalHeaders[key].value;}}}
if(method==='GET'){for(let param in params){if(param.hasOwnProperty(key)){path=addParam(path,key+(Array.isArray(param)?'[]':''),params[key]);}}}
switch(headers['content-type']){case'application/json':params=JSON.stringify(params);break;case'multipart/form-data':let formData=new FormData();Object.keys(params).forEach(function(key){let param=params[key];formData.append(key+(Array.isArray(param)?'[]':''),param);});params=formData;break;}
return new Promise(function(resolve,reject){let request=new XMLHttpRequest(),key;request.withCredentials=true;request.open(method,path,true);for(key in headers){if(headers.hasOwnProperty(key)){if(key==='content-type'&&headers[key]==='multipart/form-data'){continue;}
request.setRequestHeader(key,headers[key]);}}
request.onload=function(){if(4===request.readyState&&399>=request.status){let data=request.response;let contentType=this.getResponseHeader('content-type')||'';contentType=contentType.substring(0,contentType.indexOf(';'));switch(contentType){case'application/json':data=JSON.parse(data);break;}
let cookieFallback=this.getResponseHeader('X-Fallback-Cookies')||'';if(window.localStorage&&cookieFallback){window.console.warn('Appwrite is using localStorage for session management. Increase your security by adding a custom domain as your API endpoint.');window.localStorage.setItem('cookieFallback',cookieFallback);}
resolve(data);}else{reject(new Error(request.statusText));}};if(progress){request.addEventListener('progress',progress);request.upload.addEventListener('progress',progress,false);}
request.onerror=function(){reject(new Error("Network Error"));};request.send(params);})};return{'get':function(path,headers={},params={}){return call('GET',path+((Object.keys(params).length>0)?'?'+buildQuery(params):''),headers,{});},'post':function(path,headers={},params={},progress=null){return call('POST',path,headers,params,progress);},'put':function(path,headers={},params={},progress=null){return call('PUT',path,headers,params,progress);},'patch':function(path,headers={},params={},progress=null){return call('PATCH',path,headers,params,progress);},'delete':function(path,headers={},params={},progress=null){return call('DELETE',path,headers,params,progress);},'addGlobalParam':addGlobalParam,'addGlobalHeader':addGlobalHeader}}(window.document);let iframe=function(method,url,params){let form=document.createElement('form');form.setAttribute('method',method);form.setAttribute('action',config.endpoint+url);for(let key in params){if(params.hasOwnProperty(key)){let hiddenField=document.createElement("input");hiddenField.setAttribute("type","hidden");hiddenField.setAttribute("name",key);hiddenField.setAttribute("value",params[key]);form.appendChild(hiddenField);}}
document.body.appendChild(form);return form.submit();};let account={get:function(){let path='/account';let payload={};return http.get(path,{'content-type':'application/json',},payload);},create:function(email,password,name=''){if(email===undefined){throw new Error('Missing required parameter: "email"');}

View file

@ -123,7 +123,7 @@ children[prop]=template.cloneNode(true);element.appendChild(children[prop]);(ind
container.set('$index',originalIndex,true,false);container.set('$prefix',originalPrefix,true,false);container.set('$as',originalAs,true,false);element.dispatchEvent(new Event('looped'));};let template=(element.children.length===1)?element.children[0]:window.document.createElement('li');echo();container.bind(element,expr+'.length',echo);let path=(expr+'.length').split('.');while(path.length){container.bind(element,path.join('.'),echo);path.pop();}}});window.ls.container.get('view').add({selector:'data-ls-template',template:false,repeat:true,controller:function(element,view,http,expression,document){let template=expression.parse(element.getAttribute('data-ls-template'));let type=element.getAttribute('data-type')||'url';element.innerHTML='';if('script'===type){let inlineTemplate=document.getElementById(template);if(inlineTemplate&&inlineTemplate.innerHTML){element.innerHTML=inlineTemplate.innerHTML;element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}
else{element.innerHTML='<span style="color: red">Missing template "'+template+'"</span>';}
return;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
http.get(template).then(function(element){return function(data){element.innerHTML=data;view.render(element);element.dispatchEvent(new CustomEvent('template-loaded',{bubbles:true,cancelable:false}));}}(element),function(){throw new Error('Failed loading template');});}});window.ls.error=function(){return function(error){console.error("ERROR-APP",error);};};window.addEventListener("error",function(event){console.error("ERROR-EVENT:",event.error.message,event.error.stack);});document.addEventListener("account.deleteSession",function(){return;window.location="/auth/signin";});document.addEventListener("account.create",function(){let container=window.ls.container;let form=container.get('serviceForm');let sdk=container.get('console');let promise=sdk.account.createSession(form.email,form.password);container.set("serviceForm",{},true,true);promise.then(function(){window.location='/console';},function(error){window.location='/auth/signup?failure=1';});});(function(window){"use strict";window.ls.container.set('alerts',function(window){return{list:[],ids:0,counter:0,max:5,add:function(message,time){var scope=this;message.id=scope.ids++;scope.counter++;scope.list.unshift(message);if(scope.counter>scope.max){scope.list.pop();scope.counter--;}
if(time>0){window.setTimeout(function(message){return function(){scope.remove(message.id)}}(message),time);}
return message.id;},remove:function(id){let scope=this;for(let index=0;index<scope.list.length;index++){let obj=scope.list[index];if(obj.id===parseInt(id)){scope.counter--;if(typeof obj.callback==="function"){obj.callback();}
scope.list.splice(index,1);};}}};},true,true);})(window);(function(window){"use strict";window.ls.container.set('console',function(window){var sdk=new window.Appwrite();sdk.setEndpoint(APP_ENV.API).setProject('console').setLocale(APP_ENV.LOCALE);return sdk;},true);})(window);(function(window){"use strict";window.ls.container.set('date',function(){function format(format,timestamp){var jsdate,f
@ -299,7 +299,7 @@ if("/console"===router.getCurrent().path){document.body.classList.add("index");}
window.getSelection().removeAllRanges();});element.parentNode.parentNode.appendChild(copy);}}).add({selector:"data-ls-ui-chart",controller:function(element,container,date,document){let child=document.createElement("canvas");child.width=500;child.height=175;let stats=container.get("usage");if(!stats||!stats["requests"]||!stats["requests"]["data"]){return;}
let config={type:"line",data:{labels:[],datasets:[{label:"Requests",backgroundColor:"rgba(230, 248, 253, 0.3)",borderColor:"#29b5d9",borderWidth:2,data:[0,0,0,0,0,0,0],fill:true}]},options:{responsive:true,title:{display:false,text:"Stats"},legend:{display:false},tooltips:{mode:"index",intersect:false,caretPadding:0},hover:{mode:"nearest",intersect:true},scales:{xAxes:[{display:false}],yAxes:[{display:false}]}}};for(let i=0;i<stats["requests"]["data"].length;i++){config.data.datasets[0].data[i]=stats["requests"]["data"][i].value;config.data.labels[i]=date.format("d F Y",stats["requests"]["data"][i].date);}
let chart=container.get("chart");if(chart){}
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=element.dataset["event"];let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
element.innerHTML="";element.appendChild(child);container.set("chart",new Chart(child.getContext("2d"),config),true);element.dataset["canvas"]=true;}});(function(window){"use strict";window.ls.view.add({selector:"data-service",controller:function(element,view,container,form,alerts,expression,window){let action=element.dataset["service"];let service=element.dataset["name"]||action;let event=expression.parse(element.dataset["event"]);let confirm=element.dataset["confirm"]||"";let loading=element.dataset["loading"]||"";let loaderId=null;let scope=element.dataset["scope"]||"sdk";let debug=!!element.dataset["debug"];let success=element.dataset["success"]||"";let failure=element.dataset["failure"]||"";success=success&&success!=""?success.split(",").map(element=>element.trim()):[];failure=failure&&failure!=""?failure.split(",").map(element=>element.trim()):[];if(debug)
console.log("%c[service init]: "+action+" ("+service+")","color:red");let callbacks={reset:function(){return function(){if("FORM"===element.tagName){return element.reset();}
throw new Error("This callback is only valid for forms");};},alert:function(text,classname){return function(alerts){alerts.add({text:text,class:classname||"success"},3000);};},redirect:function(url){return function(router){window.location=url||"/";};},reload:function(){return function(router){router.reload();};},state:function(keys){let updateQueryString=function(key,value,url){var re=new RegExp("([?&])"+key+"=.*?(&|#|$)(.*)","gi"),hash;if(re.test(url)){if(typeof value!=="undefined"&&value!==null){return url.replace(re,"$1"+key+"="+value+"$2$3");}else{hash=url.split("#");url=hash[0].replace(re,"$1$3").replace(/(&|\?)$/,"");if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}
return url;}}else{if(typeof value!=="undefined"&&value!==null){var separator=url.indexOf("?")!==-1?"&":"?";hash=url.split("#");url=hash[0]+separator+key+"="+value;if(typeof hash[1]!=="undefined"&&hash[1]!==null){url+="#"+hash[1];}