1
0
Fork 0
mirror of synced 2024-06-29 11:40:45 +12:00

Merge branch '0.14.x' of https://github.com/appwrite/appwrite into feat-new-event-model

This commit is contained in:
Torsten Dittmann 2022-04-26 12:39:13 +02:00
commit 297d076caf
24 changed files with 337 additions and 26943 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View file

@ -145,8 +145,8 @@ App::get('/v1/avatars/image')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE)
->param('url', '', new URL(['http', 'https']), 'Image URL which you want to crop.')
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000.', true)
->param('width', 400, new Range(0, 2000), 'Resize preview image width, Pass an integer between 0 to 2000. Defaults to 400.', true)
->param('height', 400, new Range(0, 2000), 'Resize preview image height, Pass an integer between 0 to 2000. Defaults to 400.', true)
->inject('response')
->action(function ($url, $width, $height, $response) {
/** @var Appwrite\Utopia\Response $response */
@ -367,7 +367,7 @@ App::get('/v1/avatars/qr')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_IMAGE_PNG)
->param('text', '', new Text(512), 'Plain text to be converted to QR code image.')
->param('size', 400, new Range(0, 1000), 'QR code size. Pass an integer between 0 to 1000. Defaults to 400.', true)
->param('size', 400, new Range(1, 1000), 'QR code size. Pass an integer between 1 to 1000. Defaults to 400.', true)
->param('margin', 1, new Range(0, 10), 'Margin from edge. Pass an integer between 0 to 10. Defaults to 1.', true)
->param('download', false, new Boolean(true), 'Return resulting image with \'Content-Disposition: attachment \' headers for the browser to start downloading it. Pass 0 for no header, or 1 for otherwise. Default value is set to 0.', true)
->inject('response')

View file

@ -19,7 +19,6 @@ use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Key;
use Utopia\Database\Validator\Permissions;
use Utopia\Database\Validator\QueryValidator;
use Utopia\Database\Validator\Queries as QueriesValidator;
use Utopia\Database\Validator\Structure;
use Utopia\Database\Validator\UID;
use Utopia\Database\Exception\Authorization as AuthorizationException;
@ -31,6 +30,8 @@ use Appwrite\Network\Validator\Email;
use Appwrite\Network\Validator\IP;
use Appwrite\Network\Validator\URL;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries as QueriesValidator;
use Appwrite\Utopia\Database\Validator\OrderAttributes as OrderAttributesValidator;
use Appwrite\Utopia\Response;
use Appwrite\Detector\Detector;
use Appwrite\Event\Audit as EventAudit;
@ -1778,6 +1779,13 @@ App::get('/v1/database/collections/:collectionId/documents')
}
}
if(!empty($orderAttributes)) {
$validator = new OrderAttributesValidator($collection->getAttribute('attributes', []), $collection->getAttribute('indexes', []), true);
if (!$validator->isValid($orderAttributes)) {
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
}
}
$cursorDocument = null;
if (!empty($cursor)) {
$cursorDocument = $collection->getAttribute('permission') === 'collection'

View file

@ -9,6 +9,7 @@ use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Response;
use Utopia\App;
use Appwrite\Extend\Exception;
use Utopia\Audit\Audit;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@ -42,11 +43,13 @@ App::post('/v1/teams')
->inject('user')
->inject('dbForProject')
->inject('events')
->action(function ($teamId, $name, $roles, $response, $user, $dbForProject, $events) {
->inject('audits')
->action(function ($teamId, $name, $roles, $response, $user, $dbForProject, $events, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $user */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
/** @var Appwrite\Event\Event $audits */
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
$isAppUser = Auth::isAppUser(Authorization::getRoles());
@ -91,6 +94,12 @@ App::post('/v1/teams')
$events->setParam('userId', $user->getId());
}
$audits
->setParam('event', 'teams.create')
->setParam('resource', 'team/'.$teamId)
->setParam('data', $team->getArrayCopy())
;
$response->setStatusCode(Response::STATUS_CODE_CREATED);
$response->dynamic($team, Response::MODEL_TEAM);
});
@ -222,7 +231,8 @@ App::delete('/v1/teams/:teamId')
->inject('dbForProject')
->inject('events')
->inject('deletes')
->action(function ($teamId, $response, $dbForProject, $events, $deletes) {
->inject('audits')
->action(function ($teamId, $response, $dbForProject, $events, $deletes, $audits) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Database $dbForProject */
/** @var Appwrite\Event\Event $events */
@ -258,6 +268,12 @@ App::delete('/v1/teams/:teamId')
->setPayload($response->output($team, Response::MODEL_TEAM))
;
$audits
->setParam('event', 'teams.delete')
->setParam('resource', 'team/'.$teamId)
->setParam('data', $team->getArrayCopy())
;
$response->noContent();
});
@ -857,3 +873,88 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
$response->noContent();
});
App::get('/v1/teams/:teamId/logs')
->desc('List Team Logs')
->groups(['api', 'teams'])
->label('scope', 'teams.read')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'teams')
->label('sdk.method', 'listLogs')
->label('sdk.description', '/docs/references/teams/get-team-logs.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_LOG_LIST)
->param('teamId', null, new UID(), 'Team ID.')
->param('limit', 25, new Range(0, 100), 'Maximum number of logs to return in response. By default will return maximum 25 results. Maximum of 100 results allowed per request.', true)
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
->inject('response')
->inject('dbForProject')
->inject('locale')
->inject('geodb')
->action(function ($teamId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
/** @var Appwrite\Utopia\Response $response */
/** @var Utopia\Database\Document $project */
/** @var Utopia\Database\Database $dbForProject */
/** @var Utopia\Locale\Locale $locale */
/** @var MaxMind\Db\Reader $geodb */
$team = $dbForProject->getDocument('teams', $teamId);
if ($team->isEmpty()) {
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
}
$audit = new Audit($dbForProject);
$resource = 'team/'.$team->getId();
$logs = $audit->getLogsByResource($resource, $limit, $offset);
$output = [];
foreach ($logs as $i => &$log) {
$log['userAgent'] = (!empty($log['userAgent'])) ? $log['userAgent'] : 'UNKNOWN';
$detector = new Detector($log['userAgent']);
$detector->skipBotDetection(); // OPTIONAL: If called, bot detection will completely be skipped (bots will be detected as regular devices then)
$os = $detector->getOS();
$client = $detector->getClient();
$device = $detector->getDevice();
$output[$i] = new Document([
'event' => $log['event'],
'userId' => $log['userId'],
'userEmail' => $log['data']['userEmail'] ?? null,
'userName' => $log['data']['userName'] ?? null,
'mode' => $log['data']['mode'] ?? null,
'ip' => $log['ip'],
'time' => $log['time'],
'osCode' => $os['osCode'],
'osName' => $os['osName'],
'osVersion' => $os['osVersion'],
'clientType' => $client['clientType'],
'clientCode' => $client['clientCode'],
'clientName' => $client['clientName'],
'clientVersion' => $client['clientVersion'],
'clientEngine' => $client['clientEngine'],
'clientEngineVersion' => $client['clientEngineVersion'],
'deviceName' => $device['deviceName'],
'deviceBrand' => $device['deviceBrand'],
'deviceModel' => $device['deviceModel']
]);
$record = $geodb->get($log['ip']);
if ($record) {
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
} else {
$output[$i]['countryCode'] = '--';
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
}
}
$response->dynamic(new Document([
'total' => $audit->countLogsByResource($resource),
'logs' => $output,
]), Response::MODEL_LOG_LIST);
});

View file

@ -233,7 +233,53 @@
</div>
</div>
</li>
<li data-state="/console/users/teams/team/audit?id={{router.params.id}}&project={{router.params.project}}">
<h2>Activity</h2>
<div
data-service="teams.listLogs"
data-name="logs"
data-param-team-id="{{router.params.id}}"
data-event="load,logs-load">
<div class="box margin-top margin-bottom" data-ls-if="{{logs.logs.length}} === 0" style="display: none" class="margin-top-xxl margin-bottom-xxl text-align-center">
<h3 class="text-bold margin-bottom-no">No logs available.</h3>
</div>
<div class="box" data-ls-if="{{logs.logs.length}} !== 0" style="display: none">
<table class="vertical small">
<thead>
<tr>
<th width="140">Date</th>
<th width="175">Event</th>
<th>Client</th>
<th width="90">Location</th>
<th width="90">IP</th>
</tr>
</thead>
<tbody data-ls-loop="logs.logs" data-ls-as="log">
<tr>
<td data-title="Date: "><span data-ls-bind="{{log.time|dateTime}}"></span></td>
<td data-title="Event: "><span data-ls-bind="{{log.event}}"></span></td>
<td data-title="Client: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} !== 'cli'" data-ls-attrs="src={{env.API}}/avatars/browsers/{{log.clientCode|lowercase}}?width=80&height=80&project={{env.PROJECT}},title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.clientCode|lowercase}} === 'cli'" data-ls-attrs="src=/images/clients/terminal.png?buster=<?php echo APP_CACHE_BUSTER; ?>,title={{log.clientName}},alt={{log.clientName}}" class="avatar xxs inline margin-end-small" />
<span data-ls-if="({{log.clientName}})" data-ls-bind="{{log.clientName}} {{log.clientVersion}} on {{log.model}} {{log.osName}} {{log.osVersion}}"></span>
<div data-ls-if="(!{{log.clientName}})" class="text-align-center text-fade">Unknown</div>
</td>
<td data-title="Location: ">
<img onerror="this.onerror=null;this.className='avatar hide'" data-ls-if="{{log.countryCode}} !== '--'" data-ls-attrs="src={{env.API}}/avatars/flags/{{log.countryCode}}?width=80&height=80&project={{env.PROJECT}}" class="avatar xxs inline margin-end-small" />
<span data-ls-bind="{{log.countryName}}"></span>
</td>
<td data-title="IP: "><span data-ls-bind="{{log.ip}}"></span></td>
</tr>
</tbody>
</table>
</div>
</div>
</li>
</ul>
</div>
</div>

View file

@ -242,6 +242,52 @@
<li class="margin-bottom-small"><i class="icon-angle-circled-right margin-start-tiny margin-end-tiny"></i> <button data-ls-ui-trigger="open-json" class="link text-size-small">View as JSON</button></li>
</ul>
<div data-ls-if="{{user.emailVerification}} === false" style="display: none">
<form name="users.updateVerification" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Verification Status"
data-service="users.updateVerification"
data-scope="sdk"
data-event="submit"
data-success="trigger,alert"
data-success-param-alert-text="User verification status was updated successfully"
data-success-param-trigger-events="users.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update user verification status"
data-failure-param-alert-classname="error"
>
<input type="hidden" disabled name="userId" data-ls-bind="{{user.$id}}">
<input type="hidden" disabled name="emailVerification" value="true" data-cast-to="boolean">
<button type="submit" class="dark fill">Verify Account</button>
</form>
</div>
<div data-ls-if="{{user.emailVerification}} === true" style="display: none">
<form name="users.updateVerification" class="margin-bottom"
data-analytics
data-analytics-activity
data-analytics-event="submit"
data-analytics-category="console"
data-analytics-label="Update Verification Status"
data-service="users.updateVerification"
data-scope="sdk"
data-event="submit"
data-success="trigger,alert"
data-success-param-alert-text="User verification status was updated successfully"
data-success-param-trigger-events="users.update"
data-failure="alert"
data-failure-param-alert-text="Failed to update user verification status"
data-failure-param-alert-classname="error"
>
<input type="hidden" disabled name="userId" data-ls-bind="{{user.$id}}">
<input type="hidden" disabled name="emailVerification" value="false" data-cast-to="boolean">
<button type="submit" class="dark fill">Unverify Account</button>
</form>
</div>
<div data-ls-if="{{user.status}} === true" style="display: none">
<form name="users.updateStatus" class="margin-bottom"
data-analytics

2
composer.lock generated
View file

@ -6576,5 +6576,5 @@
"platform-overrides": {
"php": "8.0"
},
"plugin-api-version": "2.1.0"
"plugin-api-version": "2.3.0"
}

