appwrite/app/controllers/api/projects.php

2028 lines
82 KiB
PHP
Raw Normal View History

2019-12-14 19:33:29 +00:00
<?php
2024-05-29 08:57:25 +00:00
use Ahc\Jwt\JWT;
2024-02-11 14:51:19 +00:00
use Appwrite\Auth\Validator\MockNumber;
use Appwrite\Event\Delete;
2023-12-19 15:08:09 +00:00
use Appwrite\Event\Mail;
2023-08-23 02:04:36 +00:00
use Appwrite\Extend\Exception;
2025-04-14 11:56:42 +00:00
use Appwrite\Network\Platform;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\AuthType;
use Appwrite\SDK\ContentType;
use Appwrite\SDK\Deprecated;
2025-01-17 04:31:39 +00:00
use Appwrite\SDK\Method;
use Appwrite\SDK\Response as SDKResponse;
2023-08-23 02:04:36 +00:00
use Appwrite\Template\Template;
2026-02-09 13:34:05 +00:00
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Keys;
2020-08-14 21:56:50 +00:00
use Appwrite\Utopia\Response;
2023-08-23 02:04:36 +00:00
use PHPMailer\PHPMailer\PHPMailer;
2021-06-07 05:17:29 +00:00
use Utopia\Config\Config;
2021-08-16 13:27:15 +00:00
use Utopia\Database\Database;
2021-05-15 22:41:42 +00:00
use Utopia\Database\Document;
2023-08-04 19:15:32 +00:00
use Utopia\Database\Exception\Duplicate;
2026-02-09 15:58:44 +00:00
use Utopia\Database\Exception\Query as QueryException;
2022-12-14 15:42:25 +00:00
use Utopia\Database\Helpers\ID;
2022-12-14 16:04:06 +00:00
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
2023-08-23 02:04:36 +00:00
use Utopia\Database\Query;
2023-03-01 12:00:36 +00:00
use Utopia\Database\Validator\Datetime as DatetimeValidator;
2026-02-09 15:58:44 +00:00
use Utopia\Database\Validator\Query\Cursor;
2021-05-15 22:41:42 +00:00
use Utopia\Database\Validator\UID;
2026-03-19 21:54:45 +00:00
use Utopia\Emails\Validator\Email;
use Utopia\Http\Http;
2024-03-08 12:57:20 +00:00
use Utopia\Locale\Locale;
2024-04-01 11:08:46 +00:00
use Utopia\System\System;
2024-10-08 07:54:40 +00:00
use Utopia\Validator\ArrayList;
use Utopia\Validator\Boolean;
use Utopia\Validator\Hostname;
use Utopia\Validator\Integer;
use Utopia\Validator\Nullable;
2024-10-08 07:54:40 +00:00
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
2026-02-04 05:30:22 +00:00
Http::init()
2022-07-22 06:00:42 +00:00
->groups(['projects'])
2022-08-02 01:10:48 +00:00
->inject('project')
2022-07-22 06:00:42 +00:00
->action(function (Document $project) {
if ($project->getId() !== 'console') {
throw new Exception(Exception::GENERAL_ACCESS_FORBIDDEN);
2022-07-22 06:00:42 +00:00
}
});
2021-07-31 21:36:18 +00:00
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId')
2023-08-01 15:26:48 +00:00
->desc('Get project')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
2019-12-14 19:33:29 +00:00
->label('scope', 'projects.read')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'projects',
2025-01-17 04:31:39 +00:00
name: 'get',
description: '/docs/references/projects/get.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-14 19:33:29 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-14 19:33:29 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
2020-12-26 16:33:36 +00:00
});
2019-12-14 19:33:29 +00:00
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/service')
2021-07-29 10:28:17 +00:00
->desc('Update service status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'projects',
2025-01-17 04:31:39 +00:00
name: 'updateServiceStatus',
description: '/docs/references/projects/update-service-status.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-06-07 00:54:51 +00:00
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional'])), true), 'Service name.')
2021-07-31 21:36:18 +00:00
->param('status', null, new Boolean(), 'Service status.')
2021-07-29 10:28:17 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $service, bool $status, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2021-07-29 10:28:17 +00:00
2021-07-29 10:56:25 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2021-07-29 10:28:17 +00:00
}
2021-07-31 20:54:50 +00:00
$services = $project->getAttribute('services', []);
$services[$service] = $status;
2021-07-29 10:28:17 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('services', $services));
2021-07-29 10:28:17 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/service/all')
->desc('Update all service status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'projects',
2025-01-17 04:31:39 +00:00
name: 'updateServiceStatusAll',
description: '/docs/references/projects/update-service-status-all.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('status', null, new Boolean(), 'Service status.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $status, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
2024-06-07 00:54:51 +00:00
$allServices = array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional']));
$services = [];
2023-04-05 05:52:59 +00:00
foreach ($allServices as $service) {
$services[$service] = $status;
}
2023-04-05 05:52:59 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('services', $services));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/api')
2024-03-04 22:12:54 +00:00
->desc('Update API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatus',
description: '/docs/references/projects/update-api-status.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.updateAPIStatus',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'projects',
name: 'updateAPIStatus',
description: '/docs/references/projects/update-api-status.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-03-04 22:12:54 +00:00
->param('api', '', new WhiteList(array_keys(Config::getParam('apis')), true), 'API name.')
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $api, bool $status, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-03-04 22:12:54 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$apis = $project->getAttribute('apis', []);
$apis[$api] = $status;
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('apis', $apis));
2024-03-04 22:12:54 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/api/all')
2024-03-04 22:28:42 +00:00
->desc('Update all API status')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'projects',
name: 'updateApiStatusAll',
description: '/docs/references/projects/update-api-status-all.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.updateAPIStatusAll',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'projects',
name: 'updateAPIStatusAll',
description: '/docs/references/projects/update-api-status-all.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-03-04 22:28:42 +00:00
->param('status', null, new Boolean(), 'API status.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $status, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-03-04 22:28:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$allApis = array_keys(Config::getParam('apis'));
$apis = [];
foreach ($allApis as $api) {
$apis[$api] = $status;
}
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('apis', $apis));
2024-03-04 22:28:42 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/oauth2')
2023-08-01 15:26:48 +00:00
->desc('Update project OAuth2')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
2019-12-14 19:33:29 +00:00
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateOAuth2',
description: '/docs/references/projects/update-oauth2.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2023-10-25 17:33:23 +00:00
->param('provider', '', new WhiteList(\array_keys(Config::getParam('oAuthProviders')), true), 'Provider Name')
->param('appId', null, new Nullable(new Text(256)), 'Provider app ID. Max length: 256 chars.', true)
->param('secret', null, new Nullable(new text(512)), 'Provider secret key. Max length: 512 chars.', true)
->param('enabled', null, new Nullable(new Boolean()), 'Provider status. Set to \'false\' to disable new session creation.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $provider, ?string $appId, ?string $secret, ?bool $enabled, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-14 19:33:29 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-14 19:33:29 +00:00
2023-10-25 17:33:23 +00:00
$providers = $project->getAttribute('oAuthProviders', []);
2022-08-19 09:43:03 +00:00
if ($appId !== null) {
$providers[$provider . 'Appid'] = $appId;
}
2022-08-19 09:43:03 +00:00
if ($secret !== null) {
$providers[$provider . 'Secret'] = $secret;
}
2022-08-19 09:43:03 +00:00
if ($enabled !== null) {
$providers[$provider . 'Enabled'] = $enabled;
}
2021-08-06 10:35:50 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('oAuthProviders', $providers));
2020-06-30 15:46:42 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
2020-12-26 16:33:36 +00:00
});
2019-12-14 19:33:29 +00:00
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/session-alerts')
2024-06-21 18:21:05 +00:00
->desc('Update project sessions emails')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateSessionAlerts',
description: '/docs/references/projects/update-session-alerts.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-07-03 07:11:15 +00:00
->param('alerts', false, new Boolean(true), 'Set to true to enable session emails.')
2024-06-21 18:21:05 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $alerts, Response $response, Database $dbForPlatform) {
2024-06-21 18:21:05 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-06-21 18:21:05 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2024-07-03 07:11:15 +00:00
$auths['sessionAlerts'] = $alerts;
2024-06-21 18:21:05 +00:00
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-06-21 18:21:05 +00:00
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/memberships-privacy')
2024-11-07 14:33:44 +00:00
->desc('Update project memberships privacy attributes')
2024-11-01 11:38:56 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateMembershipsPrivacy',
description: '/docs/references/projects/update-memberships-privacy.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-11-06 20:15:31 +00:00
->param('userName', true, new Boolean(true), 'Set to true to show userName to members of a team.')
->param('userEmail', true, new Boolean(true), 'Set to true to show email to members of a team.')
->param('mfa', true, new Boolean(true), 'Set to true to show mfa to members of a team.')
2024-11-01 11:38:56 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $userName, bool $userEmail, bool $mfa, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-11-01 11:38:56 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2024-11-05 16:05:21 +00:00
2024-11-06 20:15:31 +00:00
$auths['membershipsUserName'] = $userName;
$auths['membershipsUserEmail'] = $userEmail;
$auths['membershipsMfa'] = $mfa;
2024-11-01 11:38:56 +00:00
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-11-01 11:38:56 +00:00
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/limit')
2023-08-01 15:26:48 +00:00
->desc('Update project users limit')
2021-02-28 11:40:59 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthLimit',
description: '/docs/references/projects/update-auth-limit.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2022-01-15 23:25:00 +00:00
->param('limit', false, new Range(0, APP_LIMIT_USERS), 'Set the max number of users allowed in this project. Use 0 for unlimited.')
2021-02-28 11:40:59 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, int $limit, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2021-02-28 11:40:59 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2021-02-28 11:40:59 +00:00
}
2021-08-06 08:34:17 +00:00
$auths = $project->getAttribute('auths', []);
$auths['limit'] = $limit;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
2021-02-28 11:40:59 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
2022-11-14 09:15:55 +00:00
});
2022-11-14 09:33:49 +00:00
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/duration')
2023-08-01 15:26:48 +00:00
->desc('Update project authentication duration')
2022-11-14 09:15:55 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthDuration',
description: '/docs/references/projects/update-auth-duration.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2022-11-15 10:25:34 +00:00
->param('duration', 31536000, new Range(0, 31536000), 'Project session length in seconds. Max length: 31536000 seconds.')
2022-11-14 09:15:55 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, int $duration, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2022-11-14 09:15:55 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2022-11-15 10:25:34 +00:00
$auths['duration'] = $duration;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
2022-11-14 09:15:55 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
2021-02-28 11:40:59 +00:00
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/:method')
2023-08-01 15:26:48 +00:00
->desc('Update project auth method status. Use this endpoint to enable or disable a given auth method for this project.')
2021-02-28 11:40:59 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthStatus',
description: '/docs/references/projects/update-auth-status.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2021-06-07 05:17:29 +00:00
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false)
2021-02-28 15:00:27 +00:00
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
2021-02-28 11:40:59 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $method, bool $status, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-10-08 07:54:40 +00:00
$auth = Config::getParam('auth')[$method] ?? [];
$authKey = $auth['key'] ?? '';
2021-02-28 15:00:27 +00:00
$status = ($status === '1' || $status === 'true' || $status === 1 || $status === true);
2021-02-28 11:40:59 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2021-02-28 11:40:59 +00:00
}
2021-08-06 07:42:31 +00:00
$auths = $project->getAttribute('auths', []);
$auths[$authKey] = $status;
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('auths', $auths));
2021-02-28 11:40:59 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
2021-02-28 11:40:59 +00:00
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/password-history')
2023-01-30 06:05:32 +00:00
->desc('Update authentication password history. Use this endpoint to set the number of password history to save and 0 to disable password history.')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthPasswordHistory',
description: '/docs/references/projects/update-auth-password-history.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2022-12-19 05:07:41 +00:00
->param('limit', 0, new Range(0, APP_LIMIT_USER_PASSWORD_HISTORY), 'Set the max number of passwords to store in user history. User can\'t choose a new password that is already stored in the password history list. Max number of passwords allowed in history is' . APP_LIMIT_USER_PASSWORD_HISTORY . '. Default value is 0')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, int $limit, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['passwordHistory'] = $limit;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/password-dictionary')
2023-04-11 23:01:50 +00:00
->desc('Update authentication password dictionary status. Use this endpoint to enable or disable the dicitonary check for user password')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthPasswordDictionary',
description: '/docs/references/projects/update-auth-password-dictionary.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2023-01-30 06:06:52 +00:00
->param('enabled', false, new Boolean(false), 'Set whether or not to enable checking user\'s password against most commonly used passwords. Default is false.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2023-01-30 06:06:52 +00:00
$auths['passwordDictionary'] = $enabled;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/personal-data')
->desc('Update personal data check')
2023-04-11 23:01:50 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updatePersonalDataCheck',
description: '/docs/references/projects/update-personal-data-check.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(false), 'Set whether or not to check a password for similarity with personal data. Default is false.')
2023-04-11 23:01:50 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-04-11 23:01:50 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2023-07-19 22:24:32 +00:00
$auths['personalDataCheck'] = $enabled;
2023-04-11 23:01:50 +00:00
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
2023-04-11 23:01:50 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/max-sessions')
2023-08-01 15:26:48 +00:00
->desc('Update project user sessions limit')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateAuthSessionsLimit',
description: '/docs/references/projects/update-auth-sessions-limit.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('limit', false, new Range(1, APP_LIMIT_USER_SESSIONS_MAX), 'Set the max number of users allowed in this project. Value allowed is between 1-' . APP_LIMIT_USER_SESSIONS_MAX . '. Default is ' . APP_LIMIT_USER_SESSIONS_DEFAULT)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, int $limit, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2022-12-09 11:33:26 +00:00
$auths['maxSessions'] = $limit;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
2024-10-08 07:54:40 +00:00
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/mock-numbers')
2024-02-11 14:51:19 +00:00
->desc('Update the mock numbers for the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'updateMockNumbers',
description: '/docs/references/projects/update-mock-numbers.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-02-11 14:51:19 +00:00
->param('numbers', '', new ArrayList(new MockNumber(), 10), 'An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, array $numbers, Response $response, Database $dbForPlatform) {
2024-02-11 14:51:19 +00:00
2024-07-21 19:30:06 +00:00
$uniqueNumbers = [];
foreach ($numbers as $number) {
2024-07-21 19:45:39 +00:00
if (isset($uniqueNumbers[$number['phone']])) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Duplicate phone numbers are not allowed.');
2024-07-21 19:30:06 +00:00
}
2024-07-21 19:45:39 +00:00
$uniqueNumbers[$number['phone']] = $number['otp'];
2024-07-21 19:30:06 +00:00
}
2024-07-22 09:08:02 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-02-11 14:51:19 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['mockNumbers'] = $numbers;
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('auths', $auths));
2024-02-11 14:51:19 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::delete('/v1/projects/:projectId')
2023-08-01 15:26:48 +00:00
->desc('Delete project')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
2024-07-22 08:03:53 +00:00
->label('audits.event', 'projects.delete')
->label('audits.resource', 'project/{request.projectId}')
2019-12-14 19:33:29 +00:00
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'projects',
2025-01-17 04:31:39 +00:00
name: 'delete',
description: '/docs/references/projects/delete.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('user')
->inject('dbForPlatform')
2022-12-20 16:11:30 +00:00
->inject('queueForDeletes')
->action(function (string $projectId, Response $response, Document $user, Database $dbForPlatform, Delete $queueForDeletes) {
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-14 19:33:29 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-14 19:33:29 +00:00
2022-12-20 16:11:30 +00:00
$queueForDeletes
2024-10-01 14:17:36 +00:00
->setProject($project)
2022-04-17 20:34:32 +00:00
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($project);
2021-06-07 05:17:29 +00:00
if (!$dbForPlatform->deleteDocument('projects', $projectId)) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove project from DB');
2019-12-18 10:24:54 +00:00
}
2020-06-30 15:46:42 +00:00
$response->noContent();
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
// Keys
2026-02-04 05:30:22 +00:00
Http::post('/v1/projects/:projectId/keys')
2023-08-01 15:26:48 +00:00
->desc('Create key')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'keys.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'keys',
2025-01-17 04:31:39 +00:00
name: 'createKey',
description: '/docs/references/projects/create-key.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_KEY,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2026-02-09 15:42:14 +00:00
// TODO: When migrating to Platform API, mark keyId required for consistency
->param('keyId', 'unique()', fn (Database $dbForPlatform) => new CustomId($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key ID. Choose a custom ID or generate a random ID with `ID.unique()`. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true, ['dbForPlatform'])->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
2026-02-09 13:34:05 +00:00
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
2026-02-09 15:42:14 +00:00
$keyId = $keyId == 'unique()' ? ID::unique() : $keyId;
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
$key = new Document([
2026-02-09 13:34:05 +00:00
'$id' => $keyId,
'$permissions' => [
2022-08-14 05:21:11 +00:00
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceInternalId' => $project->getSequence(),
'resourceId' => $project->getId(),
'resourceType' => 'projects',
2020-06-30 15:46:42 +00:00
'name' => $name,
'scopes' => $scopes,
2022-05-31 15:41:12 +00:00
'expire' => $expire,
2022-08-05 10:20:48 +00:00
'sdks' => [],
2022-08-19 09:54:26 +00:00
'accessedAt' => null,
2024-05-16 08:39:15 +00:00
'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)),
2020-06-30 15:46:42 +00:00
]);
try {
$key = $dbForPlatform->createDocument('keys', $key);
} catch (Duplicate) {
throw new Exception(Exception::KEY_ALREADY_EXISTS);
}
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2020-06-30 15:46:42 +00:00
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($key, Response::MODEL_KEY);
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/keys')
2023-08-01 15:26:48 +00:00
->desc('List keys')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'keys.read')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'keys',
2025-01-17 04:31:39 +00:00
name: 'listKeys',
description: '/docs/references/projects/list-keys.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_KEY_LIST,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2026-02-09 15:58:44 +00:00
->param('queries', [], new Keys(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Keys::ALLOWED_ATTRIBUTES), true)
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
2026-02-09 15:58:44 +00:00
->action(function (string $projectId, array $queries, bool $includeTotal, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2020-06-30 15:46:42 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-01-31 22:34:07 +00:00
}
2020-06-30 15:46:42 +00:00
try {
$queries = Query::parseQueries($queries);
} catch (QueryException $e) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
}
2026-02-10 10:48:29 +00:00
// Backwards compatibility
if (\count(Query::getByType($queries, [Query::TYPE_LIMIT])) === 0) {
$queries[] = Query::limit(5000);
}
$queries[] = Query::equal('resourceType', ['projects']);
$queries[] = Query::equal('resourceInternalId', [$project->getSequence()]);
$cursor = Query::getCursorQueries($queries, false);
$cursor = \reset($cursor);
if ($cursor !== false) {
$validator = new Cursor();
if (!$validator->isValid($cursor)) {
throw new Exception(Exception::GENERAL_QUERY_INVALID, $validator->getDescription());
}
$keyId = $cursor->getValue();
2026-02-09 19:07:33 +00:00
$cursorDocument = $dbForPlatform->getDocument('keys', $keyId);
if ($cursorDocument->isEmpty()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Key '{$keyId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$filterQueries = Query::groupByType($queries)['filters'];
$keys = $dbForPlatform->find('keys', $queries);
2020-08-14 21:56:50 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic(new Document([
2021-05-27 10:09:14 +00:00
'keys' => $keys,
'total' => $includeTotal ? $dbForPlatform->count('keys', $filterQueries, APP_LIMIT_COUNT) : 0,
2020-08-14 21:56:50 +00:00
]), Response::MODEL_KEY_LIST);
2020-12-26 16:33:36 +00:00
});
2020-01-31 22:34:07 +00:00
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/keys/:keyId')
2023-08-01 15:26:48 +00:00
->desc('Get key')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'keys.read')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'keys',
2025-01-17 04:31:39 +00:00
name: 'getKey',
description: '/docs/references/projects/get-key.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_KEY,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2020-01-31 22:34:07 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2020-01-31 22:34:07 +00:00
$key = $dbForPlatform->findOne('keys', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$keyId]),
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
]);
2020-01-31 22:34:07 +00:00
if ($key->isEmpty()) {
throw new Exception(Exception::KEY_NOT_FOUND);
2020-01-31 22:34:07 +00:00
}
2020-06-30 15:46:42 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($key, Response::MODEL_KEY);
2020-12-26 16:33:36 +00:00
});
2020-01-31 22:34:07 +00:00
2026-02-04 05:30:22 +00:00
Http::put('/v1/projects/:projectId/keys/:keyId')
2023-08-01 15:26:48 +00:00
->desc('Update key')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'keys.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'keys',
2025-01-17 04:31:39 +00:00
name: 'updateKey',
description: '/docs/references/projects/update-key.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_KEY,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
2020-09-10 14:40:14 +00:00
->param('name', null, new Text(128), 'Key name. Max length: 128 chars.')
->param('scopes', null, new Nullable(new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE)), 'Key scopes list. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' events are allowed.')
->param('expire', null, new Nullable(new DatetimeValidator()), 'Expiration time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. Use null for unlimited expiration.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, string $name, array $scopes, ?string $expire, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$key = $dbForPlatform->findOne('keys', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$keyId]),
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
]);
2019-12-18 10:24:54 +00:00
if ($key->isEmpty()) {
throw new Exception(Exception::KEY_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$key
->setAttribute('name', $name)
->setAttribute('scopes', $scopes)
->setAttribute('expire', $expire);
2019-12-18 10:24:54 +00:00
$dbForPlatform->updateDocument('keys', $key->getId(), $key);
2019-12-18 10:24:54 +00:00
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2021-06-07 05:17:29 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($key, Response::MODEL_KEY);
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
2026-02-04 05:30:22 +00:00
Http::delete('/v1/projects/:projectId/keys/:keyId')
2023-08-01 15:26:48 +00:00
->desc('Delete key')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'keys.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'keys',
2025-01-17 04:31:39 +00:00
name: 'deleteKey',
description: '/docs/references/projects/delete-key.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('keyId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Key unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $keyId, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$key = $dbForPlatform->findOne('keys', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$keyId]),
Query::equal('resourceType', ['projects']),
Query::equal('resourceInternalId', [$project->getSequence()]),
]);
if ($key->isEmpty()) {
throw new Exception(Exception::KEY_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$dbForPlatform->deleteDocument('keys', $key->getId());
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2020-06-30 15:46:42 +00:00
$response->noContent();
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
2024-05-29 08:57:25 +00:00
// JWT Keys
2026-02-04 05:30:22 +00:00
Http::post('/v1/projects/:projectId/jwts')
2024-05-29 08:57:25 +00:00
->groups(['api', 'projects'])
->desc('Create JWT')
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'auth',
2025-01-17 04:31:39 +00:00
name: 'createJWT',
description: '/docs/references/projects/create-jwt.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_JWT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('projectScopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
2024-05-29 08:57:25 +00:00
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, array $scopes, int $duration, Response $response, Database $dbForPlatform) {
2024-05-29 08:57:25 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2024-05-29 08:57:25 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => API_KEY_DYNAMIC . '_' . $jwt->encode([
'projectId' => $project->getId(),
'scopes' => $scopes
])]), Response::MODEL_JWT);
});
2019-12-18 10:24:54 +00:00
// Platforms
2026-02-04 05:30:22 +00:00
Http::post('/v1/projects/:projectId/platforms')
2023-08-01 15:26:48 +00:00
->desc('Create platform')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
2024-07-22 08:03:53 +00:00
->label('audits.event', 'platforms.create')
->label('audits.resource', 'project/{request.projectId}')
->label('scope', 'platforms.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'platforms',
2025-01-17 04:31:39 +00:00
name: 'createPlatform',
description: '/docs/references/projects/create-platform.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_CREATED,
model: Response::MODEL_PLATFORM,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2025-09-26 07:03:10 +00:00
->param(
'type',
null,
new WhiteList([
Platform::TYPE_WEB,
Platform::TYPE_FLUTTER_WEB,
Platform::TYPE_FLUTTER_IOS,
Platform::TYPE_FLUTTER_ANDROID,
Platform::TYPE_FLUTTER_LINUX,
Platform::TYPE_FLUTTER_MACOS,
Platform::TYPE_FLUTTER_WINDOWS,
Platform::TYPE_APPLE_IOS,
Platform::TYPE_APPLE_MACOS,
Platform::TYPE_APPLE_WATCHOS,
Platform::TYPE_APPLE_TVOS,
Platform::TYPE_ANDROID,
Platform::TYPE_UNITY,
Platform::TYPE_REACT_NATIVE_IOS,
Platform::TYPE_REACT_NATIVE_ANDROID,
], true),
'Platform type. Possible values are: web, flutter-web, flutter-ios, flutter-android, flutter-linux, flutter-macos, flutter-windows, apple-ios, apple-macos, apple-watchos, apple-tvos, android, unity, react-native-ios, react-native-android.'
)
2020-09-10 14:40:14 +00:00
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
2021-09-28 10:24:54 +00:00
->param('key', '', new Text(256), 'Package name for Android or bundle ID for iOS or macOS. Max length: 256 chars.', true)
2020-09-10 14:40:14 +00:00
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
2022-05-12 18:53:54 +00:00
->param('hostname', '', new Hostname(), 'Platform client hostname. Max length: 256 chars.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
2025-01-31 22:15:22 +00:00
->action(function (string $projectId, string $type, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
$platform = new Document([
2022-08-14 14:22:38 +00:00
'$id' => ID::unique(),
'$permissions' => [
2022-08-15 10:11:17 +00:00
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
2025-05-26 05:42:11 +00:00
'projectInternalId' => $project->getSequence(),
'projectId' => $project->getId(),
2020-06-30 15:46:42 +00:00
'type' => $type,
'name' => $name,
'key' => $key,
'store' => $store,
'hostname' => $hostname
2020-06-30 15:46:42 +00:00
]);
$platform = $dbForPlatform->createDocument('platforms', $platform);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2020-06-30 15:46:42 +00:00
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($platform, Response::MODEL_PLATFORM);
2020-12-26 16:33:36 +00:00
});
2021-06-07 05:17:29 +00:00
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/platforms')
2023-08-01 15:26:48 +00:00
->desc('List platforms')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'platforms.read')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'platforms',
2025-01-17 04:31:39 +00:00
name: 'listPlatforms',
description: '/docs/references/projects/list-platforms.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PLATFORM_LIST,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('total', true, new Boolean(true), 'When set to false, the total count returned will be 0 and will not be calculated.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $includeTotal, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2020-01-31 22:34:07 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-01-31 22:34:07 +00:00
}
2020-06-30 15:46:42 +00:00
$platforms = $dbForPlatform->find('platforms', [
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()]),
2022-08-11 23:53:52 +00:00
Query::limit(5000),
]);
2020-06-30 15:46:42 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic(new Document([
2021-05-27 10:09:14 +00:00
'platforms' => $platforms,
'total' => $includeTotal ? count($platforms) : 0,
2020-08-14 21:56:50 +00:00
]), Response::MODEL_PLATFORM_LIST);
2020-12-26 16:33:36 +00:00
});
2020-01-31 22:34:07 +00:00
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/platforms/:platformId')
2023-08-01 15:26:48 +00:00
->desc('Get platform')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'platforms.read')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'platforms',
2025-01-17 04:31:39 +00:00
name: 'getPlatform',
description: '/docs/references/projects/get-platform.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PLATFORM,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2020-01-31 22:34:07 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2020-01-31 22:34:07 +00:00
$platform = $dbForPlatform->findOne('platforms', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$platformId]),
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()]),
]);
2020-01-31 22:34:07 +00:00
if ($platform->isEmpty()) {
throw new Exception(Exception::PLATFORM_NOT_FOUND);
2020-01-31 22:34:07 +00:00
}
2020-06-30 15:46:42 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($platform, Response::MODEL_PLATFORM);
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
2026-02-04 05:30:22 +00:00
Http::put('/v1/projects/:projectId/platforms/:platformId')
2023-08-01 15:26:48 +00:00
->desc('Update platform')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
->label('scope', 'platforms.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'platforms',
2025-01-17 04:31:39 +00:00
name: 'updatePlatform',
description: '/docs/references/projects/update-platform.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PLATFORM,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
2020-09-10 14:40:14 +00:00
->param('name', null, new Text(128), 'Platform name. Max length: 128 chars.')
->param('key', '', new Text(256), 'Package name for android or bundle ID for iOS. Max length: 256 chars.', true)
->param('store', '', new Text(256), 'App store or Google Play store ID. Max length: 256 chars.', true)
2022-05-12 18:53:54 +00:00
->param('hostname', '', new Hostname(), 'Platform client URL. Max length: 256 chars.', true)
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, string $name, string $key, string $store, string $hostname, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$platform = $dbForPlatform->findOne('platforms', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$platformId]),
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()]),
]);
2019-12-18 10:24:54 +00:00
if ($platform->isEmpty()) {
throw new Exception(Exception::PLATFORM_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
2020-06-30 15:46:42 +00:00
$platform
->setAttribute('name', $name)
->setAttribute('key', $key)
->setAttribute('store', $store)
->setAttribute('hostname', $hostname);
2019-12-18 10:24:54 +00:00
$dbForPlatform->updateDocument('platforms', $platform->getId(), $platform);
2020-06-30 15:46:42 +00:00
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2021-05-15 22:41:42 +00:00
2021-07-25 14:47:18 +00:00
$response->dynamic($platform, Response::MODEL_PLATFORM);
2020-12-26 16:33:36 +00:00
});
2019-12-18 10:24:54 +00:00
2026-02-04 05:30:22 +00:00
Http::delete('/v1/projects/:projectId/platforms/:platformId')
2023-08-01 15:26:48 +00:00
->desc('Delete platform')
2020-06-25 18:32:12 +00:00
->groups(['api', 'projects'])
2024-07-22 08:03:53 +00:00
->label('audits.event', 'platforms.delete')
->label('audits.resource', 'project/{request.projectId}/platform/${request.platformId}')
->label('scope', 'platforms.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'platforms',
2025-01-17 04:31:39 +00:00
name: 'deletePlatform',
description: '/docs/references/projects/delete-platform.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
contentType: ContentType::NONE
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('platformId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Platform unique ID.', false, ['dbForPlatform'])
2020-12-26 16:33:36 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $platformId, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2019-12-18 10:24:54 +00:00
2021-05-15 22:41:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$platform = $dbForPlatform->findOne('platforms', [
2023-05-03 11:53:40 +00:00
Query::equal('$id', [$platformId]),
2025-05-26 05:42:11 +00:00
Query::equal('projectInternalId', [$project->getSequence()]),
]);
if ($platform->isEmpty()) {
throw new Exception(Exception::PLATFORM_NOT_FOUND);
2020-06-30 15:46:42 +00:00
}
2019-12-18 10:24:54 +00:00
$dbForPlatform->deleteDocument('platforms', $platformId);
$dbForPlatform->purgeCachedDocument('projects', $project->getId());
2020-06-30 15:46:42 +00:00
$response->noContent();
2020-12-26 16:33:36 +00:00
});
2020-02-22 08:10:30 +00:00
2023-03-09 01:59:10 +00:00
// CUSTOM SMTP and Templates
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/smtp')
2023-12-19 15:08:09 +00:00
->desc('Update SMTP')
2023-03-09 01:59:10 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmtp',
description: '/docs/references/projects/update-smtp.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.updateSMTP',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSMTP',
description: '/docs/references/projects/update-smtp.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2023-03-09 01:59:10 +00:00
->param('enabled', false, new Boolean(), 'Enable custom SMTP service')
2023-08-28 14:08:50 +00:00
->param('senderName', '', new Text(255, 0), 'Name of the email sender', true)
->param('senderEmail', '', new Email(), 'Email of the sender', true)
->param('replyTo', '', new Email(), 'Reply to email', true)
->param('host', '', new HostName(), 'SMTP server host name', true)
->param('port', 587, new Integer(), 'SMTP server port', true)
->param('username', '', new Text(0, 0), 'SMTP server username', true)
->param('password', '', new Text(0, 0), 'SMTP server password', true)
->param('secure', '', new WhiteList(['tls', 'ssl'], true), 'Does SMTP server use secure connection', true)
2023-03-09 01:59:10 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $enabled, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-03-09 01:59:10 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
// Ensure required params for when enabling SMTP
if ($enabled) {
if (empty($senderName)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sender name is required when enabling SMTP.');
2023-08-28 12:21:35 +00:00
} elseif (empty($senderEmail)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Sender email is required when enabling SMTP.');
2023-08-28 12:21:35 +00:00
} elseif (empty($host)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Host is required when enabling SMTP.');
2023-08-28 12:21:35 +00:00
} elseif (empty($port)) {
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Port is required when enabling SMTP.');
}
}
2023-05-23 05:09:17 +00:00
// validate SMTP settings
if ($enabled) {
$mail = new PHPMailer(true);
$mail->isSMTP();
$mail->SMTPAuth = (!empty($username) && !empty($password));
$mail->Username = $username;
$mail->Password = $password;
$mail->Host = $host;
$mail->Port = $port;
$mail->SMTPSecure = $secure;
$mail->SMTPAutoTLS = false;
$mail->Timeout = 5;
try {
$valid = $mail->SmtpConnect();
if (!$valid) {
throw new Exception('Connection is not valid.');
}
} catch (Throwable $error) {
throw new Exception(Exception::PROJECT_SMTP_CONFIG_INVALID, $error->getMessage());
}
}
2023-05-23 05:09:17 +00:00
// Save SMTP settings
if ($enabled) {
$smtp = [
'enabled' => $enabled,
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'replyTo' => $replyTo,
'host' => $host,
'port' => $port,
'username' => $username,
'password' => $password,
'secure' => $secure,
];
} else {
$smtp = [
'enabled' => false
];
}
2023-03-09 01:59:10 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('smtp', $smtp));
2023-03-09 01:59:10 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});
2026-02-04 05:30:22 +00:00
Http::post('/v1/projects/:projectId/smtp/tests')
2023-12-19 15:08:09 +00:00
->desc('Create SMTP test')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'templates',
name: 'createSmtpTest',
description: '/docs/references/projects/create-smtp-test.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.createSMTPTest',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'templates',
name: 'createSMTPTest',
description: '/docs/references/projects/create-smtp-test.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_NOCONTENT,
model: Response::MODEL_NONE,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
2024-01-15 10:18:17 +00:00
->param('emails', [], new ArrayList(new Email(), 10), 'Array of emails to send test email to. Maximum of 10 emails are allowed.')
2024-04-01 11:02:47 +00:00
->param('senderName', System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'), new Text(255, 0), 'Name of the email sender')
->param('senderEmail', System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM), new Email(), 'Email of the sender')
2024-01-15 10:18:17 +00:00
->param('replyTo', '', new Email(), 'Reply to email', true)
->param('host', '', new HostName(), 'SMTP server host name')
->param('port', 587, new Integer(), 'SMTP server port', true)
->param('username', '', new Text(0, 0), 'SMTP server username', true)
->param('password', '', new Text(0, 0), 'SMTP server password', true)
2024-10-02 17:26:45 +00:00
->param('secure', '', new WhiteList(['tls', 'ssl'], true), 'Does SMTP server use secure connection', true)
2023-12-19 15:08:09 +00:00
->inject('response')
->inject('dbForPlatform')
2023-12-19 15:08:09 +00:00
->inject('queueForMails')
2025-05-12 13:10:58 +00:00
->inject('plan')
->action(function (string $projectId, array $emails, string $senderName, string $senderEmail, string $replyTo, string $host, int $port, string $username, string $password, string $secure, Response $response, Database $dbForPlatform, Mail $queueForMails, array $plan) {
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-12-19 15:08:09 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
2023-12-19 21:46:35 +00:00
2024-01-17 11:22:08 +00:00
$replyToEmail = !empty($replyTo) ? $replyTo : $senderEmail;
$subject = 'Custom SMTP email sample';
$template = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-smtp-test.tpl');
$template
->setParam('{{from}}', "{$senderName} ({$senderEmail})")
2025-05-12 13:10:58 +00:00
->setParam('{{replyTo}}', "{$senderName} ({$replyToEmail})")
2025-05-12 13:20:59 +00:00
->setParam('{{logoUrl}}', $plan['logoUrl'] ?? APP_EMAIL_LOGO_URL)
2025-05-15 07:44:14 +00:00
->setParam('{{accentColor}}', $plan['accentColor'] ?? APP_EMAIL_ACCENT_COLOR)
2025-05-12 13:20:59 +00:00
->setParam('{{twitterUrl}}', $plan['twitterUrl'] ?? APP_SOCIAL_TWITTER)
->setParam('{{discordUrl}}', $plan['discordUrl'] ?? APP_SOCIAL_DISCORD)
->setParam('{{githubUrl}}', $plan['githubUrl'] ?? APP_SOCIAL_GITHUB_APPWRITE)
->setParam('{{termsUrl}}', $plan['termsUrl'] ?? APP_EMAIL_TERMS_URL)
->setParam('{{privacyUrl}}', $plan['privacyUrl'] ?? APP_EMAIL_PRIVACY_URL);
2024-01-15 10:18:17 +00:00
2024-01-15 11:02:05 +00:00
foreach ($emails as $email) {
2024-01-15 10:18:17 +00:00
$queueForMails
->setSmtpHost($host)
->setSmtpPort($port)
->setSmtpUsername($username)
->setSmtpPassword($password)
->setSmtpSecure($secure)
->setSmtpReplyTo($replyTo)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName)
->setRecipient($email)
->setName('')
2025-07-20 09:10:30 +00:00
->setBodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
2024-01-17 11:22:08 +00:00
->setBody($template->render())
2024-01-15 10:18:17 +00:00
->setVariables([])
->setSubject($subject)
->trigger();
2023-12-19 15:08:09 +00:00
}
2026-04-01 17:31:11 +00:00
$response->noContent();
2023-12-19 15:08:09 +00:00
});
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/templates/sms/:type/:locale')
2023-05-11 06:56:16 +00:00
->desc('Get custom SMS template')
2023-03-09 13:44:31 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'templates',
name: 'getSmsTemplate',
description: '/docs/references/projects/get-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.getSMSTemplate',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'templates',
name: 'getSMSTemplate',
description: '/docs/references/projects/get-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-03-09 13:44:31 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
2023-08-25 15:13:25 +00:00
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-03-09 13:44:31 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2024-10-08 07:54:40 +00:00
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
2023-03-09 13:44:31 +00:00
2023-05-11 06:56:16 +00:00
if (is_null($template)) {
$template = [
'message' => Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl')->render(),
2023-05-11 06:56:16 +00:00
];
}
2023-03-09 13:44:31 +00:00
2023-05-11 06:56:16 +00:00
$template['type'] = $type;
$template['locale'] = $locale;
$response->dynamic(new Document($template), Response::MODEL_SMS_TEMPLATE);
2023-03-09 13:44:31 +00:00
});
2026-02-04 05:30:22 +00:00
Http::get('/v1/projects/:projectId/templates/email/:type/:locale')
2023-05-11 06:56:16 +00:00
->desc('Get custom email template')
2023-03-09 13:44:31 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'templates',
2025-01-17 04:31:39 +00:00
name: 'getEmailTemplate',
description: '/docs/references/projects/get-email-template.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_EMAIL_TEMPLATE,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-03-09 13:44:31 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-03-09 13:44:31 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2024-10-08 07:54:40 +00:00
$template = $templates['email.' . $type . '-' . $locale] ?? null;
2023-03-09 13:44:31 +00:00
2023-05-11 06:56:16 +00:00
$localeObj = new Locale($locale);
2025-08-12 13:13:08 +00:00
$localeObj->setFallback(System::getEnv('_APP_LOCALE', 'en'));
2023-05-11 06:56:16 +00:00
if (is_null($template)) {
2025-07-20 09:10:30 +00:00
/**
* different templates, different placeholders.
*/
$templateConfigs = [
'magicSession' => [
'file' => 'email-magic-url.tpl',
'placeholders' => ['optionButton', 'buttonText', 'optionUrl', 'clientInfo', 'securityPhrase']
],
'mfaChallenge' => [
'file' => 'email-mfa-challenge.tpl',
'placeholders' => ['description', 'clientInfo']
],
'otpSession' => [
'file' => 'email-otp.tpl',
'placeholders' => ['description', 'clientInfo', 'securityPhrase']
],
'sessionAlert' => [
2025-07-20 11:09:08 +00:00
'file' => 'email-session-alert.tpl',
2025-07-20 09:10:30 +00:00
'placeholders' => ['body', 'listDevice', 'listIpAddress', 'listCountry', 'footer']
],
];
// fallback to the base template.
$config = $templateConfigs[$type] ?? [
'file' => 'email-inner-base.tpl',
'placeholders' => ['buttonText', 'body', 'footer']
];
$templateString = file_get_contents(__DIR__ . '/../../config/locale/templates/' . $config['file']);
// We use `fromString` due to the replace above
$message = Template::fromString($templateString);
// Set type-specific parameters
foreach ($config['placeholders'] as $param) {
$escapeHtml = !in_array($param, ['clientInfo', 'body', 'footer', 'description']);
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$type}.{$param}"), escapeHtml: $escapeHtml);
}
2023-08-28 08:03:55 +00:00
$message
2025-07-20 09:10:30 +00:00
// common placeholders on all the templates
2023-05-11 06:56:16 +00:00
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
2025-07-20 09:10:30 +00:00
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"));
// `useContent: false` will strip new lines!
$message = $message->render(useContent: true);
2023-05-17 08:25:24 +00:00
2023-05-11 06:56:16 +00:00
$template = [
'message' => $message,
'subject' => $localeObj->getText('emails.' . $type . '.subject'),
2023-08-29 09:40:30 +00:00
'senderEmail' => '',
'senderName' => ''
2023-05-11 06:56:16 +00:00
];
}
2023-03-09 13:44:31 +00:00
2023-05-11 06:56:16 +00:00
$template['type'] = $type;
$template['locale'] = $locale;
$response->dynamic(new Document($template), Response::MODEL_EMAIL_TEMPLATE);
2023-03-10 06:27:42 +00:00
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
2023-05-11 06:56:16 +00:00
->desc('Update custom SMS template')
2023-03-10 06:27:42 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSmsTemplate',
description: '/docs/references/projects/update-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
],
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.updateSMSTemplate',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'templates',
name: 'updateSMSTemplate',
description: '/docs/references/projects/update-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
]
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-05-11 06:56:16 +00:00
->param('message', '', new Text(0), 'Template message')
2023-03-10 06:27:42 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, string $message, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
2023-08-25 15:13:25 +00:00
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-03-10 06:27:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2023-05-11 06:56:16 +00:00
$templates['sms.' . $type . '-' . $locale] = [
'message' => $message
];
2023-03-10 06:27:42 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
2023-03-10 06:27:42 +00:00
2023-05-11 06:56:16 +00:00
$response->dynamic(new Document([
'message' => $message,
'type' => $type,
'locale' => $locale,
]), Response::MODEL_SMS_TEMPLATE);
2023-03-10 06:27:42 +00:00
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/templates/email/:type/:locale')
2023-05-11 06:56:16 +00:00
->desc('Update custom email templates')
2023-03-10 06:27:42 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'templates',
2025-01-17 04:31:39 +00:00
name: 'updateEmailTemplate',
description: '/docs/references/projects/update-email-template.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_EMAIL_TEMPLATE,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-05-11 06:56:16 +00:00
->param('subject', '', new Text(255), 'Email Subject')
->param('message', '', new Text(0), 'Template message')
2023-08-28 14:08:50 +00:00
->param('senderName', '', new Text(255, 0), 'Name of the email sender', true)
->param('senderEmail', '', new Email(), 'Email of the sender', true)
2023-05-11 06:56:16 +00:00
->param('replyTo', '', new Email(), 'Reply to email', true)
2023-03-10 06:27:42 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, string $subject, string $message, string $senderName, string $senderEmail, string $replyTo, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-03-10 06:27:42 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2023-05-11 06:56:16 +00:00
$templates['email.' . $type . '-' . $locale] = [
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'message' => $message
];
2023-03-10 06:27:42 +00:00
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
2023-03-10 06:27:42 +00:00
2023-05-11 06:56:16 +00:00
$response->dynamic(new Document([
'type' => $type,
'locale' => $locale,
'senderName' => $senderName,
'senderEmail' => $senderEmail,
'subject' => $subject,
'replyTo' => $replyTo,
'message' => $message
]), Response::MODEL_EMAIL_TEMPLATE);
2023-03-09 13:44:31 +00:00
});
2023-04-19 08:44:22 +00:00
2026-02-04 05:30:22 +00:00
Http::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
2023-04-19 08:44:22 +00:00
->desc('Reset custom SMS template')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', [
new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteSmsTemplate',
description: '/docs/references/projects/delete-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
],
contentType: ContentType::JSON,
deprecated: new Deprecated(
since: '1.8.0',
2025-08-23 09:36:02 +00:00
replaceWith: 'projects.deleteSMSTemplate',
),
public: false,
),
new Method(
namespace: 'projects',
group: 'templates',
name: 'deleteSMSTemplate',
description: '/docs/references/projects/delete-sms-template.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_SMS_TEMPLATE,
)
],
contentType: ContentType::JSON
)
])
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['sms'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-04-19 08:44:22 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
2023-08-25 15:13:25 +00:00
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-04-19 08:44:22 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2024-10-08 07:54:40 +00:00
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
2023-04-19 08:44:22 +00:00
if (is_null($template)) {
2023-05-11 06:52:27 +00:00
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
2023-04-19 08:44:22 +00:00
}
unset($template['sms.' . $type . '-' . $locale]);
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
2023-04-19 08:44:22 +00:00
$response->dynamic(new Document([
'type' => $type,
'locale' => $locale,
'message' => $template['message']
]), Response::MODEL_SMS_TEMPLATE);
});
2026-02-04 05:30:22 +00:00
Http::delete('/v1/projects/:projectId/templates/email/:type/:locale')
->desc('Delete custom email template')
2023-04-19 08:44:22 +00:00
->groups(['api', 'projects'])
->label('scope', 'projects.write')
2025-01-17 04:31:39 +00:00
->label('sdk', new Method(
namespace: 'projects',
2025-03-31 05:48:17 +00:00
group: 'templates',
2025-01-17 04:31:39 +00:00
name: 'deleteEmailTemplate',
description: '/docs/references/projects/delete-email-template.md',
2025-01-17 04:31:39 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_EMAIL_TEMPLATE,
)
],
contentType: ContentType::JSON
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('type', '', new WhiteList(Config::getParam('locale-templates')['email'] ?? [], true), 'Template type')
2024-06-07 00:54:51 +00:00
->param('locale', '', fn ($localeCodes) => new WhiteList($localeCodes), 'Template locale', false, ['localeCodes'])
2023-04-19 08:44:22 +00:00
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $type, string $locale, Response $response, Database $dbForPlatform) {
2024-10-08 07:54:40 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
2023-04-19 08:44:22 +00:00
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
2024-10-08 07:54:40 +00:00
$template = $templates['email.' . $type . '-' . $locale] ?? null;
2023-04-19 08:44:22 +00:00
if (is_null($template)) {
2023-05-11 06:52:27 +00:00
throw new Exception(Exception::PROJECT_TEMPLATE_DEFAULT_DELETION);
2023-04-19 08:44:22 +00:00
}
unset($templates['email.' . $type . '-' . $locale]);
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project->setAttribute('templates', $templates));
2023-04-19 08:44:22 +00:00
$response->dynamic(new Document([
'type' => $type,
'locale' => $locale,
'senderName' => $template['senderName'],
'senderEmail' => $template['senderEmail'],
'subject' => $template['subject'],
'replyTo' => $template['replyTo'],
'message' => $template['message']
]), Response::MODEL_EMAIL_TEMPLATE);
2025-01-17 04:39:16 +00:00
});
2026-02-04 05:30:22 +00:00
Http::patch('/v1/projects/:projectId/auth/session-invalidation')
2025-06-16 18:18:08 +00:00
->desc('Update invalidate session option of the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'auth',
name: 'updateSessionInvalidation',
description: '/docs/references/projects/update-session-invalidation.md',
2025-06-16 18:18:08 +00:00
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
2025-10-21 02:04:08 +00:00
->param('projectId', '', fn (Database $dbForPlatform) => new UID($dbForPlatform->getAdapter()->getMaxUIDLength()), 'Project unique ID.', false, ['dbForPlatform'])
->param('enabled', false, new Boolean(), 'Update authentication session invalidation status. Use this endpoint to enable or disable session invalidation on password change')
2025-06-16 18:18:08 +00:00
->inject('response')
->inject('dbForPlatform')
2025-07-22 02:43:32 +00:00
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForPlatform) {
2025-06-16 18:18:08 +00:00
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
2025-07-22 02:43:32 +00:00
$auths['invalidateSessions'] = $enabled;
2025-06-16 18:18:08 +00:00
$dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
2025-06-16 18:18:08 +00:00
$response->dynamic($project, Response::MODEL_PROJECT);
});