Merge remote-tracking branch 'origin/1.8.x' into documents-db-api

This commit is contained in:
ArnabChatterjee20k
2026-02-20 13:58:56 +05:30
129 changed files with 936 additions and 836459 deletions
+96
View File
@@ -0,0 +1,96 @@
<?php
namespace Appwrite\Filter;
use Utopia\Validator\Text;
class BranchDomain implements Filter
{
/**
* Maximum length for branch prefix in domain name
*/
public const BRANCH_PREFIX_MAX_LENGTH = 16;
/**
* Length of hash suffix when branch name exceeds max length
*/
public const HASH_SUFFIX_LENGTH = 7;
/**
* Pre-process branch name to a valid domain name.
*
* Input should be an array with:
* - 'branch' (string): The branch name
* - 'resourceId' (string): The resource ID (site or function)
* - 'projectId' (string): The project ID
* - 'sitesDomain' (string): The base sites domain
*/
public function apply(mixed $input): mixed
{
$branch = $input['branch'] ?? '';
$resourceId = $input['resourceId'] ?? '';
$projectId = $input['projectId'] ?? '';
$sitesDomain = $input['sitesDomain'] ?? '';
$branchPrefix = $this->generateBranchPrefix($branch);
$resourceProjectHash = substr(hash('sha256', $resourceId . $projectId), 0, self::HASH_SUFFIX_LENGTH);
$domain = \strtolower("branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}");
return $domain;
}
/**
* Generate a branch prefix for domain name from a branch name.
* Takes up to 16 characters, sanitizes them for domain use,
* and appends a hash suffix if the branch name is longer than 16 characters.
*
* @param string $branch The branch name
* @return string The branch prefix for domain name
*/
private function generateBranchPrefix(string $branch): string
{
$branchPrefix = substr($branch, 0, self::BRANCH_PREFIX_MAX_LENGTH);
$branchPrefix = $this->sanitizeBranchName($branchPrefix);
if (strlen($branch) > self::BRANCH_PREFIX_MAX_LENGTH) {
$remainingChars = substr($branch, self::BRANCH_PREFIX_MAX_LENGTH);
$branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, self::HASH_SUFFIX_LENGTH);
}
return $branchPrefix;
}
/**
* Sanitize a branch name for use in a domain name.
* Replaces any characters that are not alphanumeric or hyphens with hyphens,
* and removes leading/trailing hyphens.
*
* @param string $branch The branch name to sanitize
* @return string The sanitized branch name
*/
private function sanitizeBranchName(string $branch): string
{
$allowedChars = array_merge(
Text::NUMBERS,
Text::ALPHABET_UPPER,
Text::ALPHABET_LOWER,
['-']
);
$allowedCharsFlip = array_flip($allowedChars);
$sanitized = '';
for ($i = 0; $i < \strlen($branch); $i++) {
$char = $branch[$i];
if (isset($allowedCharsFlip[$char])) {
$sanitized .= $char;
} else {
// Prevents two -- or more in a row
if (strlen($sanitized) > 0 && $sanitized[strlen($sanitized) - 1] !== '-') {
$sanitized .= '-';
}
}
}
return trim($sanitized, '-');
}
}
+8
View File
@@ -0,0 +1,8 @@
<?php
namespace Appwrite\Filter;
interface Filter
{
public function apply(mixed $input): mixed;
}
+9 -24
View File
@@ -4,8 +4,10 @@ namespace Appwrite\Platform\Modules\Compute;
use Appwrite\Event\Build;
use Appwrite\Extend\Exception;
use Appwrite\Filter\BranchDomain as BranchDomainFilter;
use Appwrite\Platform\Action;
use Appwrite\Platform\Modules\Compute\Validator\Specification as SpecificationValidator;
use Appwrite\Platform\Permission as AppwritePermission;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
@@ -22,23 +24,7 @@ use Utopia\VCS\Exception\RepositoryNotFound;
class Base extends Action
{
/**
* Permissions for resources in this project.
*
* @param string $teamId
* @param string $projectId
* @return string[]
*/
protected function getPermissions(string $teamId, string $projectId): array
{
return [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
];
}
use AppwritePermission;
/**
* Get default specification based on plan and available specifications.
@@ -326,13 +312,12 @@ class Base extends Action
// VCS branch preview
if (!empty($providerBranch)) {
$branchPrefix = substr($providerBranch, 0, 16);
if (strlen($providerBranch) > 16) {
$remainingChars = substr($providerBranch, 16);
$branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7);
}
$resourceProjectHash = substr(hash('sha256', $site->getId() . $project->getId()), 0, 7);
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$domain = (new BranchDomainFilter())->apply([
'branch' => $providerBranch,
'resourceId' => $site->getId(),
'projectId' => $project->getId(),
'sitesDomain' => $sitesDomain,
]);
$ruleId = md5($domain);
try {
$authorization->skip(
@@ -137,6 +137,7 @@ class Get extends Action
}
$response->noContent();
return;
}
// Only occurs if type is added into whitelist, but not supported in action
@@ -9,6 +9,7 @@ use Appwrite\Event\Realtime;
use Appwrite\Event\Screenshot;
use Appwrite\Event\StatsUsage;
use Appwrite\Event\Webhook;
use Appwrite\Filter\BranchDomain as BranchDomainFilter;
use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment;
use Exception;
@@ -1013,16 +1014,6 @@ class Builds extends Action
Console::log('Deployment activated');
}
/** Screenshot site */
if ($resource->getCollection() === 'sites') {
$queueForScreenshots
->setDeploymentId($deployment->getId())
->setProject($project)
->trigger();
Console::log('Site screenshot queued');
}
$this->afterDeploymentSuccess(
$project,
$deployment,
@@ -1037,14 +1028,12 @@ class Builds extends Action
// VCS branch
$branchName = $deployment->getAttribute('providerBranch');
if (!empty($branchName)) {
$sitesDomain = $platform['sitesDomain'];
$branchPrefix = substr($branchName, 0, 16);
if (strlen($branchName) > 16) {
$remainingChars = substr($branchName, 16);
$branchPrefix .= '-' . substr(hash('sha256', $remainingChars), 0, 7);
}
$resourceProjectHash = substr(hash('sha256', $resource->getId() . $project->getId()), 0, 7);
$domain = "branch-{$branchPrefix}-{$resourceProjectHash}.{$sitesDomain}";
$domain = (new BranchDomainFilter())->apply([
'branch' => $branchName,
'resourceId' => $resource->getId(),
'projectId' => $project->getId(),
'sitesDomain' => $platform['sitesDomain'],
]);
$ruleId = md5($domain);
try {
@@ -1123,6 +1112,16 @@ class Builds extends Action
$dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule);
}
/** Screenshot site */
if ($resource->getCollection() === 'sites') {
$queueForScreenshots
->setDeploymentId($deployment->getId())
->setProject($project)
->trigger();
Console::log('Site screenshot queued');
}
Console::info('Deployment action finished');
} catch (\Throwable $th) {
Console::warning('Build failed:');
@@ -111,7 +111,12 @@ class Screenshots extends Action
throw new \Exception('Bucket not found');
}
$routerHost = System::getEnv('_APP_WORKER_SCREENSHOTS_ROUTER', 'http://appwrite');
$routerHost = System::getEnv('_APP_WORKER_SCREENSHOTS_ROUTER', '');
if (empty($routerHost)) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$routerHost = "$protocol://{$rule->getAttribute('domain')}";
}
$configs = [
'screenshotLight' => [
'headers' => [ 'x-appwrite-hostname' => $rule->getAttribute('domain') ],
@@ -3,20 +3,9 @@
namespace Appwrite\Platform\Modules\Projects\Http\Projects;
use Appwrite\Platform\Action as AppwriteAction;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Appwrite\Platform\Permission as AppwritePermission;
class Action extends AppwriteAction
{
protected function getPermissions(string $teamId, string $projectId): array
{
return [
Permission::read(Role::team(ID::custom($teamId))),
Permission::update(Role::team(ID::custom($teamId), 'owner')),
Permission::update(Role::team(ID::custom($teamId), 'developer')),
Permission::delete(Role::team(ID::custom($teamId), 'owner')),
Permission::delete(Role::team(ID::custom($teamId), 'developer')),
];
}
use AppwritePermission;
}
@@ -75,7 +75,7 @@ class Create extends Action
$this->validateDomainRestrictions($domain, $platform);
// TODO: (@Meldiron) Remove after 1.7.x migration
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique();
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5(\strtolower($domain)) : ID::unique();
$status = RULE_STATUS_CREATED;
$owner = '';
@@ -87,7 +87,7 @@ class Create extends Action
$deployment = $dbForProject->getDocument('deployments', $function->getAttribute('deploymentId', ''));
// TODO: (@Meldiron) Remove after 1.7.x migration
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique();
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5(\strtolower($domain)) : ID::unique();
$status = RULE_STATUS_CREATED;
$owner = '';
@@ -92,7 +92,7 @@ class Create extends Action
}
// TODO: (@Meldiron) Remove after 1.7.x migration
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique();
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5(\strtolower($domain)) : ID::unique();
$status = RULE_STATUS_CREATED;
$owner = '';
@@ -87,7 +87,7 @@ class Create extends Action
$deployment = $dbForProject->getDocument('deployments', $site->getAttribute('deploymentId', ''));
// TODO: (@Meldiron) Remove after 1.7.x migration
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique();
$ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5(\strtolower($domain)) : ID::unique();
$status = RULE_STATUS_CREATED;
$owner = '';
+37
View File
@@ -0,0 +1,37 @@
<?php
namespace Appwrite\Platform;
use Utopia\Database\Helpers\ID;
use Utopia\Database\Helpers\Permission as DbPermission;
use Utopia\Database\Helpers\Role;
trait Permission
{
/**
* Permissions for projects & project resources.
*
* @param string $teamId
* @param string $projectId
* @return array
*/
public function getPermissions(string $teamId, string $projectId): array
{
return [
// Team-wide permissions
DbPermission::read(Role::team(ID::custom($teamId), 'owner')),
DbPermission::read(Role::team(ID::custom($teamId), 'developer')),
DbPermission::update(Role::team(ID::custom($teamId), 'owner')),
DbPermission::update(Role::team(ID::custom($teamId), 'developer')),
DbPermission::delete(Role::team(ID::custom($teamId), 'owner')),
DbPermission::delete(Role::team(ID::custom($teamId), 'developer')),
// Project-wide permissions
DbPermission::read(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::read(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
DbPermission::update(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::update(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
DbPermission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-owner")),
DbPermission::delete(Role::team(ID::custom($teamId), "project-{$projectId}-developer")),
];
}
}
@@ -39,7 +39,7 @@ class User extends Document
{
$roles = [];
if (!$this->isPrivileged($authorization->getRoles()) && !$this->isApp($authorization->getRoles())) {
if (!$this->isApp($authorization->getRoles())) {
if ($this->getId()) {
$roles[] = Role::user($this->getId())->toString();
$roles[] = Role::users()->toString();