delayed project deletion

This commit is contained in:
shimon
2025-06-15 10:38:06 +03:00
parent a949a1ffd5
commit c953a4815e
6 changed files with 263 additions and 40 deletions
+218 -11
View File
@@ -19,7 +19,6 @@ use Appwrite\Utopia\Database\Validator\ProjectId;
use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Cron\CronExpression;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\App;
use Utopia\Audit\Audit;
@@ -358,9 +357,18 @@ App::get('/v1/projects')
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$projectId}' for the 'cursor' value not found.");
}
if(!empty($cursorDocument->getAttribute('_deletedAt')) && $cursorDocument->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::GENERAL_CURSOR_NOT_FOUND, "Project '{$projectId}' for the 'cursor' value not found.");
}
$cursor->setValue($cursorDocument);
}
$queries[] = query::or([
query::isNull('_deletedAt'),
query::lessThan('_deletedAt', DateTime::now())
]);
$filterQueries = Query::groupByType($queries)['filters'];
try {
$projects = $dbForPlatform->find('projects', $queries);
@@ -402,6 +410,10 @@ App::get('/v1/projects/:projectId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$response->dynamic($project, Response::MODEL_PROJECT);
});
@@ -435,10 +447,9 @@ App::patch('/v1/projects/:projectId')
->param('legalCity', '', new Text(256), 'Project legal city. Max length: 256 chars.', true)
->param('legalAddress', '', new Text(256), 'Project legal address. Max length: 256 chars.', true)
->param('legalTaxId', '', new Text(256), 'Project legal tax ID. Max length: 256 chars.', true)
->param('restore', null, new Boolean(), 'Restore project from being marked for deletion', true)
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, bool $restore, Response $response, Database $dbForPlatform) {
->action(function (string $projectId, string $name, string $description, string $logo, string $url, string $legalName, string $legalCountry, string $legalState, string $legalCity, string $legalAddress, string $legalTaxId, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -446,7 +457,12 @@ App::patch('/v1/projects/:projectId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$project
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('name', $name)
->setAttribute('description', $description)
->setAttribute('logo', $logo)
@@ -457,14 +473,42 @@ App::patch('/v1/projects/:projectId')
->setAttribute('legalCity', $legalCity)
->setAttribute('legalAddress', $legalAddress)
->setAttribute('legalTaxId', $legalTaxId)
->setAttribute('search', implode(' ', [$projectId, $name]))
;
->setAttribute('search', implode(' ', [$projectId, $name])));
if (!empty($restore)) {
$project->setAttribute('_deletedAt', null);
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/restore')
->desc('Restore project from being a candidate for deletion')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk', new Method(
namespace: 'projects',
group: 'projects',
name: 'restore',
description: '/docs/references/projects/restore.md',
auth: [AuthType::ADMIN],
responses: [
new SDKResponse(
code: Response::STATUS_CODE_OK,
model: Response::MODEL_PROJECT,
)
]
))
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$project = $dbForPlatform->updateDocument('projects', $project->getId(), $project);
$project = $dbForPlatform->updateDocument('projects', $project->getId(),
$project->setAttribute('_deletedAt', null)
);
$response->dynamic($project, Response::MODEL_PROJECT);
});
@@ -499,6 +543,10 @@ App::patch('/v1/projects/:projectId/team')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if ($team->isEmpty()) {
throw new Exception(Exception::TEAM_NOT_FOUND);
}
@@ -574,6 +622,10 @@ App::patch('/v1/projects/:projectId/service')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$services = $project->getAttribute('services', []);
$services[$service] = $status;
@@ -611,6 +663,10 @@ App::patch('/v1/projects/:projectId/service/all')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$allServices = array_keys(array_filter(Config::getParam('services'), fn ($element) => $element['optional']));
$services = [];
@@ -653,6 +709,10 @@ App::patch('/v1/projects/:projectId/api')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$apis = $project->getAttribute('apis', []);
$apis[$api] = $status;
@@ -690,6 +750,10 @@ App::patch('/v1/projects/:projectId/api/all')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$allApis = array_keys(Config::getParam('apis'));
$apis = [];
@@ -734,6 +798,10 @@ App::patch('/v1/projects/:projectId/oauth2')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$providers = $project->getAttribute('oAuthProviders', []);
if ($appId !== null) {
@@ -782,6 +850,10 @@ App::patch('/v1/projects/:projectId/auth/session-alerts')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['sessionAlerts'] = $alerts;
@@ -821,6 +893,10 @@ App::patch('/v1/projects/:projectId/auth/memberships-privacy')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['membershipsUserName'] = $userName;
@@ -862,6 +938,10 @@ App::patch('/v1/projects/:projectId/auth/limit')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['limit'] = $limit;
@@ -900,6 +980,10 @@ App::patch('/v1/projects/:projectId/auth/duration')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['duration'] = $duration;
@@ -942,6 +1026,10 @@ App::patch('/v1/projects/:projectId/auth/:method')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths[$authKey] = $status;
@@ -979,6 +1067,10 @@ App::patch('/v1/projects/:projectId/auth/password-history')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['passwordHistory'] = $limit;
@@ -1017,6 +1109,10 @@ App::patch('/v1/projects/:projectId/auth/password-dictionary')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['passwordDictionary'] = $enabled;
@@ -1055,6 +1151,10 @@ App::patch('/v1/projects/:projectId/auth/personal-data')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['personalDataCheck'] = $enabled;
@@ -1093,6 +1193,10 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['maxSessions'] = $limit;
@@ -1139,6 +1243,10 @@ App::patch('/v1/projects/:projectId/auth/mock-numbers')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['mockNumbers'] = $numbers;
@@ -1170,16 +1278,19 @@ App::delete('/v1/projects/:projectId')
))
->param('projectId', '', new UID(), 'Project unique ID.')
->inject('response')
->inject('user')
->inject('dbForPlatform')
->inject('queueForDeletes')
->action(function (string $projectId, Response $response, Document $user, Database $dbForPlatform, Delete $queueForDeletes) {
->action(function (string $projectId, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$expiration = System::getEnv('_APP_PROJECTS_DELETE_EXPIRATION','3600');
$deletedAt = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), (int)$expiration));
$dbForPlatform->updateDocument('projects', $project->getId(),
@@ -1226,6 +1337,10 @@ App::post('/v1/projects/:projectId/webhooks')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN);
$webhook = new Document([
@@ -1284,6 +1399,10 @@ App::get('/v1/projects/:projectId/webhooks')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$webhooks = $dbForPlatform->find('webhooks', [
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
@@ -1324,6 +1443,10 @@ App::get('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$webhook = $dbForPlatform->findOne('webhooks', [
Query::equal('$id', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1372,6 +1495,10 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
$webhook = $dbForPlatform->findOne('webhooks', [
@@ -1431,6 +1558,10 @@ App::patch('/v1/projects/:projectId/webhooks/:webhookId/signature')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$webhook = $dbForPlatform->findOne('webhooks', [
Query::equal('$id', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1478,6 +1609,10 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$webhook = $dbForPlatform->findOne('webhooks', [
Query::equal('$id', [$webhookId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1527,6 +1662,10 @@ App::post('/v1/projects/:projectId/keys')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$key = new Document([
'$id' => ID::unique(),
'$permissions' => [
@@ -1581,6 +1720,10 @@ App::get('/v1/projects/:projectId/keys')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$keys = $dbForPlatform->find('keys', [
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::limit(5000),
@@ -1621,6 +1764,10 @@ App::get('/v1/projects/:projectId/keys/:keyId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$key = $dbForPlatform->findOne('keys', [
Query::equal('$id', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1665,6 +1812,10 @@ App::put('/v1/projects/:projectId/keys/:keyId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$key = $dbForPlatform->findOne('keys', [
Query::equal('$id', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1716,6 +1867,10 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$key = $dbForPlatform->findOne('keys', [
Query::equal('$id', [$keyId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1764,6 +1919,10 @@ App::post('/v1/projects/:projectId/jwts')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
@@ -1810,6 +1969,10 @@ App::post('/v1/projects/:projectId/platforms')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$platform = new Document([
'$id' => ID::unique(),
'$permissions' => [
@@ -1903,6 +2066,10 @@ App::get('/v1/projects/:projectId/platforms/:platformId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$platform = $dbForPlatform->findOne('platforms', [
Query::equal('$id', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -1947,6 +2114,10 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$platform = $dbForPlatform->findOne('platforms', [
Query::equal('$id', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -2001,6 +2172,10 @@ App::delete('/v1/projects/:projectId/platforms/:platformId')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$platform = $dbForPlatform->findOne('platforms', [
Query::equal('$id', [$platformId]),
Query::equal('projectInternalId', [$project->getInternalId()]),
@@ -2056,6 +2231,10 @@ App::patch('/v1/projects/:projectId/smtp')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
// Ensure required params for when enabling SMTP
if ($enabled) {
if (empty($senderName)) {
@@ -2154,6 +2333,10 @@ App::post('/v1/projects/:projectId/smtp/tests')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$replyToEmail = !empty($replyTo) ? $replyTo : $senderEmail;
$subject = 'Custom SMTP email sample';
@@ -2223,6 +2406,10 @@ App::get('/v1/projects/:projectId/templates/sms/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
@@ -2269,6 +2456,10 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$template = $templates['email.' . $type . '-' . $locale] ?? null;
@@ -2331,6 +2522,10 @@ App::patch('/v1/projects/:projectId/templates/sms/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$templates['sms.' . $type . '-' . $locale] = [
'message' => $message
@@ -2380,6 +2575,10 @@ App::patch('/v1/projects/:projectId/templates/email/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$templates['email.' . $type . '-' . $locale] = [
'senderName' => $senderName,
@@ -2435,6 +2634,10 @@ App::delete('/v1/projects/:projectId/templates/sms/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$template = $templates['sms.' . $type . '-' . $locale] ?? null;
@@ -2484,6 +2687,10 @@ App::delete('/v1/projects/:projectId/templates/email/:type/:locale')
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$templates = $project->getAttribute('templates', []);
$template = $templates['email.' . $type . '-' . $locale] ?? null;
+5
View File
@@ -31,6 +31,7 @@ use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Database\Adapter\Pool as DatabasePool;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\DateTime as DatabaseDateTime;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -294,6 +295,10 @@ App::setResource('project', function ($dbForPlatform, $request, $console) {
$project = Authorization::skip(fn () => $dbForPlatform->getDocument('projects', $projectId));
if(!empty($project->getAttribute('_deletedAt')) && $project->getAttribute('_deletedAt') < DateTime::now()) {
$project = new Document([]);
}
return $project;
}, ['dbForPlatform', 'request', 'console']);
-28
View File
@@ -932,34 +932,6 @@ services:
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-projects:
entrypoint: schedule-projects
<<: *x-logging
container_name: appwrite-task-scheduler-projects
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-assistant:
container_name: appwrite-assistant
image: appwrite/assistant:0.7.0
+1
View File
@@ -0,0 +1 @@
Restore project from being a candidate for deletion.
+21 -1
View File
@@ -9,6 +9,7 @@ use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Platform\Action;
use Utopia\System\System;
@@ -87,7 +88,26 @@ class Maintenance extends Action
$this->renewCertificates($dbForPlatform, $queueForCertificates);
$this->notifyDeleteCache($cacheRetention, $queueForDeletes);
$this->notifyDeleteSchedules($schedulesDeletionRetention, $queueForDeletes);
}, $interval, $delay);
Console::warning("Deleting projects that there delete date smaller then ".DateTime::now());
$dbForPlatform->foreach(
'projects',
function (Document $project) use ($dbForPlatform, $queueForDeletes) {
$queueForDeletes
->setProject($project)
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($project)
->trigger();
Authorization::skip(fn () => $dbForPlatform->deleteDocument('projects', $project->getId()));
}, [
Query::lessThan('_deletedAt', DateTime::now()),
Query::equal('region', [System::getEnv('_APP_REGION', 'default')]),
]
);
}, $interval, 0);
}
private function notifyDeleteConnections(Delete $queueForDeletes): void
@@ -3876,7 +3876,25 @@ class ProjectsConsoleClientTest extends Scope
], $this->getHeaders()));
$this->assertEquals(404, $project['headers']['status-code']);
var_dump(['before restore' => $response['headers']['status-code']]);
// restore from deletion
$response = $this->client->call(Client::METHOD_PATCH, '/projects/' . $projectId . '/restore', array_merge([
'origin' => 'http://localhost',
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $response['headers']['status-code']);
var_dump(['restore' => $response['headers']['status-code']]);
$project = $this->client->call(Client::METHOD_GET, '/projects/' . $projectId, array_merge([
'content-type' => 'application/json',
'x-appwrite-project' => $this->getProject()['$id'],
], $this->getHeaders()));
$this->assertEquals(200, $project['headers']['status-code']);
var_dump(['after restore' => $response['headers']['status-code']]);
return $data;
}