mirror of
https://github.com/appwrite/appwrite.git
synced 2026-05-26 13:51:13 +00:00
Merge branch '0.16.x' of https://github.com/appwrite/appwrite into matej-qa-3
This commit is contained in:
@@ -2672,7 +2672,7 @@ $collections = [
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('time'),
|
||||
'$id' => ID::custom('duration'),
|
||||
'type' => Database::VAR_FLOAT,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
@@ -2731,9 +2731,9 @@ $collections = [
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_time'),
|
||||
'$id' => ID::custom('_key_duration'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['time'],
|
||||
'attributes' => ['duration'],
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
|
||||
@@ -360,7 +360,7 @@ App::get('/v1/avatars/initials')
|
||||
->action(function (string $name, int $width, int $height, string $background, Response $response, Document $user) {
|
||||
|
||||
$themes = [
|
||||
['background' => '#F2F2F8'], // Default
|
||||
['background' => '#FFA1CE'], // Default (Pink)
|
||||
['background' => '#FDC584'], // Orange
|
||||
['background' => '#94DBD1'], // Green
|
||||
['background' => '#A1C4FF'], // Blue
|
||||
|
||||
@@ -1008,7 +1008,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
'statusCode' => 0,
|
||||
'response' => '',
|
||||
'stderr' => '',
|
||||
'time' => 0.0,
|
||||
'duration' => 0.0,
|
||||
'search' => implode(' ', [$functionId, $executionId]),
|
||||
])));
|
||||
|
||||
@@ -1095,11 +1095,11 @@ App::post('/v1/functions/:functionId/executions')
|
||||
$execution->setAttribute('response', $executionResponse['response']);
|
||||
$execution->setAttribute('stdout', $executionResponse['stdout']);
|
||||
$execution->setAttribute('stderr', $executionResponse['stderr']);
|
||||
$execution->setAttribute('time', $executionResponse['time']);
|
||||
$execution->setAttribute('duration', $executionResponse['duration']);
|
||||
} catch (\Throwable $th) {
|
||||
$interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt()));
|
||||
$execution
|
||||
->setAttribute('time', (float)$interval->format('%s.%f'))
|
||||
->setAttribute('duration', (float)$interval->format('%s.%f'))
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('statusCode', $th->getCode())
|
||||
->setAttribute('stderr', $th->getMessage());
|
||||
@@ -1113,7 +1113,7 @@ App::post('/v1/functions/:functionId/executions')
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executions.{scope}.compute', 1)
|
||||
->setParam('executionStatus', $execution->getAttribute('status', ''))
|
||||
->setParam('executionTime', $execution->getAttribute('time')); // ms
|
||||
->setParam('executionTime', $execution->getAttribute('duration')); // ms
|
||||
|
||||
$roles = Authorization::getRoles();
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
|
||||
|
||||
+2
-2
@@ -503,7 +503,7 @@ App::post('/v1/execution')
|
||||
|
||||
$ch = \curl_init();
|
||||
$body = \json_encode([
|
||||
'env' => $vars,
|
||||
'variables' => $vars,
|
||||
'payload' => $data,
|
||||
'timeout' => $timeout
|
||||
]);
|
||||
@@ -574,7 +574,7 @@ App::post('/v1/execution')
|
||||
'response' => \mb_strcut($res, 0, 1000000), // Limit to 1MB
|
||||
'stdout' => \mb_strcut($stdout, 0, 1000000), // Limit to 1MB
|
||||
'stderr' => \mb_strcut($stderr, 0, 1000000), // Limit to 1MB
|
||||
'time' => $executionTime,
|
||||
'duration' => $executionTime,
|
||||
];
|
||||
|
||||
/** Update swoole table */
|
||||
|
||||
@@ -108,7 +108,7 @@ $cli
|
||||
$time = DateTime::now();
|
||||
|
||||
$certificates = $dbForConsole->find('certificates', [
|
||||
Query::lessThanEqual('attempts', 5), // Maximum 5 attempts
|
||||
Query::lessThan('attempts', 5), // Maximum 5 attempts
|
||||
Query::lessThanEqual('renewDate', $time), // includes 60 days cooldown (we have 30 days to renew)
|
||||
Query::limit(200), // Limit 200 comes from LetsEncrypt (300 orders per 3 hours, keeping some for new domains)
|
||||
]);
|
||||
|
||||
@@ -46,8 +46,20 @@ $escapedPermissions = \array_map(function ($perm) {
|
||||
<?php endif; ?>
|
||||
:value="rawPermissions"/>
|
||||
|
||||
<table class="u-table-layout-normal" data-ls-attrs="x-init=load({{<?php if (!empty($data)) echo $data . '.$permissions' ?>}})">
|
||||
<thead>
|
||||
<datalist id="types">
|
||||
<option value="user:">
|
||||
<option value="team:">
|
||||
<option value="member:">
|
||||
<option value="users">
|
||||
<option value="guests">
|
||||
<option value="any">
|
||||
</datalist>
|
||||
|
||||
<table
|
||||
class="u-table-layout-normal"
|
||||
data-ls-attrs="x-init=load({{<?php if (!empty($data)) echo $data . '.$permissions' ?>}})">
|
||||
|
||||
<thead x-show="permissions.length > 0">
|
||||
<tr>
|
||||
<th>Role</th>
|
||||
<?php foreach ($permissions as $permission): ?>
|
||||
@@ -60,7 +72,16 @@ $escapedPermissions = \array_map(function ($perm) {
|
||||
<template x-for="(permission, index) in permissions">
|
||||
<tr>
|
||||
<td>
|
||||
<p x-text="permission.role"></p>
|
||||
<input
|
||||
required
|
||||
autocomplete="off"
|
||||
:id="'<?php echo $form; ?>Input' + index"
|
||||
name="<?php echo $form; ?>"
|
||||
form="<?php echo $form ?>"
|
||||
list="types"
|
||||
type="text"
|
||||
x-model="permission.role"
|
||||
@keyup="updatePermission(index)"/>
|
||||
</td>
|
||||
<?php foreach ($escapedPermissions as $permission): ?>
|
||||
<td>
|
||||
@@ -78,38 +99,11 @@ $escapedPermissions = \array_map(function ($perm) {
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<tr x-data="permissionsRow"
|
||||
@addrow<?php echo \strtolower($form); ?>.window="addPermission('<?php echo $form; ?>', role, { <?php echo \implode(',', $escapedPermissions) ?> })">
|
||||
<td>
|
||||
<datalist id="types">
|
||||
<option value="user:">
|
||||
<option value="team:">
|
||||
<option value="users">
|
||||
<option value="guests">
|
||||
<option value="any">
|
||||
</datalist>
|
||||
|
||||
<input
|
||||
required
|
||||
id="<?php echo $form; ?>Input"
|
||||
name="<?php echo $form; ?>"
|
||||
form="<?php echo $form ?>"
|
||||
list="types"
|
||||
type="text"
|
||||
x-model="role" />
|
||||
</td>
|
||||
<?php foreach ($escapedPermissions as $permission): ?>
|
||||
<td>
|
||||
<input type="checkbox" name="<?php echo $permission ?>" x-model="<?php echo $permission; ?>"/>
|
||||
</td>
|
||||
<?php endforeach; ?>
|
||||
<td></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="<?php \count($permissions) + 2 ?>">
|
||||
<button type="button" class="margin-top-small reverse" @click="$dispatch('addrow<?php echo \strtolower($form); ?>')">Add</button>
|
||||
<button type="button" class="margin-top-small reverse" @click="addPermission('<?php echo $form ?>')">Add Role</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
|
||||
@@ -508,7 +508,7 @@ $permissions = $this->getParam('permissions', null);
|
||||
<li data-state="/console/databases/collection/settings?id={{router.params.id}}&databaseId={{router.params.databaseId}}&project={{router.params.project}}">
|
||||
<h2>Settings</h2>
|
||||
|
||||
<div class="row responsive margin-top-negative">
|
||||
<div class="row responsive">
|
||||
<div class="col span-8 margin-bottom">
|
||||
<form id="<?php echo $permissions->getParam('form') ?>"></form>
|
||||
|
||||
@@ -607,7 +607,7 @@ $permissions = $this->getParam('permissions', null);
|
||||
data-success="alert,trigger,redirect"
|
||||
data-success-param-alert-text="Collection deleted successfully"
|
||||
data-success-param-trigger-events="databases.deleteCollection"
|
||||
data-success-param-redirect-url="/console/databases?project={{router.params.project}}"
|
||||
data-success-param-redirect-url="/console/databases?project={{router.params.project}}&databaseId={{router.params.databaseId}}"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to delete collection"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
@@ -416,7 +416,7 @@ sort($patterns);
|
||||
<span data-ls-bind="{{execution.trigger}}"></span>
|
||||
</td>
|
||||
<td data-title="Time: ">
|
||||
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.time|seconds2hum}}"></span>
|
||||
<span data-ls-if="{{execution.status}} === 'completed' || {{execution.status}} === 'failed'" data-ls-bind="{{execution.duration|seconds2hum}}"></span>
|
||||
<span data-ls-if="{{execution.status}} === 'waiting' || {{execution.status}} === 'processing'">-</span>
|
||||
</td>
|
||||
<td data-title="">
|
||||
|
||||
@@ -8,6 +8,7 @@ use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\DateTime;
|
||||
use Utopia\Database\ID;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Domains\Domain;
|
||||
|
||||
@@ -100,8 +101,11 @@ class CertificatesV1 extends Worker
|
||||
throw new Exception('Renew isn\'t required.');
|
||||
}
|
||||
|
||||
// Prepare folder name for certbot. Using this helps prevent miss-match in LetsEncrypt configuration when renewing certificate
|
||||
$folder = ID::unique();
|
||||
|
||||
// Generate certificate files using Let's Encrypt
|
||||
$letsEncryptData = $this->issueCertificate($domain->get(), $email);
|
||||
$letsEncryptData = $this->issueCertificate($folder, $domain->get(), $email);
|
||||
|
||||
// Command succeeded, store all data into document
|
||||
// We store stderr too, because it may include warnings
|
||||
@@ -111,7 +115,7 @@ class CertificatesV1 extends Worker
|
||||
]));
|
||||
|
||||
// Give certificates to Traefik
|
||||
$this->applyCertificateFiles($domain->get(), $letsEncryptData);
|
||||
$this->applyCertificateFiles($folder, $domain->get(), $letsEncryptData);
|
||||
|
||||
// Update certificate info stored in database
|
||||
$certificate->setAttribute('renewDate', $this->getRenewDate($domain->get()));
|
||||
@@ -125,6 +129,9 @@ class CertificatesV1 extends Worker
|
||||
$attempts = $certificate->getAttribute('attempts', 0) + 1;
|
||||
$certificate->setAttribute('attempts', $attempts);
|
||||
|
||||
// Store cuttent time as renew date to ensure another attempt in next maintenance cycle
|
||||
$certificate->setAttribute('renewDate', DateTime::now());
|
||||
|
||||
// Send email to security email
|
||||
$this->notifyError($domain->get(), $e->getMessage(), $attempts);
|
||||
} finally {
|
||||
@@ -259,11 +266,12 @@ class CertificatesV1 extends Worker
|
||||
/**
|
||||
* LetsEncrypt communication to issue certificate (using certbot CLI)
|
||||
*
|
||||
* @param string $folder Folder into which certificates should be generated
|
||||
* @param string $domain Domain to generate certificate for
|
||||
*
|
||||
* @return array Named array with keys 'stdout' and 'stderr', both string
|
||||
*/
|
||||
private function issueCertificate(string $domain, string $email): array
|
||||
private function issueCertificate(string $folder, string $domain, string $email): array
|
||||
{
|
||||
$stdout = '';
|
||||
$stderr = '';
|
||||
@@ -271,6 +279,7 @@ class CertificatesV1 extends Worker
|
||||
$staging = (App::isProduction()) ? '' : ' --dry-run';
|
||||
$exit = Console::execute("certbot certonly --webroot --noninteractive --agree-tos{$staging}"
|
||||
. " --email " . $email
|
||||
. " --cert-name " . $folder
|
||||
. " -w " . APP_STORAGE_CERTIFICATES
|
||||
. " -d {$domain}", '', $stdout, $stderr);
|
||||
|
||||
@@ -290,9 +299,9 @@ class CertificatesV1 extends Worker
|
||||
*
|
||||
* @param string $domain Domain which certificate was generated for
|
||||
*
|
||||
* @return int
|
||||
* @return string
|
||||
*/
|
||||
private function getRenewDate(string $domain): int
|
||||
private function getRenewDate(string $domain): string
|
||||
{
|
||||
$certPath = APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem';
|
||||
$certData = openssl_x509_parse(file_get_contents($certPath));
|
||||
@@ -305,11 +314,12 @@ class CertificatesV1 extends Worker
|
||||
* Method to take files from Let's Encrypt, and put it into Traefik.
|
||||
*
|
||||
* @param string $domain Domain which certificate was generated for
|
||||
* @param string $folder Folder in which certificates were generated
|
||||
* @param array $letsEncryptData Let's Encrypt logs to use for additional info when throwing error
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function applyCertificateFiles(string $domain, array $letsEncryptData): void
|
||||
private function applyCertificateFiles(string $folder, string $domain, array $letsEncryptData): void
|
||||
{
|
||||
// Prepare folder in storage for domain
|
||||
$path = APP_STORAGE_CERTIFICATES . '/' . $domain;
|
||||
@@ -319,20 +329,20 @@ class CertificatesV1 extends Worker
|
||||
}
|
||||
}
|
||||
|
||||
// Move generated files from certbot into our storage
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $domain . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) {
|
||||
// Move generated files
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/cert.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/cert.pem')) {
|
||||
throw new Exception('Failed to rename certificate cert.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
|
||||
}
|
||||
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $domain . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) {
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/chain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/chain.pem')) {
|
||||
throw new Exception('Failed to rename certificate chain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
|
||||
}
|
||||
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $domain . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) {
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/fullchain.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/fullchain.pem')) {
|
||||
throw new Exception('Failed to rename certificate fullchain.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
|
||||
}
|
||||
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $domain . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) {
|
||||
if (!@\rename('/etc/letsencrypt/live/' . $folder . '/privkey.pem', APP_STORAGE_CERTIFICATES . '/' . $domain . '/privkey.pem')) {
|
||||
throw new Exception('Failed to rename certificate privkey.pem. Let\'s Encrypt log: ' . $letsEncryptData['stderr'] . ' ; ' . $letsEncryptData['stdout']);
|
||||
}
|
||||
|
||||
|
||||
@@ -248,7 +248,7 @@ class FunctionsV1 extends Worker
|
||||
'statusCode' => 0,
|
||||
'response' => '',
|
||||
'stderr' => '',
|
||||
'time' => 0.0,
|
||||
'duration' => 0.0,
|
||||
'search' => implode(' ', [$functionId, $executionId]),
|
||||
]));
|
||||
|
||||
@@ -301,11 +301,11 @@ class FunctionsV1 extends Worker
|
||||
->setAttribute('response', $executionResponse['response'])
|
||||
->setAttribute('stdout', $executionResponse['stdout'])
|
||||
->setAttribute('stderr', $executionResponse['stderr'])
|
||||
->setAttribute('time', $executionResponse['time']);
|
||||
->setAttribute('duration', $executionResponse['duration']);
|
||||
} catch (\Throwable $th) {
|
||||
$interval = (new \DateTime())->diff(new \DateTime($execution->getCreatedAt()));
|
||||
$execution
|
||||
->setAttribute('time', (float)$interval->format('%s.%f'))
|
||||
->setAttribute('duration', (float)$interval->format('%s.%f'))
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('statusCode', $th->getCode())
|
||||
->setAttribute('stderr', $th->getMessage());
|
||||
@@ -367,7 +367,7 @@ class FunctionsV1 extends Worker
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('executions.{scope}.compute', 1)
|
||||
->setParam('executionStatus', $execution->getAttribute('status', ''))
|
||||
->setParam('executionTime', $execution->getAttribute('time'))
|
||||
->setParam('executionTime', $execution->getAttribute('duration'))
|
||||
->setParam('networkRequestSize', 0)
|
||||
->setParam('networkResponseSize', 0)
|
||||
->submit();
|
||||
|
||||
Reference in New Issue
Block a user