View file

@ -1 +1,3 @@
You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user /account/sessions endpoint. Use width, height and quality arguments to change the output settings.
You can use this endpoint to show different browser icons to your users. The code argument receives the browser code as it appears in your user [GET /account/sessions](/docs/client/account#accountGetSessions) endpoint. Use width, height and quality arguments to change the output settings.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.

View file

@ -1 +1,3 @@
The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.
The credit card endpoint will return you the icon of the credit card provider you need. Use width, height and quality arguments to change the output settings.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.

View file

@ -1 +1,3 @@
You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings.
You can use this endpoint to show different country flags icons to your users. The code argument receives the 2 letter country code. Use width, height and quality arguments to change the output settings.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.

View file

@ -1 +1,3 @@
Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.
Use this endpoint to fetch a remote image URL and crop it to any image size you want. This endpoint is very useful if you need to crop and display remote images in your app or in case you want to make sure a 3rd party image is properly served using a TLS protocol.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 400x400px.

View file

@ -1,3 +1,5 @@
Use this endpoint to show your user initials avatar icon on your website or app. By default, this route will try to print your logged-in user name or email initials. You can also overwrite the user name if you pass the 'name' parameter. If no name is given and no user is logged, an empty avatar will be returned.
You can use the color and background params to change the avatar colors. By default, a random theme will be selected. The random theme will persist for the user's initials when reloading the same theme will always return for the same initials.
You can use the color and background params to change the avatar colors. By default, a random theme will be selected. The random theme will persist for the user's initials when reloading the same theme will always return for the same initials.
When one dimension is specified and the other is 0, the image is scaled with preserved aspect ratio. If both dimensions are 0, the API provides an image at source quality. If dimensions are not specified, the default size of image returned is 100x100px.

View file

@ -1 +1 @@
Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.
Converts a given plain text to a QR code image. You can use the query parameters to change the size and style of the resulting image.

View file

@ -0,0 +1 @@
Get the team activity logs list by its unique ID.

View file

@ -5,7 +5,7 @@ function rejected(value){try{step(generator["throw"](value));}catch(e){reject(e)
function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected);}
step((generator=generator.apply(thisArg,_arguments||[])).next());});}
class AppwriteException extends Error{constructor(message,code=0,type='',response=''){super(message);this.name='AppwriteException';this.message=message;this.code=code;this.type=type;this.response=response;}}
class Appwrite{constructor(){this.config={endpoint:'https://HOSTNAME/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:4.0.4','X-Appwrite-Response-Format':'0.13.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
class Appwrite{constructor(){this.config={endpoint:'https://HOSTNAME/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:5.0.0','X-Appwrite-Response-Format':'0.13.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
return;const channels=new URLSearchParams();channels.set('project',this.config.project);this.realtime.channels.forEach(channel=>{channels.append('channels[]',channel);});const url=this.config.endpointRealtime+'/realtime?'+channels.toString();if(url!==this.realtime.url||!this.realtime.socket||((_a=this.realtime.socket)===null||_a===void 0?void 0:_a.readyState)>WebSocket.OPEN){if(this.realtime.socket&&((_b=this.realtime.socket)===null||_b===void 0?void 0:_b.readyState)<WebSocket.CLOSING){this.realtime.reconnect=false;this.realtime.socket.close();}
this.realtime.url=url;this.realtime.socket=new WebSocket(url);this.realtime.socket.addEventListener('message',this.realtime.onMessage);this.realtime.socket.addEventListener('open',_event=>{this.realtime.reconnectAttempts=0;});this.realtime.socket.addEventListener('close',event=>{var _a,_b,_c;if(!this.realtime.reconnect||(((_b=(_a=this.realtime)===null||_a===void 0?void 0:_a.lastMessage)===null||_b===void 0?void 0:_b.type)==='error'&&((_c=this.realtime)===null||_c===void 0?void 0:_c.lastMessage.data).code===1008)){this.realtime.reconnect=true;return;}
const timeout=this.realtime.getTimeout();console.error(`Realtime got disconnected. Reconnect will be attempted in ${timeout / 1000} seconds.`,event.reason);setTimeout(()=>{this.realtime.reconnectAttempts++;this.realtime.createSocket();},timeout);});}},onMessage:(event)=>{var _a,_b;try{const message=JSON.parse(event.data);this.realtime.lastMessage=message;switch(message.type){case'connected':const cookie=JSON.parse((_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'{}');const session=cookie===null||cookie===void 0?void 0:cookie[`a_session_${this.config.project}`];const messageData=message.data;if(session&&!messageData.user){(_b=this.realtime.socket)===null||_b===void 0?void 0:_b.send(JSON.stringify({type:'authentication',data:{session}}));}
@ -296,7 +296,7 @@ if(typeof activate!=='undefined'){payload['activate']=activate;}
const uri=new URL(this.config.endpoint+path);const size=code.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=code.slice(start,end+1);payload['code']=new File([stream],code.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE-1,size)/size*100,sizeUploaded:end,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
@ -501,7 +501,7 @@ let id=undefined;let response=undefined;const headers={'content-type':'multipart
catch(e){}}
for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=file.slice(start,end+1);payload['file']=new File([stream],file.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE-1,size)/size*100,sizeUploaded:end,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateFile:(bucketId,fileId,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
@ -549,7 +549,10 @@ let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=n
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(teamId)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),getMemberships:(teamId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listLogs:(teamId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}/logs'.replace('{teamId}',teamId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getMemberships:(teamId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}/memberships'.replace('{teamId}',teamId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
@ -630,7 +633,7 @@ setJWT(value){this.headers['X-Appwrite-JWT']=value;this.config.jwt=value;return
setLocale(value){this.headers['X-Appwrite-Locale']=value;this.config.locale=value;return this;}
setMode(value){this.headers['X-Appwrite-Mode']=value;this.config.mode=value;return this;}
subscribe(channels,callback){let channelArray=typeof channels==='string'?[channels]:channels;channelArray.forEach(channel=>this.realtime.channels.add(channel));const counter=this.realtime.subscriptionsCounter++;this.realtime.subscriptions.set(counter,{channels:channelArray,callback});this.realtime.connect();return()=>{this.realtime.subscriptions.delete(counter);this.realtime.cleanUp(channelArray);this.realtime.connect();};}
call(method,url,headers={},params={}){var _a,_b;return __awaiter(this,void 0,void 0,function*(){method=method.toUpperCase();headers=Object.assign(Object.assign({},headers),this.headers);let options={method,headers,credentials:'include'};if(typeof window!=='undefined'&&window.localStorage){headers['X-Fallback-Cookies']=(_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'';}
call(method,url,headers={},params={}){var _a,_b;return __awaiter(this,void 0,void 0,function*(){method=method.toUpperCase();headers=Object.assign({},this.headers,headers);let options={method,headers,credentials:'include'};if(typeof window!=='undefined'&&window.localStorage){headers['X-Fallback-Cookies']=(_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'';}
if(method==='GET'){for(const[key,value]of Object.entries(this.flatten(params))){url.searchParams.append(key,value);}}
else{switch(headers['content-type']){case'application/json':options.body=JSON.stringify(params);break;case'multipart/form-data':let formData=new FormData();for(const key in params){if(Array.isArray(params[key])){params[key].forEach((value)=>{formData.append(key+'[]',value);});}
else{formData.append(key,params[key]);}}

View file

@ -5,7 +5,7 @@ function rejected(value){try{step(generator["throw"](value));}catch(e){reject(e)
function step(result){result.done?resolve(result.value):adopt(result.value).then(fulfilled,rejected);}
step((generator=generator.apply(thisArg,_arguments||[])).next());});}
class AppwriteException extends Error{constructor(message,code=0,type='',response=''){super(message);this.name='AppwriteException';this.message=message;this.code=code;this.type=type;this.response=response;}}
class Appwrite{constructor(){this.config={endpoint:'https://HOSTNAME/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:4.0.4','X-Appwrite-Response-Format':'0.13.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
class Appwrite{constructor(){this.config={endpoint:'https://HOSTNAME/v1',endpointRealtime:'',project:'',key:'',jwt:'',locale:'',mode:'',};this.headers={'x-sdk-version':'appwrite:web:5.0.0','X-Appwrite-Response-Format':'0.13.0',};this.realtime={socket:undefined,timeout:undefined,url:'',channels:new Set(),subscriptions:new Map(),subscriptionsCounter:0,reconnect:true,reconnectAttempts:0,lastMessage:undefined,connect:()=>{clearTimeout(this.realtime.timeout);this.realtime.timeout=window===null||window===void 0?void 0:window.setTimeout(()=>{this.realtime.createSocket();},50);},getTimeout:()=>{switch(true){case this.realtime.reconnectAttempts<5:return 1000;case this.realtime.reconnectAttempts<15:return 5000;case this.realtime.reconnectAttempts<100:return 10000;default:return 60000;}},createSocket:()=>{var _a,_b;if(this.realtime.channels.size<1)
return;const channels=new URLSearchParams();channels.set('project',this.config.project);this.realtime.channels.forEach(channel=>{channels.append('channels[]',channel);});const url=this.config.endpointRealtime+'/realtime?'+channels.toString();if(url!==this.realtime.url||!this.realtime.socket||((_a=this.realtime.socket)===null||_a===void 0?void 0:_a.readyState)>WebSocket.OPEN){if(this.realtime.socket&&((_b=this.realtime.socket)===null||_b===void 0?void 0:_b.readyState)<WebSocket.CLOSING){this.realtime.reconnect=false;this.realtime.socket.close();}
this.realtime.url=url;this.realtime.socket=new WebSocket(url);this.realtime.socket.addEventListener('message',this.realtime.onMessage);this.realtime.socket.addEventListener('open',_event=>{this.realtime.reconnectAttempts=0;});this.realtime.socket.addEventListener('close',event=>{var _a,_b,_c;if(!this.realtime.reconnect||(((_b=(_a=this.realtime)===null||_a===void 0?void 0:_a.lastMessage)===null||_b===void 0?void 0:_b.type)==='error'&&((_c=this.realtime)===null||_c===void 0?void 0:_c.lastMessage.data).code===1008)){this.realtime.reconnect=true;return;}
const timeout=this.realtime.getTimeout();console.error(`Realtime got disconnected. Reconnect will be attempted in ${timeout / 1000} seconds.`,event.reason);setTimeout(()=>{this.realtime.reconnectAttempts++;this.realtime.createSocket();},timeout);});}},onMessage:(event)=>{var _a,_b;try{const message=JSON.parse(event.data);this.realtime.lastMessage=message;switch(message.type){case'connected':const cookie=JSON.parse((_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'{}');const session=cookie===null||cookie===void 0?void 0:cookie[`a_session_${this.config.project}`];const messageData=message.data;if(session&&!messageData.user){(_b=this.realtime.socket)===null||_b===void 0?void 0:_b.send(JSON.stringify({type:'authentication',data:{session}}));}
@ -296,7 +296,7 @@ if(typeof activate!=='undefined'){payload['activate']=activate;}
const uri=new URL(this.config.endpoint+path);const size=code.size;if(size<=Appwrite.CHUNK_SIZE){return yield this.call('post',uri,{'content-type':'multipart/form-data',},payload);}
let id=undefined;let response=undefined;const headers={'content-type':'multipart/form-data',};let counter=0;const totalCounters=Math.ceil(size/Appwrite.CHUNK_SIZE);for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=code.slice(start,end+1);payload['code']=new File([stream],code.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE-1,size)/size*100,sizeUploaded:end,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
if(typeof deploymentId==='undefined'){throw new AppwriteException('Missing required parameter: "deploymentId"');}
let path='/functions/{functionId}/deployments/{deploymentId}'.replace('{functionId}',functionId).replace('{deploymentId}',deploymentId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateDeployment:(functionId,deploymentId)=>__awaiter(this,void 0,void 0,function*(){if(typeof functionId==='undefined'){throw new AppwriteException('Missing required parameter: "functionId"');}
@ -501,7 +501,7 @@ let id=undefined;let response=undefined;const headers={'content-type':'multipart
catch(e){}}
for(counter;counter<totalCounters;counter++){const start=(counter*Appwrite.CHUNK_SIZE);const end=Math.min((((counter*Appwrite.CHUNK_SIZE)+Appwrite.CHUNK_SIZE)-1),size);headers['content-range']='bytes '+start+'-'+end+'/'+size;if(id){headers['x-appwrite-id']=id;}
const stream=file.slice(start,end+1);payload['file']=new File([stream],file.name);response=yield this.call('post',uri,headers,payload);if(!id){id=response['$id'];}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE,size)/size*100,sizeUploaded:end+1,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
if(onProgress){onProgress({$id:response.$id,progress:Math.min((counter+1)*Appwrite.CHUNK_SIZE-1,size)/size*100,sizeUploaded:end,chunksTotal:response.chunksTotal,chunksUploaded:response.chunksUploaded});}}
return response;}),getFile:(bucketId,fileId)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
if(typeof fileId==='undefined'){throw new AppwriteException('Missing required parameter: "fileId"');}
let path='/storage/buckets/{bucketId}/files/{fileId}'.replace('{bucketId}',bucketId).replace('{fileId}',fileId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),updateFile:(bucketId,fileId,read,write)=>__awaiter(this,void 0,void 0,function*(){if(typeof bucketId==='undefined'){throw new AppwriteException('Missing required parameter: "bucketId"');}
@ -549,7 +549,10 @@ let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=n
if(typeof name==='undefined'){throw new AppwriteException('Missing required parameter: "name"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};if(typeof name!=='undefined'){payload['name']=name;}
const uri=new URL(this.config.endpoint+path);return yield this.call('put',uri,{'content-type':'application/json',},payload);}),delete:(teamId)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),getMemberships:(teamId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}'.replace('{teamId}',teamId);let payload={};const uri=new URL(this.config.endpoint+path);return yield this.call('delete',uri,{'content-type':'application/json',},payload);}),listLogs:(teamId,limit,offset)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}/logs'.replace('{teamId}',teamId);let payload={};if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
const uri=new URL(this.config.endpoint+path);return yield this.call('get',uri,{'content-type':'application/json',},payload);}),getMemberships:(teamId,search,limit,offset,cursor,cursorDirection,orderType)=>__awaiter(this,void 0,void 0,function*(){if(typeof teamId==='undefined'){throw new AppwriteException('Missing required parameter: "teamId"');}
let path='/teams/{teamId}/memberships'.replace('{teamId}',teamId);let payload={};if(typeof search!=='undefined'){payload['search']=search;}
if(typeof limit!=='undefined'){payload['limit']=limit;}
if(typeof offset!=='undefined'){payload['offset']=offset;}
@ -630,7 +633,7 @@ setJWT(value){this.headers['X-Appwrite-JWT']=value;this.config.jwt=value;return
setLocale(value){this.headers['X-Appwrite-Locale']=value;this.config.locale=value;return this;}
setMode(value){this.headers['X-Appwrite-Mode']=value;this.config.mode=value;return this;}
subscribe(channels,callback){let channelArray=typeof channels==='string'?[channels]:channels;channelArray.forEach(channel=>this.realtime.channels.add(channel));const counter=this.realtime.subscriptionsCounter++;this.realtime.subscriptions.set(counter,{channels:channelArray,callback});this.realtime.connect();return()=>{this.realtime.subscriptions.delete(counter);this.realtime.cleanUp(channelArray);this.realtime.connect();};}
call(method,url,headers={},params={}){var _a,_b;return __awaiter(this,void 0,void 0,function*(){method=method.toUpperCase();headers=Object.assign(Object.assign({},headers),this.headers);let options={method,headers,credentials:'include'};if(typeof window!=='undefined'&&window.localStorage){headers['X-Fallback-Cookies']=(_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'';}
call(method,url,headers={},params={}){var _a,_b;return __awaiter(this,void 0,void 0,function*(){method=method.toUpperCase();headers=Object.assign({},this.headers,headers);let options={method,headers,credentials:'include'};if(typeof window!=='undefined'&&window.localStorage){headers['X-Fallback-Cookies']=(_a=window.localStorage.getItem('cookieFallback'))!==null&&_a!==void 0?_a:'';}
if(method==='GET'){for(const[key,value]of Object.entries(this.flatten(params))){url.searchParams.append(key,value);}}
else{switch(headers['content-type']){case'application/json':options.body=JSON.stringify(params);break;case'multipart/form-data':let formData=new FormData();for(const key in params){if(Array.isArray(params[key])){params[key].forEach((value)=>{formData.append(key+'[]',value);});}
else{formData.append(key,params[key]);}}

View file

@ -48,7 +48,7 @@
mode: '',
};
this.headers = {
'x-sdk-version': 'appwrite:web:4.0.4',
'x-sdk-version': 'appwrite:web:5.0.0',
'X-Appwrite-Response-Format': '0.13.0',
};
this.realtime = {
@ -371,7 +371,7 @@
*
* Update currently logged in user password. For validation, user is required
* to pass in the new password, and the old password. For users created with
* OAuth and Team Invites, oldPassword is optional.
* OAuth, Team Invites and Magic URL, oldPassword is optional.
*
* @param {string} password
* @param {string} oldPassword
@ -768,6 +768,9 @@
/**
* Update Session (Refresh Tokens)
*
* Access tokens have limited lifespan and expire to mitigate security risks.
* If session was created using an OAuth provider, this route can be used to
* "refresh" the access token.
*
* @param {string} sessionId
* @throws {AppwriteException}
@ -1923,9 +1926,7 @@
/**
* Delete Document
*
* Delete a document by its unique ID. This endpoint deletes only the parent
* documents, its attributes and relations to other documents. Child documents
* **will not** be deleted.
* Delete a document by its unique ID.
*
* @param {string} collectionId
* @param {string} documentId
@ -2263,7 +2264,7 @@
}, payload);
}),
/**
* List the currently active function runtimes.
* List runtimes
*
* Get a list of all runtimes that are currently active on your instance.
*
@ -2488,8 +2489,8 @@
if (onProgress) {
onProgress({
$id: response.$id,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE, size) / size * 100,
sizeUploaded: end + 1,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE - 1, size) / size * 100,
sizeUploaded: end,
chunksTotal: response.chunksTotal,
chunksUploaded: response.chunksUploaded
});
@ -4329,8 +4330,8 @@
if (onProgress) {
onProgress({
$id: response.$id,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE, size) / size * 100,
sizeUploaded: end + 1,
progress: Math.min((counter + 1) * Appwrite.CHUNK_SIZE - 1, size) / size * 100,
sizeUploaded: end,
chunksTotal: response.chunksTotal,
chunksUploaded: response.chunksUploaded
});
@ -4744,6 +4745,34 @@
'content-type': 'application/json',
}, payload);
}),
/**
* List Team Logs
*
* Get the team activity logs list by its unique ID.
*
* @param {string} teamId
* @param {number} limit
* @param {number} offset
* @throws {AppwriteException}
* @returns {Promise}
*/
listLogs: (teamId, limit, offset) => __awaiter(this, void 0, void 0, function* () {
if (typeof teamId === 'undefined') {
throw new AppwriteException('Missing required parameter: "teamId"');
}
let path = '/teams/{teamId}/logs'.replace('{teamId}', teamId);
let payload = {};
if (typeof limit !== 'undefined') {
payload['limit'] = limit;
}
if (typeof offset !== 'undefined') {
payload['offset'] = offset;
}
const uri = new URL(this.config.endpoint + path);
return yield this.call('get', uri, {
'content-type': 'application/json',
}, payload);
}),
/**
* Get Team Memberships
*
@ -5536,7 +5565,7 @@
var _a, _b;
return __awaiter(this, void 0, void 0, function* () {
method = method.toUpperCase();
headers = Object.assign(Object.assign({}, headers), this.headers);
headers = Object.assign({}, this.headers, headers);
let options = {
method,
headers,

View file

@ -0,0 +1,25 @@
<?php
namespace Appwrite\Utopia\Database\Validator;
use Utopia\Database\Document;
use Utopia\Database\Validator\OrderAttributes as ValidatorOrderAttributes;
class OrderAttributes extends ValidatorOrderAttributes
{
/**
* Expression constructor
*
* @param Document[] $attributes
* @param Document[] $indexes
* @param bool $strict
*/
public function __construct($attributes, $indexes, $strict)
{
// Remove failed/stuck/processing indexes
$indexes = \array_filter($indexes, function($index) {
return $index->getAttribute('status') === 'available';
});
parent::__construct($attributes, $indexes, $strict);
}
}

View file

@ -0,0 +1,25 @@
<?php
namespace Appwrite\Utopia\Database\Validator;
use Utopia\Database\Document;
use Utopia\Database\Validator\Queries as ValidatorQueries;
class Queries extends ValidatorQueries
{
/**
* Expression constructor
*
* @param Document[] $attributes
* @param Document[] $indexes
* @param bool $strict
*/
public function __construct($attributes, $indexes, $strict)
{
// Remove failed/stuck/processing indexes
$indexes = \array_filter($indexes, function($index) {
return $index->getAttribute('status') === 'available';
});
parent::__construct($attributes, $indexes, $strict);
}
}