integrate abuse and audit
This commit is contained in:
parent
f1d75dc805
commit
12e01f8092
|
@ -21,7 +21,6 @@ use Utopia\Audit\Audit;
|
|||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Ahc\Jwt\JWT;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
@ -830,22 +829,19 @@ App::get('/v1/account/logs')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($response, $register, $project, $user, $locale, $geodb) {
|
||||
->inject('dbForInternal')
|
||||
->action(function ($response, $user, $locale, $geodb, $dbForInternal) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
|
||||
$adapter = new AuditAdapter($register->get('db'));
|
||||
$adapter->setNamespace('app_'.$project->getId());
|
||||
|
||||
$audit = new Audit($adapter);
|
||||
$audit = new Audit($dbForInternal);
|
||||
$countries = $locale->getText('countries');
|
||||
|
||||
$logs = $audit->getLogsByUserAndActions($user->getId(), [
|
||||
|
|
|
@ -1,25 +1,25 @@
|
|||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
use Appwrite\Network\Validator\CNAME;
|
||||
use Appwrite\Network\Validator\Domain as DomainValidator;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Cron\CronExpression;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Domains\Domain;
|
||||
use Utopia\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Integer;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
App::post('/v1/projects')
|
||||
->desc('Create Project')
|
||||
|
@ -60,8 +60,8 @@ App::post('/v1/projects')
|
|||
|
||||
$project = $dbForConsole->createDocument('projects', new Document([
|
||||
'$collection' => 'projects',
|
||||
'$read' => ['team:'.$teamId],
|
||||
'$write' => ['team:'.$teamId.'/owner', 'team:'.$teamId.'/developer'],
|
||||
'$read' => ['team:' . $teamId],
|
||||
'$write' => ['team:' . $teamId . '/owner', 'team:' . $teamId . '/developer'],
|
||||
'name' => $name,
|
||||
'description' => $description,
|
||||
'logo' => $logo,
|
||||
|
@ -87,9 +87,9 @@ App::post('/v1/projects')
|
|||
|
||||
$collections = Config::getParam('collections2', []); /** @var array $collections */
|
||||
|
||||
$dbForInternal->setNamespace('project_'.$project->getId().'_internal');
|
||||
$dbForInternal->setNamespace('project_' . $project->getId() . '_internal');
|
||||
$dbForInternal->create();
|
||||
$dbForExternal->setNamespace('project_'.$project->getId().'_external');
|
||||
$dbForExternal->setNamespace('project_' . $project->getId() . '_external');
|
||||
$dbForExternal->create();
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
|
@ -206,7 +206,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
throw new Exception('Project not found', 404);
|
||||
}
|
||||
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
|
||||
$period = [
|
||||
'24h' => [
|
||||
|
@ -230,44 +230,44 @@ App::get('/v1/projects/:projectId/usage')
|
|||
'group' => '1d',
|
||||
],
|
||||
];
|
||||
|
||||
|
||||
$client = $register->get('influxdb');
|
||||
|
||||
|
||||
$requests = [];
|
||||
$network = [];
|
||||
$functions = [];
|
||||
|
||||
|
||||
if ($client) {
|
||||
$start = $period[$range]['start']->format(DateTime::RFC3339);
|
||||
$end = $period[$range]['end']->format(DateTime::RFC3339);
|
||||
$database = $client->selectDB('telegraf');
|
||||
|
||||
|
||||
// Requests
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_requests_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
|
||||
foreach ($points as $point) {
|
||||
$requests[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Network
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_network_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
|
||||
foreach ($points as $point) {
|
||||
$network[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
'date' => \strtotime($point['time']),
|
||||
];
|
||||
}
|
||||
|
||||
|
||||
// Functions
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \''.$start.'\' AND time < \''.$end.'\' AND "metric_type"=\'counter\' AND "project"=\''.$project->getId().'\' GROUP BY time('.$period[$range]['group'].') FILL(null)');
|
||||
$result = $database->query('SELECT sum(value) AS "value" FROM "appwrite_usage_executions_all" WHERE time > \'' . $start . '\' AND time < \'' . $end . '\' AND "metric_type"=\'counter\' AND "project"=\'' . $project->getId() . '\' GROUP BY time(' . $period[$range]['group'] . ') FILL(null)');
|
||||
$points = $result->getPoints();
|
||||
|
||||
|
||||
foreach ($points as $point) {
|
||||
$functions[] = [
|
||||
'value' => (!empty($point['value'])) ? $point['value'] : 0,
|
||||
|
@ -281,14 +281,13 @@ App::get('/v1/projects/:projectId/usage')
|
|||
$functions = [];
|
||||
}
|
||||
|
||||
|
||||
// Users
|
||||
|
||||
$projectDB->getCollection([
|
||||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'filters' => [
|
||||
'$collection=users'
|
||||
'$collection=users',
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -313,7 +312,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
'limit' => 0,
|
||||
'offset' => 0,
|
||||
'filters' => [
|
||||
'$collection='.$collection['$id'],
|
||||
'$collection=' . $collection['$id'],
|
||||
],
|
||||
]);
|
||||
|
||||
|
@ -369,7 +368,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
'$collection=files',
|
||||
],
|
||||
]
|
||||
) +
|
||||
) +
|
||||
$projectDB->getCount(
|
||||
[
|
||||
'attribute' => 'size',
|
||||
|
@ -416,16 +415,16 @@ App::patch('/v1/projects/:projectId')
|
|||
}
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('description', $description)
|
||||
->setAttribute('logo', $logo)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('legalName', $legalName)
|
||||
->setAttribute('legalCountry', $legalCountry)
|
||||
->setAttribute('legalState', $legalState)
|
||||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
);
|
||||
|
||||
$response->dynamic2($project, Response::MODEL_PROJECT);
|
||||
|
@ -458,8 +457,8 @@ App::patch('/v1/projects/:projectId/oauth2')
|
|||
}
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('usersOauth2'.\ucfirst($provider).'Appid', $appId)
|
||||
->setAttribute('usersOauth2'.\ucfirst($provider).'Secret', $secret)
|
||||
->setAttribute('usersOauth2' . \ucfirst($provider) . 'Appid', $appId)
|
||||
->setAttribute('usersOauth2' . \ucfirst($provider) . 'Secret', $secret)
|
||||
);
|
||||
|
||||
$response->dynamic2($project, Response::MODEL_PROJECT);
|
||||
|
@ -490,7 +489,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
|||
}
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('usersAuthLimit', $limit)
|
||||
->setAttribute('usersAuthLimit', $limit)
|
||||
);
|
||||
|
||||
$response->dynamic2($project, Response::MODEL_PROJECT);
|
||||
|
@ -507,7 +506,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: '.implode(',', \array_keys(Config::getParam('auth'))), false)
|
||||
->param('method', '', new WhiteList(\array_keys(Config::getParam('auth')), true), 'Auth Method. Possible values: ' . implode(',', \array_keys(Config::getParam('auth'))), false)
|
||||
->param('status', false, new Boolean(true), 'Set the status of this auth method.')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
|
@ -525,7 +524,7 @@ App::patch('/v1/projects/:projectId/auth/:method')
|
|||
}
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute($authKey, $status)
|
||||
->setAttribute($authKey, $status)
|
||||
);
|
||||
|
||||
$response->dynamic2($project, Response::MODEL_PROJECT);
|
||||
|
@ -566,7 +565,7 @@ App::delete('/v1/projects/:projectId')
|
|||
->setParam('type', DELETE_TYPE_DOCUMENT)
|
||||
->setParam('document', $project)
|
||||
;
|
||||
|
||||
|
||||
if (!$dbForConsole->deleteDocument('teams', $project->getAttribute('teamId', null))) {
|
||||
throw new Exception('Failed to remove project team from DB', 500);
|
||||
}
|
||||
|
@ -622,7 +621,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
]);
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('webhooks', $webhook, Document::SET_TYPE_APPEND)
|
||||
);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
|
@ -732,16 +731,16 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
}
|
||||
|
||||
$project->findAndReplace('$id', $webhook->getId(), $webhook
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('events', $events)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('security', $security)
|
||||
->setAttribute('httpUser', $httpUser)
|
||||
->setAttribute('httpPass', $httpPass)
|
||||
, 'webhooks');
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('events', $events)
|
||||
->setAttribute('url', $url)
|
||||
->setAttribute('security', $security)
|
||||
->setAttribute('httpUser', $httpUser)
|
||||
->setAttribute('httpPass', $httpPass)
|
||||
, 'webhooks');
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
|
||||
$response->dynamic2($webhook, Response::MODEL_WEBHOOK);
|
||||
});
|
||||
|
||||
|
@ -812,7 +811,7 @@ App::post('/v1/projects/:projectId/keys')
|
|||
]);
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('keys', $key, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('keys', $key, Document::SET_TYPE_APPEND)
|
||||
);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
|
@ -835,7 +834,7 @@ App::get('/v1/projects/:projectId/keys')
|
|||
->action(function ($projectId, $response, $dbForConsole) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
|
@ -913,12 +912,12 @@ App::put('/v1/projects/:projectId/keys/:keyId')
|
|||
}
|
||||
|
||||
$project->findAndReplace('$id', $key->getId(), $key
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('scopes', $scopes)
|
||||
, 'keys');
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('scopes', $scopes)
|
||||
, 'keys');
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
|
||||
$response->dynamic2($key, Response::MODEL_KEY);
|
||||
});
|
||||
|
||||
|
@ -1011,7 +1010,7 @@ App::post('/v1/projects/:projectId/tasks')
|
|||
]);
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('tasks', $task, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('tasks', $task, Document::SET_TYPE_APPEND)
|
||||
);
|
||||
|
||||
if ($next) {
|
||||
|
@ -1131,18 +1130,18 @@ App::put('/v1/projects/:projectId/tasks/:taskId')
|
|||
$security = ($security === '1' || $security === 'true' || $security === 1 || $security === true);
|
||||
|
||||
$project->findAndReplace('$id', $task->getId(), $task
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('status', $status)
|
||||
->setAttribute('schedule', $schedule)
|
||||
->setAttribute('updated', \time())
|
||||
->setAttribute('next', $next)
|
||||
->setAttribute('security', $security)
|
||||
->setAttribute('httpMethod', $httpMethod)
|
||||
->setAttribute('httpUrl', $httpUrl)
|
||||
->setAttribute('httpHeaders', $httpHeaders)
|
||||
->setAttribute('httpUser', $httpUser)
|
||||
->setAttribute('httpPass', $httpPass)
|
||||
, 'tasks');
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('status', $status)
|
||||
->setAttribute('schedule', $schedule)
|
||||
->setAttribute('updated', \time())
|
||||
->setAttribute('next', $next)
|
||||
->setAttribute('security', $security)
|
||||
->setAttribute('httpMethod', $httpMethod)
|
||||
->setAttribute('httpUrl', $httpUrl)
|
||||
->setAttribute('httpHeaders', $httpHeaders)
|
||||
->setAttribute('httpUser', $httpUser)
|
||||
->setAttribute('httpPass', $httpPass)
|
||||
, 'tasks');
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
|
@ -1227,13 +1226,13 @@ App::post('/v1/projects/:projectId/platforms')
|
|||
]);
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('platforms', $platform, Document::SET_TYPE_APPEND)
|
||||
);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic2($platform, Response::MODEL_PLATFORM);
|
||||
});
|
||||
|
||||
|
||||
App::get('/v1/projects/:projectId/platforms')
|
||||
->desc('List Platforms')
|
||||
->groups(['api', 'projects'])
|
||||
|
@ -1341,12 +1340,12 @@ App::put('/v1/projects/:projectId/platforms/:platformId')
|
|||
;
|
||||
|
||||
$project->findAndReplace('$id', $platform->getId(), $platform
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('dateUpdated', \time())
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('store', $store)
|
||||
->setAttribute('hostname', $hostname)
|
||||
, 'platforms');
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('dateUpdated', \time())
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('store', $store)
|
||||
->setAttribute('hostname', $hostname)
|
||||
, 'platforms');
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
|
@ -1420,7 +1419,7 @@ App::post('/v1/projects/:projectId/domains')
|
|||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
if (!$target->isKnown() || $target->isTest()) {
|
||||
throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.', 500);
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500);
|
||||
}
|
||||
|
||||
$domain = new Domain($domain);
|
||||
|
@ -1436,7 +1435,7 @@ App::post('/v1/projects/:projectId/domains')
|
|||
]);
|
||||
|
||||
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('domains', $domain, Document::SET_TYPE_APPEND)
|
||||
->setAttribute('domains', $domain, Document::SET_TYPE_APPEND)
|
||||
);
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
|
@ -1467,7 +1466,7 @@ App::get('/v1/projects/:projectId/domains')
|
|||
}
|
||||
|
||||
$domains = $project->getAttribute('domains', []);
|
||||
|
||||
|
||||
$response->dynamic2(new Document([
|
||||
'domains' => $domains,
|
||||
'sum' => count($domains),
|
||||
|
@ -1540,7 +1539,7 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
|||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
if (!$target->isKnown() || $target->isTest()) {
|
||||
throw new Exception('Unreachable CNAME target ('.$target->get().'), please use a domain with a public suffix.', 500);
|
||||
throw new Exception('Unreachable CNAME target (' . $target->get() . '), please use a domain with a public suffix.', 500);
|
||||
}
|
||||
|
||||
if ($domain->getAttribute('verification') === true) {
|
||||
|
@ -1554,8 +1553,8 @@ App::patch('/v1/projects/:projectId/domains/:domainId/verification')
|
|||
}
|
||||
|
||||
$project->findAndReplace('$id', $domain->getId(), $domain
|
||||
->setAttribute('verification', true)
|
||||
, 'domains');
|
||||
->setAttribute('verification', true)
|
||||
, 'domains');
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
|
||||
|
@ -1606,4 +1605,4 @@ App::delete('/v1/projects/:projectId/domains/:domainId')
|
|||
;
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
});
|
||||
|
|
|
@ -13,7 +13,6 @@ use Utopia\Validator\Text;
|
|||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Validator\UID;
|
||||
|
@ -212,12 +211,10 @@ App::get('/v1/users/:userId/logs')
|
|||
->label('sdk.response.model', Response::MODEL_LOG_LIST)
|
||||
->param('userId', '', new UID(), 'User unique ID.')
|
||||
->inject('response')
|
||||
->inject('register')
|
||||
->inject('project')
|
||||
->inject('dbForInternal')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($userId, $response, $register, $project, $dbForInternal, $locale, $geodb) {
|
||||
->action(function ($userId, $response, $dbForInternal, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
/** @var Appwrite\Database\Document $project */
|
||||
|
@ -231,10 +228,7 @@ App::get('/v1/users/:userId/logs')
|
|||
throw new Exception('User not found', 404);
|
||||
}
|
||||
|
||||
$adapter = new AuditAdapter($register->get('db'));
|
||||
$adapter->setNamespace('app_'.$project->getId());
|
||||
|
||||
$audit = new Audit($adapter);
|
||||
$audit = new Audit($dbForInternal);
|
||||
|
||||
$countries = $locale->getText('countries');
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ use Utopia\Abuse\Adapters\TimeLimit;
|
|||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes) {
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $register, $events, $audits, $usage, $deletes, $dbForInternal) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
|
@ -21,6 +21,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
|
|||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Appwrite\Event\Event $deletes */
|
||||
/** @var Appwrite\Event\Event $functions */
|
||||
/** @var Utopia\Database\Database $dbForInternal */
|
||||
|
||||
Storage::setDevice('files', new Local(APP_STORAGE_UPLOADS.'/app-'.$project->getId()));
|
||||
Storage::setDevice('functions', new Local(APP_STORAGE_FUNCTIONS.'/app-'.$project->getId()));
|
||||
|
@ -31,47 +32,44 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
|
|||
throw new Exception('Missing or unknown project ID', 400);
|
||||
}
|
||||
|
||||
// /*
|
||||
// * Abuse Check
|
||||
// */
|
||||
// $timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), function () use ($register) {
|
||||
// return $register->get('db');
|
||||
// });
|
||||
// $timeLimit->setNamespace('app_'.$project->getId());
|
||||
// $timeLimit
|
||||
// ->setParam('{userId}', $user->getId())
|
||||
// ->setParam('{userAgent}', $request->getUserAgent(''))
|
||||
// ->setParam('{ip}', $request->getIP())
|
||||
// ->setParam('{url}', $request->getHostname().$route->getURL())
|
||||
// ;
|
||||
/*
|
||||
* Abuse Check
|
||||
*/
|
||||
$timeLimit = new TimeLimit($route->getLabel('abuse-key', 'url:{url},ip:{ip}'), $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForInternal);
|
||||
$timeLimit
|
||||
->setParam('{userId}', $user->getId())
|
||||
->setParam('{userAgent}', $request->getUserAgent(''))
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getHostname().$route->getURL())
|
||||
;
|
||||
|
||||
// //TODO make sure we get array here
|
||||
//TODO make sure we get array here
|
||||
|
||||
// foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
// if(!empty($value)) {
|
||||
// $timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
// }
|
||||
// }
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
if(!empty($value)) {
|
||||
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
}
|
||||
}
|
||||
|
||||
// $abuse = new Abuse($timeLimit);
|
||||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
// if ($timeLimit->limit()) {
|
||||
// $response
|
||||
// ->addHeader('X-RateLimit-Limit', $timeLimit->limit())
|
||||
// ->addHeader('X-RateLimit-Remaining', $timeLimit->remaining())
|
||||
// ->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600))
|
||||
// ;
|
||||
// }
|
||||
if ($timeLimit->limit()) {
|
||||
$response
|
||||
->addHeader('X-RateLimit-Limit', $timeLimit->limit())
|
||||
->addHeader('X-RateLimit-Remaining', $timeLimit->remaining())
|
||||
->addHeader('X-RateLimit-Reset', $timeLimit->time() + $route->getLabel('abuse-time', 3600))
|
||||
;
|
||||
}
|
||||
|
||||
// $isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
// $isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::$roles);
|
||||
$isAppUser = Auth::isAppUser(Authorization::$roles);
|
||||
|
||||
// if (($abuse->check() // Route is rate-limited
|
||||
// && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled
|
||||
// && (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||
// {
|
||||
// throw new Exception('Too many requests', 429);
|
||||
// }
|
||||
if (($abuse->check() // Route is rate-limited
|
||||
&& App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled') // Abuse is not diabled
|
||||
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||
{
|
||||
throw new Exception('Too many requests', 429);
|
||||
}
|
||||
|
||||
/*
|
||||
* Background Jobs
|
||||
|
@ -111,7 +109,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $register, $e
|
|||
->setParam('projectId', $project->getId())
|
||||
;
|
||||
|
||||
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes'], 'api');
|
||||
}, ['utopia', 'request', 'response', 'project', 'user', 'register', 'events', 'audits', 'usage', 'deletes', 'dbForInternal'], 'api');
|
||||
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user) {
|
||||
|
|
|
@ -2,7 +2,11 @@
|
|||
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Cache\Adapter\Redis;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
use Utopia\Database\Database;
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
|
||||
|
@ -31,10 +35,11 @@ class AuditsV1
|
|||
$data = $this->args['data'];
|
||||
$db = $register->get('db', true);
|
||||
|
||||
$adapter = new AuditAdapter($db);
|
||||
$adapter->setNamespace('app_'.$projectId);
|
||||
$cache = new Cache(new Redis($register->get('cache')));
|
||||
$dbForInternal = new Database(new MariaDB($db), $cache);
|
||||
$dbForInternal->setNamespace('project_'.$projectId.'_internal');
|
||||
|
||||
$audit = new Audit($adapter);
|
||||
$audit = new Audit($dbForInternal);
|
||||
|
||||
$audit->log($userId, $event, $resource, $userAgent, $ip, '', $data);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Database\Database;
|
||||
use Utopia\Database\Database as Database2;
|
||||
use Utopia\Cache\Adapter\Redis as RedisCache;
|
||||
use Appwrite\Database\Adapter\MySQL as MySQLAdapter;
|
||||
use Appwrite\Database\Adapter\Redis as RedisAdapter;
|
||||
use Appwrite\Database\Document;
|
||||
|
@ -12,6 +14,8 @@ use Utopia\CLI\Console;
|
|||
use Utopia\Config\Config;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Audit\Adapters\MySQL as AuditAdapter;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Database\Adapter\MariaDB;
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
|
||||
|
@ -167,12 +171,9 @@ class DeletesV1
|
|||
throw new Exception('Failed to delete audit logs. No timestamp provided');
|
||||
}
|
||||
|
||||
$timeLimit = new TimeLimit("", 0, 1, function () use ($register) {
|
||||
return $register->get('db');
|
||||
});
|
||||
|
||||
$this->deleteForProjectIds(function($projectId) use ($timeLimit, $timestamp){
|
||||
$timeLimit->setNamespace('app_'.$projectId);
|
||||
|
||||
$this->deleteForProjectIds(function($projectId) use ($timestamp){
|
||||
$timeLimit = new TimeLimit("", 0, 1, $this->getInternalDB($projectId));
|
||||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
$status = $abuse->cleanup($timestamp);
|
||||
|
@ -189,9 +190,7 @@ class DeletesV1
|
|||
throw new Exception('Failed to delete audit logs. No timestamp provided');
|
||||
}
|
||||
$this->deleteForProjectIds(function($projectId) use ($register, $timestamp){
|
||||
$adapter = new AuditAdapter($register->get('db'));
|
||||
$adapter->setNamespace('app_'.$projectId);
|
||||
$audit = new Audit($adapter);
|
||||
$audit = new Audit($this->getInternalDB($projectId));
|
||||
$status = $audit->cleanup($timestamp);
|
||||
if (!$status) {
|
||||
throw new Exception('Failed to delete Audit logs for project'.$projectId);
|
||||
|
@ -375,4 +374,18 @@ class DeletesV1
|
|||
|
||||
return $projectDB;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Database2
|
||||
*/
|
||||
protected function getInternalDB($projectId): Database2
|
||||
{
|
||||
global $register;
|
||||
|
||||
$cache = new Cache(new RedisCache($register->get('cache')));
|
||||
$dbForInternal = new Database2(new MariaDB($register->get('db')), $cache);
|
||||
$dbForInternal->setNamespace('project_'.$projectId.'_internal'); // Main DB
|
||||
|
||||
return $dbForInternal;
|
||||
}
|
||||
}
|
|
@ -39,9 +39,9 @@
|
|||
"appwrite/php-runtimes": "0.2.*",
|
||||
|
||||
"utopia-php/framework": "0.14.*",
|
||||
"utopia-php/abuse": "0.4.*",
|
||||
"utopia-php/abuse": "dev-feat-utopia-db-integration",
|
||||
"utopia-php/analytics": "0.2.*",
|
||||
"utopia-php/audit": "0.5.*",
|
||||
"utopia-php/audit": "dev-feat-utopia-db-integration",
|
||||
"utopia-php/cache": "0.4.*",
|
||||
"utopia-php/cli": "0.11.*",
|
||||
"utopia-php/config": "0.2.*",
|
||||
|
@ -62,6 +62,16 @@
|
|||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.0.2"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/lohanidamodar/audit"
|
||||
},
|
||||
{
|
||||
"type": "git",
|
||||
"url": "https://github.com/lohanidamodar/abuse"
|
||||
}
|
||||
],
|
||||
"require-dev": {
|
||||
"appwrite/sdk-generator": "0.10.6",
|
||||
"swoole/ide-helper": "4.6.6",
|
||||
|
|
143
composer.lock
generated
143
composer.lock
generated
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "5cf39daf305902a168233757d4a00066",
|
||||
"content-hash": "3eb7dc73e9524209a38bd6c33a540732",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
@ -1603,21 +1603,22 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/abuse",
|
||||
"version": "0.4.0",
|
||||
"version": "dev-feat-utopia-db-integration",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/abuse.git",
|
||||
"reference": "2b8cc40a67c045c137b44d1a11326f494acf50a4"
|
||||
"url": "https://github.com/lohanidamodar/abuse.git",
|
||||
"reference": "525cdf6674e11a7229cba31941915ac50a3e575a"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/abuse/zipball/2b8cc40a67c045c137b44d1a11326f494acf50a4",
|
||||
"reference": "2b8cc40a67c045c137b44d1a11326f494acf50a4",
|
||||
"url": "https://api.github.com/repos/lohanidamodar/abuse/zipball/525cdf6674e11a7229cba31941915ac50a3e575a",
|
||||
"reference": "525cdf6674e11a7229cba31941915ac50a3e575a",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=7.4"
|
||||
"php": ">=7.4",
|
||||
"utopia-php/database": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.4",
|
||||
|
@ -1629,7 +1630,6 @@
|
|||
"Utopia\\Abuse\\": "src/Abuse"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -1641,17 +1641,16 @@
|
|||
],
|
||||
"description": "A simple abuse library to manage application usage limits",
|
||||
"keywords": [
|
||||
"Abuse",
|
||||
"abuse",
|
||||
"framework",
|
||||
"php",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/abuse/issues",
|
||||
"source": "https://github.com/utopia-php/abuse/tree/0.4.0"
|
||||
"source": "https://github.com/lohanidamodar/abuse/tree/feat-utopia-db-integration"
|
||||
},
|
||||
"time": "2021-03-17T20:21:24+00:00"
|
||||
"time": "2021-06-04T08:40:29+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/analytics",
|
||||
|
@ -1710,21 +1709,22 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/audit",
|
||||
"version": "0.5.1",
|
||||
"version": "dev-feat-utopia-db-integration",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/audit.git",
|
||||
"reference": "154a850170a58667a15e4b65fbabb6cd0b709dd9"
|
||||
"url": "https://github.com/lohanidamodar/audit.git",
|
||||
"reference": "3421b6a54740fbb29570004848cdc0b818717e83"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/audit/zipball/154a850170a58667a15e4b65fbabb6cd0b709dd9",
|
||||
"reference": "154a850170a58667a15e4b65fbabb6cd0b709dd9",
|
||||
"url": "https://api.github.com/repos/lohanidamodar/audit/zipball/3421b6a54740fbb29570004848cdc0b818717e83",
|
||||
"reference": "3421b6a54740fbb29570004848cdc0b818717e83",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"ext-pdo": "*",
|
||||
"php": ">=7.1"
|
||||
"php": ">=7.4",
|
||||
"utopia-php/database": "0.2.*"
|
||||
},
|
||||
"require-dev": {
|
||||
"phpunit/phpunit": "^9.3",
|
||||
|
@ -1736,7 +1736,6 @@
|
|||
"Utopia\\Audit\\": "src/Audit"
|
||||
}
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
|
@ -1748,17 +1747,16 @@
|
|||
],
|
||||
"description": "A simple audit library to manage application users logs",
|
||||
"keywords": [
|
||||
"Audit",
|
||||
"audit",
|
||||
"framework",
|
||||
"php",
|
||||
"upf",
|
||||
"utopia"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/audit/issues",
|
||||
"source": "https://github.com/utopia-php/audit/tree/0.5.1"
|
||||
"source": "https://github.com/lohanidamodar/audit/tree/feat-utopia-db-integration"
|
||||
},
|
||||
"time": "2020-12-21T17:28:53+00:00"
|
||||
"time": "2021-06-04T08:41:19+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/cache",
|
||||
|
@ -5252,20 +5250,21 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v5.2.8",
|
||||
"version": "v5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "864568fdc0208b3eba3638b6000b69d2386e6768"
|
||||
"reference": "058553870f7809087fa80fa734704a21b9bcaeb2"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/864568fdc0208b3eba3638b6000b69d2386e6768",
|
||||
"reference": "864568fdc0208b3eba3638b6000b69d2386e6768",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/058553870f7809087fa80fa734704a21b9bcaeb2",
|
||||
"reference": "058553870f7809087fa80fa734704a21b9bcaeb2",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.2.5",
|
||||
"symfony/deprecation-contracts": "^2.1",
|
||||
"symfony/polyfill-mbstring": "~1.0",
|
||||
"symfony/polyfill-php73": "^1.8",
|
||||
"symfony/polyfill-php80": "^1.15",
|
||||
|
@ -5329,7 +5328,7 @@
|
|||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v5.2.8"
|
||||
"source": "https://github.com/symfony/console/tree/v5.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5345,7 +5344,74 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-11T15:45:21+00:00"
|
||||
"time": "2021-05-26T17:43:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/deprecation-contracts",
|
||||
"version": "v2.4.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/deprecation-contracts.git",
|
||||
"reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/5f38c8804a9e97d23e0c8d63341088cd8a22d627",
|
||||
"reference": "5f38c8804a9e97d23e0c8d63341088cd8a22d627",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": ">=7.1"
|
||||
},
|
||||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-main": "2.4-dev"
|
||||
},
|
||||
"thanks": {
|
||||
"name": "symfony/contracts",
|
||||
"url": "https://github.com/symfony/contracts"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"function.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
"name": "Nicolas Grekas",
|
||||
"email": "p@tchwork.com"
|
||||
},
|
||||
{
|
||||
"name": "Symfony Community",
|
||||
"homepage": "https://symfony.com/contributors"
|
||||
}
|
||||
],
|
||||
"description": "A generic function and convention to trigger deprecation notices",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/deprecation-contracts/tree/v2.4.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://symfony.com/sponsor",
|
||||
"type": "custom"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/fabpot",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-03-23T23:28:01+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/polyfill-intl-grapheme",
|
||||
|
@ -5752,16 +5818,16 @@
|
|||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
"version": "v5.2.8",
|
||||
"version": "v5.3.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/string.git",
|
||||
"reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db"
|
||||
"reference": "a9a0f8b6aafc5d2d1c116dcccd1573a95153515b"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db",
|
||||
"reference": "01b35eb64cac8467c3f94cd0ce2d0d376bb7d1db",
|
||||
"url": "https://api.github.com/repos/symfony/string/zipball/a9a0f8b6aafc5d2d1c116dcccd1573a95153515b",
|
||||
"reference": "a9a0f8b6aafc5d2d1c116dcccd1573a95153515b",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -5815,7 +5881,7 @@
|
|||
"utf8"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/string/tree/v5.2.8"
|
||||
"source": "https://github.com/symfony/string/tree/v5.3.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
@ -5831,7 +5897,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2021-05-10T14:56:10+00:00"
|
||||
"time": "2021-05-26T17:43:10+00:00"
|
||||
},
|
||||
{
|
||||
"name": "theseer/tokenizer",
|
||||
|
@ -6120,7 +6186,10 @@
|
|||
],
|
||||
"aliases": [],
|
||||
"minimum-stability": "stable",
|
||||
"stability-flags": [],
|
||||
"stability-flags": {
|
||||
"utopia-php/abuse": 20,
|
||||
"utopia-php/audit": 20
|
||||
},
|
||||
"prefer-stable": false,
|
||||
"prefer-lowest": false,
|
||||
"platform": {
|
||||
|
@ -6143,4 +6212,4 @@
|
|||
"php": "8.0"
|
||||
},
|
||||
"plugin-api-version": "2.0.0"
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue