diff --git a/.github/workflows/specs.yml b/.github/workflows/specs.yml new file mode 100644 index 0000000000..6f377354d5 --- /dev/null +++ b/.github/workflows/specs.yml @@ -0,0 +1,113 @@ +name: "Generate Specs" + +env: + COMPOSE_FILE: docker-compose.yml + +on: + workflow_dispatch: + inputs: + version: + type: choice + description: "Appwrite version to generate specs for" + required: true + options: + - "1.8.x" + - "1.7.x" + - "1.6.x" + - "1.5.x" + - "latest" + push: + type: boolean + description: "Push specs to appwrite/specs repo and create PR" + default: true + message: + type: string + description: "Commit message for the specs PR" + default: "chore: update API specs and SDK examples" + +jobs: + generate: + name: Generate Specs + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + submodules: recursive + + - name: Build and start Appwrite + run: | + docker compose build + docker compose up -d + + - name: Generate specs + run: | + docker compose exec appwrite specs --version=${{ inputs.version }} --mode=normal --git=no + + - name: Generate SDK examples + if: inputs.push + run: | + docker compose exec appwrite sdks --platform=* --sdk=* --version=${{ inputs.version }} --git=no --mode=examples + sudo chown -R $USER:$USER ./docs/examples/ + + - name: Push to appwrite/specs and create PR + if: inputs.push + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + VERSION="${{ inputs.version }}" + MESSAGE="${{ inputs.message }}" + GIT_BRANCH="feat-${VERSION}-specs" + SPECS_DIR="./app/config/specs" + EXAMPLES_DIR="./docs/examples/${VERSION}" + TARGET="/tmp/specs-repo" + + sudo chown -R $USER:$USER "${SPECS_DIR}" + + # Clone the specs repo + git clone "https://x-access-token:${GH_TOKEN}@github.com/appwrite/specs.git" "${TARGET}" + cd "${TARGET}" + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + # Create or checkout the feature branch + git checkout -b "${GIT_BRANCH}" || git checkout "${GIT_BRANCH}" + + # Copy spec files + mkdir -p "specs/${VERSION}" + cp ${GITHUB_WORKSPACE}/${SPECS_DIR}/*${VERSION}*.json "specs/${VERSION}/" 2>/dev/null || true + + # Copy latest specs if version is latest + if [ "${VERSION}" = "latest" ]; then + cp ${GITHUB_WORKSPACE}/${SPECS_DIR}/*latest*.json "specs/latest/" 2>/dev/null || true + fi + + # Copy SDK examples + if [ -d "${GITHUB_WORKSPACE}/docs/examples/${VERSION}" ]; then + mkdir -p "examples/${VERSION}" + cp -r "${GITHUB_WORKSPACE}/docs/examples/${VERSION}/." "examples/${VERSION}/" + fi + + # Commit and push + git add -A + git diff --cached --quiet && echo "No changes to commit" && exit 0 + git commit -m "${MESSAGE}" + git push -u origin "${GIT_BRANCH}" --force + + # Create or update PR + EXISTING_PR=$(gh pr list --repo appwrite/specs --head "${GIT_BRANCH}" --json number --jq '.[0].number' 2>/dev/null || true) + if [ -n "${EXISTING_PR}" ]; then + echo "PR #${EXISTING_PR} already exists, updated branch" + echo "https://github.com/appwrite/specs/pull/${EXISTING_PR}" + else + gh pr create \ + --repo "appwrite/specs" \ + --title "feat: API specs update for version ${VERSION}" \ + --body "This PR contains API specification updates and SDK examples for version ${VERSION}." \ + --base "main" \ + --head "${GIT_BRANCH}" + fi + + - name: Stop containers + if: always() + run: docker compose down diff --git a/app/config/sdks.php b/app/config/sdks.php index a3088b247b..1bf2725785 100644 --- a/app/config/sdks.php +++ b/app/config/sdks.php @@ -11,7 +11,7 @@ return [ [ 'key' => 'web', 'name' => 'Web', - 'version' => '22.1.0', + 'version' => '22.3.1', 'url' => 'https://github.com/appwrite/sdk-for-web', 'package' => 'https://www.npmjs.com/package/appwrite', 'enabled' => true, @@ -140,7 +140,7 @@ return [ [ 'key' => 'react-native', 'name' => 'React Native', - 'version' => '0.21.0', + 'version' => '0.23.1', 'url' => 'https://github.com/appwrite/sdk-for-react-native', 'package' => 'https://npmjs.com/package/react-native-appwrite', 'enabled' => true, diff --git a/app/controllers/api/migrations.php b/app/controllers/api/migrations.php index 9d5e013cdc..95339feec2 100644 --- a/app/controllers/api/migrations.php +++ b/app/controllers/api/migrations.php @@ -11,6 +11,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CompoundUID; use Appwrite\Utopia\Database\Validator\Queries\Migrations; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Order as OrderException; @@ -29,9 +32,6 @@ use Utopia\Migration\Sources\Firebase; use Utopia\Migration\Sources\NHost; use Utopia\Migration\Sources\Supabase; use Utopia\Migration\Transfer; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/app/controllers/general.php b/app/controllers/general.php index 2d3bde69d6..7fdbeb1855 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -34,6 +34,7 @@ use Appwrite\Utopia\View; use Executor\Executor; use MaxMind\Db\Reader; use Swoole\Http\Request as SwooleRequest; +use Swoole\Table; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -1073,21 +1074,20 @@ Http::init() ->inject('queueForCertificates') ->inject('platform') ->inject('authorization') - ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization) { + ->inject('certifiedDomains') + ->action(function (Request $request, Document $console, Database $dbForPlatform, Certificate $queueForCertificates, array $platform, Authorization $authorization, Table $certifiedDomains) { $hostname = $request->getHostname(); - $cache = Config::getParam('hostnames', []); $platformHostnames = $platform['hostnames'] ?? []; // 1. Cache hit - if (array_key_exists($hostname, $cache)) { + if ($certifiedDomains->exists(md5($hostname))) { return; } // 2. Domain validation $domain = new Domain(!empty($hostname) ? $hostname : ''); if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) { - $cache[$domain->get()] = false; - Config::setParam('hostnames', $cache); + $certifiedDomains->set(md5($domain->get()), ['value' => 0]); return; } @@ -1101,7 +1101,7 @@ Http::init() } // 4. Check/create rule (requires DB access) - $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, &$cache) { + $authorization->skip(function () use ($dbForPlatform, $domain, $console, $queueForCertificates, $certifiedDomains) { try { // TODO: (@Meldiron) Remove after 1.7.x migration $isMd5 = System::getEnv('_APP_RULES_FORMAT') === 'md5'; @@ -1164,8 +1164,7 @@ Http::init() } catch (Duplicate $e) { Console::info('Certificate already exists'); } finally { - $cache[$domain->get()] = true; - Config::setParam('hostnames', $cache); + $certifiedDomains->set(md5($domain->get()), ['value' => 1]); } }); }); diff --git a/app/http.php b/app/http.php index 1d7949da86..d771796a57 100644 --- a/app/http.php +++ b/app/http.php @@ -37,11 +37,19 @@ use Utopia\System\System; const DOMAIN_SYNC_TIMER = 30; // 30 seconds -$files = null; +$files = new Files(); +$files->load(__DIR__ . '/../public'); -$domains = new Table(1_000_000); // 1 million rows -$domains->column('value', Table::TYPE_INT, 1); -$domains->create(); +$riskyDomains = new Table(100_000); +$riskyDomains->column('value', Table::TYPE_INT, 1); +$riskyDomains->create(); + +$certifiedDomains = new Table(100_000); +$certifiedDomains->column('value', Table::TYPE_INT, 1); +$certifiedDomains->create(); + +Http::setResource('riskyDomains', fn () => $riskyDomains); +Http::setResource('certifiedDomains', fn () => $certifiedDomains); $http = new Server( host: "0.0.0.0", @@ -70,7 +78,7 @@ $totalWorkers = intval(System::getEnv('_APP_CPU_NUM', swoole_cpu_num())) * intva function dispatch(Server $server, int $fd, int $type, $data = null): int { $resolveWorkerId = function (Server $server, $data = null) { - global $totalWorkers, $domains; + global $totalWorkers, $riskyDomains; // If data is not set we can send request to any worker // first we try to pick idle worker, if not we randomly pick a worker @@ -102,7 +110,7 @@ function dispatch(Server $server, int $fd, int $type, $data = null): int $risky = false; if (str_starts_with($request, 'POST') && str_contains($request, '/executions')) { $risky = true; - } elseif ($domains->get(md5($domain), 'value') === 1) { + } elseif ($riskyDomains->get(md5($domain), 'value') === 1) { // executions request coming from custom domain $risky = true; } else { @@ -164,11 +172,7 @@ $http Constant::OPTION_TASK_WORKER_NUM => 1, // required for the task to fetch domains background ]); -$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) use (&$files) { - if (!$server->taskworker) { - $files = new Files(); - $files->load(__DIR__ . '/../public'); - } +$http->on(Constant::EVENT_WORKER_START, function ($server, $workerId) { }); $http->on(Constant::EVENT_WORKER_STOP, function ($server, $workerId) { @@ -452,7 +456,7 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $tot }); }); -$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, &$files) { +$http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, SwooleResponse $swooleResponse) use ($register, $files) { Span::init('http.request'); Http::setResource('swooleRequest', fn () => $swooleRequest); @@ -463,7 +467,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool Span::add('http.method', $request->getMethod()); - if ($files instanceof Files && $files->isFileLoaded($request->getURI())) { + if ($files->isFileLoaded($request->getURI())) { $time = (60 * 60 * 24 * 45); // 45 days cache $response @@ -582,7 +586,7 @@ $http->on(Constant::EVENT_REQUEST, function (SwooleRequest $swooleRequest, Swool }); // Fetch domains every `DOMAIN_SYNC_TIMER` seconds and update in the memory -$http->on(Constant::EVENT_TASK, function () use ($register, $domains) { +$http->on(Constant::EVENT_TASK, function () use ($register) { $lastSyncUpdate = null; $pools = $register->get('pools'); Http::setResource('pools', fn () => $pools); @@ -591,7 +595,10 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) { /** @var Utopia\Database\Database $dbForPlatform */ $dbForPlatform = $app->getResource('dbForPlatform'); - Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $domains, &$lastSyncUpdate, $app) { + /** @var Table $riskyDomains */ + $riskyDomains = $app->getResource('riskyDomains'); + + Timer::tick(DOMAIN_SYNC_TIMER * 1000, function () use ($dbForPlatform, $riskyDomains, &$lastSyncUpdate, $app) { try { $time = DateTime::now(); $limit = 1000; @@ -644,7 +651,7 @@ $http->on(Constant::EVENT_TASK, function () use ($register, $domains) { continue; } - $domains->set(md5($domain), ['value' => 1]); + $riskyDomains->set(md5($domain), ['value' => 1]); } $latestDocument = !empty(array_key_last($results)) ? $results[array_key_last($results)] : null; } diff --git a/composer.json b/composer.json index b7d3bc5483..ce55926d23 100644 --- a/composer.json +++ b/composer.json @@ -51,6 +51,7 @@ "utopia-php/auth": "0.5.*", "utopia-php/cache": "1.0.*", "utopia-php/cli": "0.22.*", + "utopia-php/compression": "0.1.*", "utopia-php/config": "1.*", "utopia-php/console": "0.1.*", "utopia-php/database": "5.*", @@ -72,7 +73,7 @@ "utopia-php/preloader": "0.2.*", "utopia-php/queue": "0.15.*", "utopia-php/registry": "0.5.*", - "utopia-php/storage": "0.18.*", + "utopia-php/storage": "1.0.*", "utopia-php/system": "0.10.*", "utopia-php/telemetry": "0.2.*", "utopia-php/vcs": "1.*", diff --git a/composer.lock b/composer.lock index ddc2754e98..5a6958c241 100644 --- a/composer.lock +++ b/composer.lock @@ -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": "fe847eccf6ba73bfca1e08d26e9fc7ab", + "content-hash": "dd9887a50fb434887461a99413007573", "packages": [ { "name": "adhocore/jwt", @@ -161,16 +161,16 @@ }, { "name": "appwrite/php-runtimes", - "version": "0.19.3", + "version": "0.19.4", "source": { "type": "git", "url": "https://github.com/appwrite/runtimes.git", - "reference": "bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e" + "reference": "eea9d1b3ca2540eab623b419c8afde09ef406c0b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/runtimes/zipball/bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e", - "reference": "bfe9f3c730c8c203c5e2d80bd5a81c9b1579714e", + "url": "https://api.github.com/repos/appwrite/runtimes/zipball/eea9d1b3ca2540eab623b419c8afde09ef406c0b", + "reference": "eea9d1b3ca2540eab623b419c8afde09ef406c0b", "shasum": "" }, "require": { @@ -210,9 +210,9 @@ ], "support": { "issues": "https://github.com/appwrite/runtimes/issues", - "source": "https://github.com/appwrite/runtimes/tree/0.19.3" + "source": "https://github.com/appwrite/runtimes/tree/0.19.4" }, - "time": "2026-02-06T10:08:18+00:00" + "time": "2026-02-17T10:04:39+00:00" }, { "name": "brick/math", @@ -3656,20 +3656,20 @@ }, { "name": "utopia-php/compression", - "version": "0.1.3", + "version": "0.1.4", "source": { "type": "git", "url": "https://github.com/utopia-php/compression.git", - "reference": "66f093557ba66d98245e562036182016c7dcfe8a" + "reference": "68045cb9d714c1259582d2dfd0e76bd34f83e713" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/compression/zipball/66f093557ba66d98245e562036182016c7dcfe8a", - "reference": "66f093557ba66d98245e562036182016c7dcfe8a", + "url": "https://api.github.com/repos/utopia-php/compression/zipball/68045cb9d714c1259582d2dfd0e76bd34f83e713", + "reference": "68045cb9d714c1259582d2dfd0e76bd34f83e713", "shasum": "" }, "require": { - "php": ">=8.0" + "php": ">=8.1" }, "require-dev": { "laravel/pint": "1.2.*", @@ -3696,9 +3696,9 @@ ], "support": { "issues": "https://github.com/utopia-php/compression/issues", - "source": "https://github.com/utopia-php/compression/tree/0.1.3" + "source": "https://github.com/utopia-php/compression/tree/0.1.4" }, - "time": "2025-01-15T15:15:51+00:00" + "time": "2026-02-17T05:53:40+00:00" }, { "name": "utopia-php/config", @@ -3797,16 +3797,16 @@ }, { "name": "utopia-php/database", - "version": "5.2.0", + "version": "5.3.0", "source": { "type": "git", "url": "https://github.com/utopia-php/database.git", - "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7" + "reference": "5e49f32ebb6e40a78209231564c40e5068810429" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/database/zipball/5c89b39de00f2b3126d0fbbdea36786341293df7", - "reference": "5c89b39de00f2b3126d0fbbdea36786341293df7", + "url": "https://api.github.com/repos/utopia-php/database/zipball/5e49f32ebb6e40a78209231564c40e5068810429", + "reference": "5e49f32ebb6e40a78209231564c40e5068810429", "shasum": "" }, "require": { @@ -3849,9 +3849,9 @@ ], "support": { "issues": "https://github.com/utopia-php/database/issues", - "source": "https://github.com/utopia-php/database/tree/5.2.0" + "source": "https://github.com/utopia-php/database/tree/5.3.0" }, - "time": "2026-02-14T09:37:28+00:00" + "time": "2026-02-17T11:50:01+00:00" }, { "name": "utopia-php/detector", @@ -3948,16 +3948,16 @@ }, { "name": "utopia-php/dns", - "version": "1.6.2", + "version": "1.6.3", "source": { "type": "git", "url": "https://github.com/utopia-php/dns.git", - "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab" + "reference": "c46a13c42bc19365985264a6fdaaaf7d8e736d3e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/dns/zipball/98c70520213a41e2fe1867e5b110273c06bf1cab", - "reference": "98c70520213a41e2fe1867e5b110273c06bf1cab", + "url": "https://api.github.com/repos/utopia-php/dns/zipball/c46a13c42bc19365985264a6fdaaaf7d8e736d3e", + "reference": "c46a13c42bc19365985264a6fdaaaf7d8e736d3e", "shasum": "" }, "require": { @@ -3999,9 +3999,9 @@ ], "support": { "issues": "https://github.com/utopia-php/dns/issues", - "source": "https://github.com/utopia-php/dns/tree/1.6.2" + "source": "https://github.com/utopia-php/dns/tree/1.6.3" }, - "time": "2026-02-13T12:29:08+00:00" + "time": "2026-02-17T10:45:46+00:00" }, { "name": "utopia-php/domains", @@ -4464,16 +4464,16 @@ }, { "name": "utopia-php/migration", - "version": "1.6.0", + "version": "1.6.1", "source": { "type": "git", "url": "https://github.com/utopia-php/migration.git", - "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849" + "reference": "c5c7544d02d2418536d41050794050132f247d62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/migration/zipball/aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", - "reference": "aa07cf9ae8cc4b8df0ab64588e033693b5ad6849", + "url": "https://api.github.com/repos/utopia-php/migration/zipball/c5c7544d02d2418536d41050794050132f247d62", + "reference": "c5c7544d02d2418536d41050794050132f247d62", "shasum": "" }, "require": { @@ -4484,7 +4484,7 @@ "php": ">=8.1", "utopia-php/database": "5.*", "utopia-php/dsn": "0.2.*", - "utopia-php/storage": "0.18.*" + "utopia-php/storage": "1.0.*" }, "require-dev": { "ext-pdo": "*", @@ -4513,9 +4513,9 @@ ], "support": { "issues": "https://github.com/utopia-php/migration/issues", - "source": "https://github.com/utopia-php/migration/tree/1.6.0" + "source": "https://github.com/utopia-php/migration/tree/1.6.1" }, - "time": "2026-02-16T07:19:27+00:00" + "time": "2026-02-17T05:49:48+00:00" }, { "name": "utopia-php/mongo", @@ -4953,32 +4953,27 @@ }, { "name": "utopia-php/storage", - "version": "0.18.19", + "version": "1.0.0", "source": { "type": "git", "url": "https://github.com/utopia-php/storage.git", - "reference": "9c3f9a471250d22de7d405ee19e23e72b14181ca" + "reference": "f672e8865938e5d1d6dc3bd149ceba4e5582199e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/utopia-php/storage/zipball/9c3f9a471250d22de7d405ee19e23e72b14181ca", - "reference": "9c3f9a471250d22de7d405ee19e23e72b14181ca", + "url": "https://api.github.com/repos/utopia-php/storage/zipball/f672e8865938e5d1d6dc3bd149ceba4e5582199e", + "reference": "f672e8865938e5d1d6dc3bd149ceba4e5582199e", "shasum": "" }, "require": { - "ext-brotli": "*", "ext-curl": "*", "ext-fileinfo": "*", - "ext-lz4": "*", "ext-simplexml": "*", - "ext-snappy": "*", - "ext-xz": "*", "ext-zlib": "*", - "ext-zstd": "*", "php": ">=8.1", - "utopia-php/framework": "0.*.*", "utopia-php/system": "0.*.*", - "utopia-php/telemetry": "0.2.*" + "utopia-php/telemetry": "0.2.*", + "utopia-php/validators": "0.2.*" }, "require-dev": { "laravel/pint": "1.2.*", @@ -5005,9 +5000,9 @@ ], "support": { "issues": "https://github.com/utopia-php/storage/issues", - "source": "https://github.com/utopia-php/storage/tree/0.18.19" + "source": "https://github.com/utopia-php/storage/tree/1.0.0" }, - "time": "2025-12-17T13:55:20+00:00" + "time": "2026-02-17T04:37:10+00:00" }, { "name": "utopia-php/system", @@ -5390,16 +5385,16 @@ "packages-dev": [ { "name": "appwrite/sdk-generator", - "version": "1.9.2", + "version": "1.9.3", "source": { "type": "git", "url": "https://github.com/appwrite/sdk-generator.git", - "reference": "74de906ea5051030c5299a5d4aa74d963a531130" + "reference": "5a8a304e404c84539f3ac6562c736f8c7ef2bef2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/74de906ea5051030c5299a5d4aa74d963a531130", - "reference": "74de906ea5051030c5299a5d4aa74d963a531130", + "url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/5a8a304e404c84539f3ac6562c736f8c7ef2bef2", + "reference": "5a8a304e404c84539f3ac6562c736f8c7ef2bef2", "shasum": "" }, "require": { @@ -5435,9 +5430,9 @@ "description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms", "support": { "issues": "https://github.com/appwrite/sdk-generator/issues", - "source": "https://github.com/appwrite/sdk-generator/tree/1.9.2" + "source": "https://github.com/appwrite/sdk-generator/tree/1.9.3" }, - "time": "2026-02-16T06:59:54+00:00" + "time": "2026-02-17T11:48:05+00:00" }, { "name": "doctrine/annotations", @@ -8890,7 +8885,7 @@ ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md index 2f777f513b..2a45350168 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-longtext-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createLongtextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md index 4a07863a87..6d47b00f2b 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-mediumtext-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createMediumtextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md index 8e2a1c979b..d883c36b89 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-text-attribute.md @@ -13,7 +13,8 @@ const result = await databases.createTextAttribute({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md b/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md index f5d61db290..9b3d2a4924 100644 --- a/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md +++ b/docs/examples/1.8.x/console-web/examples/databases/create-varchar-attribute.md @@ -14,7 +14,8 @@ const result = await databases.createVarcharAttribute({ size: 1, required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md index d3b25f3046..89603a4780 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-longtext-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createLongtextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md index a0ec9f2192..aef3b40476 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-mediumtext-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createMediumtextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md index 65a79782ee..763e2df801 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-text-column.md @@ -13,7 +13,8 @@ const result = await tablesDB.createTextColumn({ key: '', required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md b/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md index a5d0978ddf..eee225d886 100644 --- a/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md +++ b/docs/examples/1.8.x/console-web/examples/tablesdb/create-varchar-column.md @@ -14,7 +14,8 @@ const result = await tablesDB.createVarcharColumn({ size: 1, required: false, default: '', // optional - array: false // optional + array: false, // optional + encrypt: false // optional }); console.log(result); diff --git a/docs/sdks/cli/CHANGELOG.md b/docs/sdks/cli/CHANGELOG.md index 5aa8ee4cdc..9d83abe9c1 100644 --- a/docs/sdks/cli/CHANGELOG.md +++ b/docs/sdks/cli/CHANGELOG.md @@ -4,6 +4,9 @@ * Add `--queries` option to `list-keys` command * Add `--key-id` option to `create-key` command +* Add `encrypt` param support to new string type attributes +* Fix `bignumber.js` bundler conflict by removing direct dependency in favor of transitive dependency from `json-bigint` +* Fix missing region in console failUrl for failed deployments ## 13.3.2 diff --git a/docs/sdks/react-native/CHANGELOG.md b/docs/sdks/react-native/CHANGELOG.md index d111ee5a20..332b27ada4 100644 --- a/docs/sdks/react-native/CHANGELOG.md +++ b/docs/sdks/react-native/CHANGELOG.md @@ -1,5 +1,10 @@ # Change log +## 0.23.1 + +* Add `upsert` method to Realtime `Channels` helper class +* Fix `bignumber.js` bundler conflict by removing direct dependency in favor of transitive dependency from `json-bigint` + ## 0.21.0 * Add `queries` parameter to `client.subscribe()` for filtering Realtime events diff --git a/docs/sdks/web/CHANGELOG.md b/docs/sdks/web/CHANGELOG.md index 7f24cb7c38..a91f186fc4 100644 --- a/docs/sdks/web/CHANGELOG.md +++ b/docs/sdks/web/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +## 22.3.1 + +* Add `upsert` method to Realtime `Channels` helper class +* Fix `bignumber.js` bundler conflict with Next.js Turbopack by removing direct dependency in favor of transitive dependency from `json-bigint` + ## 22.1.0 * Add `queries` parameter to `Realtime.subscribe()` and `client.subscribe()` for server-side query filtering diff --git a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php index af83a3efd3..40b3692ad9 100644 --- a/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php +++ b/src/Appwrite/Platform/Modules/Console/Http/Variables/Get.php @@ -56,7 +56,8 @@ class Get extends Action $isCNAMEValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')) && $validator->isKnown() && !$validator->isTest(); $validator = new IP(IP::V4); - $isAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_A', '')) && ($validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_A'))); + $targetA = \explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', ''))[0]; + $isAValid = !empty($targetA) && ($validator->isValid($targetA)); $validator = new IP(IP::V6); $isAAAAValid = !empty(System::getEnv('_APP_DOMAIN_TARGET_AAAA', '')) && $validator->isValid(System::getEnv('_APP_DOMAIN_TARGET_AAAA')); @@ -74,7 +75,7 @@ class Get extends Action $variables = new Document([ '_APP_DOMAIN_TARGET_CNAME' => System::getEnv('_APP_DOMAIN_TARGET_CNAME'), '_APP_DOMAIN_TARGET_AAAA' => System::getEnv('_APP_DOMAIN_TARGET_AAAA'), - '_APP_DOMAIN_TARGET_A' => System::getEnv('_APP_DOMAIN_TARGET_A'), + '_APP_DOMAIN_TARGET_A' => $targetA, '_APP_DOMAIN_TARGET_CAA' => '0 issue "' . System::getEnv('_APP_DOMAIN_TARGET_CAA') . '"', '_APP_STORAGE_LIMIT' => +System::getEnv('_APP_STORAGE_LIMIT'), '_APP_COMPUTE_BUILD_TIMEOUT' => +System::getEnv('_APP_COMPUTE_BUILD_TIMEOUT'), diff --git a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php index f5f7a7974b..485078af88 100644 --- a/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php +++ b/src/Appwrite/Platform/Modules/Functions/Workers/Screenshots.php @@ -7,6 +7,7 @@ use Appwrite\Event\Realtime; use Appwrite\Permission; use Appwrite\Role; use Exception; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -16,7 +17,6 @@ use Utopia\Database\Query; use Utopia\Fetch\Client as FetchClient; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; diff --git a/src/Appwrite/Platform/Modules/Proxy/Action.php b/src/Appwrite/Platform/Modules/Proxy/Action.php index 62d6efbfa3..30ad140530 100644 --- a/src/Appwrite/Platform/Modules/Proxy/Action.php +++ b/src/Appwrite/Platform/Modules/Proxy/Action.php @@ -130,15 +130,7 @@ class Action extends PlatformAction $resourceType = $rule->getAttribute('deploymentResourceType', ''); // Ensures different target based on rule's type, as configured by env variables - if ($resourceType === 'function') { - // For example: fra.appwrite.run - foreach (\explode(',', System::getEnv('_APP_DOMAIN_FUNCTIONS', '')) as $targetCNAME) { - if (empty($targetCNAME)) { - continue; - } - $targetCNAMEs[] = new Domain($targetCNAME); - } - } elseif ($resourceType === 'site') { + if ($resourceType === 'site') { // For example: appwrite.network foreach (\explode(',', System::getEnv('_APP_DOMAIN_SITES', '')) as $targetCNAME) { if (empty($targetCNAME)) { @@ -146,7 +138,7 @@ class Action extends PlatformAction } $targetCNAMEs[] = new Domain($targetCNAME); } - } elseif ($ruleType === 'api') { + } elseif ($resourceType === 'function' || $ruleType === 'api') { // For example: fra.cloud.appwrite.io $targetCNAMEs[] = new Domain(System::getEnv('_APP_DOMAIN_TARGET_CNAME', '')); } elseif ($ruleType === 'redirect') { @@ -178,13 +170,17 @@ class Action extends PlatformAction } // Ensure at least one of CNAME/A/AAAA record points to our servers properly - $targetA = System::getEnv('_APP_DOMAIN_TARGET_A', ''); - if ((new IP(IP::V4))->isValid($targetA)) { - $validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers); - $validators[] = $validator; + foreach (\explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', '')) as $targetA) { + if (empty($targetA)) { + continue; + } + if ((new IP(IP::V4))->isValid($targetA)) { + $validator = new $dnsValidatorClass($targetA, Record::TYPE_A, $dnsServers); + $validators[] = $validator; - if (\is_null($mainValidator)) { - $mainValidator = $validator; + if (\is_null($mainValidator)) { + $mainValidator = $validator; + } } } diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php index c78402e5a4..f31d30188a 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Create.php @@ -9,6 +9,7 @@ use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -17,7 +18,6 @@ use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php index 6473f1058a..827dbc8dd9 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Create.php @@ -14,6 +14,9 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Duplicate as DuplicateException; @@ -28,9 +31,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\Storage\Storage; use Utopia\Storage\Validator\File; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php index 4a52c87238..042ae76565 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Download/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Validator\Authorization; @@ -19,9 +22,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php index a53cbb3a52..63a72fc683 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Preview/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -23,9 +26,6 @@ use Utopia\Http\Adapter\Swoole\Request; use Utopia\Image\Image; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\HexColor; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php index 60846cb546..c475c53d24 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/Push/Get.php @@ -8,6 +8,9 @@ use Appwrite\Extend\Exception; use Appwrite\OpenSSL\OpenSSL; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -16,9 +19,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php index 73e7e58421..cba6c2fa13 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Files/View/Get.php @@ -11,6 +11,9 @@ use Appwrite\SDK\MethodType; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Database\Documents\User; use Appwrite\Utopia\Response; +use Utopia\Compression\Algorithms\GZIP; +use Utopia\Compression\Algorithms\Zstd; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Database\Database; use Utopia\Database\Document; @@ -20,9 +23,6 @@ use Utopia\Database\Validator\UID; use Utopia\Http\Adapter\Swoole\Request; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Algorithms\GZIP; -use Utopia\Storage\Compression\Algorithms\Zstd; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; use Utopia\Validator\Text; diff --git a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php index 9dd5a29967..e4827a6354 100644 --- a/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php +++ b/src/Appwrite/Platform/Modules/Storage/Http/Buckets/Update.php @@ -8,13 +8,13 @@ use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; use Appwrite\SDK\Response as SDKResponse; use Appwrite\Utopia\Response; +use Utopia\Compression\Compression; use Utopia\Database\Database; use Utopia\Database\Helpers\Permission; use Utopia\Database\Validator\Permissions; use Utopia\Database\Validator\UID; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Storage; use Utopia\System\System; use Utopia\Validator\ArrayList; diff --git a/src/Appwrite/Platform/Tasks/Doctor.php b/src/Appwrite/Platform/Tasks/Doctor.php index 8ac1e1121b..9a9c2fdf73 100644 --- a/src/Appwrite/Platform/Tasks/Doctor.php +++ b/src/Appwrite/Platform/Tasks/Doctor.php @@ -63,10 +63,11 @@ class Doctor extends Action } $ipv4 = new IP(IP::V4); - if (!$ipv4->isValid(System::getEnv('_APP_DOMAIN_TARGET_A'))) { - Console::log('🔴 A record target is not valid (' . System::getEnv('_APP_DOMAIN_TARGET_A') . ')'); + $targetA = \explode(',', System::getEnv('_APP_DOMAIN_TARGET_A', ''))[0]; + if (!$ipv4->isValid($targetA)) { + Console::log('🔴 A record target is not valid (' . $targetA . ')'); } else { - Console::log('🟢 A record target is valid (' . System::getEnv('_APP_DOMAIN_TARGET_A') . ')'); + Console::log('🟢 A record target is valid (' . $targetA . ')'); } $ipv6 = new IP(IP::V6); diff --git a/src/Appwrite/Platform/Tasks/SDKs.php b/src/Appwrite/Platform/Tasks/SDKs.php index f45435757b..9020cd3208 100644 --- a/src/Appwrite/Platform/Tasks/SDKs.php +++ b/src/Appwrite/Platform/Tasks/SDKs.php @@ -77,11 +77,13 @@ class SDKs extends Action ->param('release', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we create releases?', optional: true) ->param('commit', null, new Nullable(new WhiteList(['yes', 'no'])), 'Actually create releases (yes) or dry-run (no)?', optional: true) ->param('sdks', null, new Nullable(new Text(256)), 'Selected SDKs', optional: true) + ->param('mode', 'full', new WhiteList(['full', 'examples']), 'Generation mode: full (default) or examples (only generate and copy examples)', optional: true) ->callback($this->action(...)); } - public function action(?string $platform, ?string $sdk, ?string $version, ?string $git, ?string $message, ?string $release, ?string $commit, ?string $sdks): void + public function action(?string $platform, ?string $sdk, ?string $version, ?string $git, ?string $message, ?string $release, ?string $commit, ?string $sdks, string $mode): void { + $examplesOnly = ($mode === 'examples'); $selectedPlatform = $platform; $selectedSDK = $sdk; @@ -99,7 +101,7 @@ class SDKs extends Action $createRelease = ($release === 'yes'); $commitRelease = ($commit === 'yes'); - if (!$createRelease) { + if (!$createRelease && !$examplesOnly) { $git ??= Console::confirm('Should we use git push? (yes/no)'); $git = ($git === 'yes'); @@ -108,6 +110,9 @@ class SDKs extends Action if ($git) { $message ??= Console::confirm('Please enter your commit message:'); } + } elseif ($examplesOnly) { + $git = false; + $prUrls = []; } if (!\in_array($version, [ @@ -293,7 +298,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND throw new \Exception('Language "' . $language['key'] . '" not supported'); } - if ($createRelease) { + if ($createRelease && !$examplesOnly) { $releaseVersion = $language['version']; $repoName = $language['gitUserName'] . '/' . $language['gitRepoName']; @@ -403,7 +408,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND continue; } - Console::info("Generating {$language['name']} SDK..."); + Console::info($examplesOnly + ? "Generating examples for {$language['name']} SDK..." + : "Generating {$language['name']} SDK..."); $sdk = new SDK($config, new Swagger2($spec)); diff --git a/src/Appwrite/Platform/Tasks/Specs.php b/src/Appwrite/Platform/Tasks/Specs.php index 02d83e6d48..adc00d83df 100644 --- a/src/Appwrite/Platform/Tasks/Specs.php +++ b/src/Appwrite/Platform/Tasks/Specs.php @@ -23,6 +23,7 @@ use Utopia\Http\Request as UtopiaRequest; use Utopia\Http\Response as UtopiaResponse; use Utopia\Platform\Action; use Utopia\System\System; +use Utopia\Validator\Nullable; use Utopia\Validator\Text; use Utopia\Validator\WhiteList; @@ -34,6 +35,9 @@ class Specs extends Action ->desc('Generate Appwrite API specifications') ->param('version', 'latest', new Text(16), 'Spec version', true) ->param('mode', 'normal', new WhiteList(['normal', 'mocks']), 'Spec Mode', true) + ->param('git', null, new Nullable(new WhiteList(['yes', 'no'])), 'Should we push to the specs repo?', optional: true) + ->param('message', null, new Nullable(new Text(256)), 'Commit Message', optional: true) + ->param('branch', null, new Nullable(new Text(256)), 'Target branch for PR (defaults to main)', optional: true) ->callback($this->action(...)); } @@ -230,8 +234,20 @@ class Specs extends Action return $sdkPlatforms; } - public function action(string $version, string $mode): void + public function action(string $version, string $mode, ?string $git, ?string $message, ?string $branch): void { + if (\is_null($git)) { + $git = Console::confirm('Should we push specs to the appwrite/specs repo? (yes/no)'); + } + + if ($git === 'yes' && \is_null($message)) { + $message = Console::confirm('Please enter your commit message:'); + } + + if (\is_null($branch)) { + $branch = 'main'; + } + $appRoutes = Http::getRoutes(); /** @var AppwriteResponse $response */ @@ -249,6 +265,8 @@ class Specs extends Action $authCounts = $this->getAuthCounts(); $keys = $this->getKeys(); + $generatedFiles = []; + foreach ($platforms as $platform) { $routes = []; $models = []; @@ -371,6 +389,7 @@ class Specs extends Action throw new Exception('Failed to save mocks spec file: ' . $path); } + $generatedFiles[] = realpath($path); Console::success('Saved mocks spec file: ' . realpath($path)); continue; @@ -382,8 +401,113 @@ class Specs extends Action throw new Exception('Failed to save spec file: ' . $path); } + $generatedFiles[] = realpath($path); Console::success('Saved spec file: ' . realpath($path)); } } + + if ($git === 'yes') { + $gitUrl = 'git@github.com:appwrite/specs.git'; + $gitRepoName = 'appwrite/specs'; + $gitBranch = 'feat-' . $version . '-specs'; + $repoBranch = $branch; + $target = \realpath(__DIR__ . '/../../../../app') . '/sdks/git/specs/'; + $examplesDir = \realpath(__DIR__ . '/../../../..') . '/docs/examples/' . $version; + + Console::info("Cloning {$gitRepoName} into {$target}..."); + + \exec('rm -rf ' . $target . ' && \ + mkdir -p ' . $target . ' && \ + cd ' . $target . ' && \ + git init && \ + git config core.ignorecase false && \ + git config pull.rebase false && \ + git remote add origin ' . $gitUrl . ' && \ + git fetch origin && \ + (git checkout -f ' . $repoBranch . ' 2>/dev/null || git checkout -b ' . $repoBranch . ') && \ + git pull origin ' . $repoBranch . ' && \ + (git checkout -f ' . $gitBranch . ' 2>/dev/null || git checkout -b ' . $gitBranch . ') && \ + (git fetch origin ' . $gitBranch . ' 2>/dev/null || git push -u origin ' . $gitBranch . ') && \ + git reset --hard origin/' . $gitBranch . ' 2>/dev/null || true + '); + + // Copy generated spec files into specs/{version}/ subdirectory + $specsSubDir = $mocks ? 'mocks' : $version; + \exec('mkdir -p ' . \escapeshellarg("{$target}/specs/{$specsSubDir}")); + foreach ($generatedFiles as $file) { + $fileName = \basename($file); + \exec('cp ' . \escapeshellarg($file) . ' ' . \escapeshellarg("{$target}/specs/{$specsSubDir}/{$fileName}")); + Console::success("Copied spec file to repo: specs/{$specsSubDir}/{$fileName}"); + } + + // Regenerate SDK examples for this version + Console::info("Regenerating SDK examples for version {$version}..."); + $sdksCommand = 'php ' . \escapeshellarg(\realpath(__DIR__ . '/../../../../app') . '/cli.php') + . ' sdks --platform=* --sdk=* --version=' . \escapeshellarg($version) + . ' --git=no --mode=examples 2>&1'; + \exec($sdksCommand, $sdksOutput, $sdksReturnCode); + + if ($sdksReturnCode !== 0) { + Console::warning("SDK examples generation returned non-zero exit code: {$sdksReturnCode}"); + Console::warning(\implode("\n", $sdksOutput)); + } else { + Console::success("Regenerated SDK examples for version {$version}"); + } + + // Copy SDK examples for this version + if (\is_dir($examplesDir)) { + \exec('mkdir -p ' . \escapeshellarg("{$target}/examples/{$version}") . ' && \ + cp -r ' . \escapeshellarg($examplesDir) . '/. ' . \escapeshellarg("{$target}/examples/{$version}/")); + Console::success("Copied SDK examples for version {$version} to repo: examples/{$version}/"); + } else { + Console::warning("No SDK examples found at: {$examplesDir}. Skipping examples copy."); + } + + // Git add, commit, and push + \exec('cd ' . $target . ' && \ + git add -A && \ + git commit -m "' . \addslashes($message) . '" && \ + git push -u origin ' . $gitBranch . ' + '); + + Console::success("Pushed specs to {$gitRepoName} on branch {$gitBranch}"); + + // Create or update PR + $prTitle = "feat: API specs update for version {$version}"; + $prBody = "This PR contains API specification updates and SDK examples for version {$version}."; + + $prCommand = 'cd ' . $target . ' && \ + gh pr create \ + --repo "' . $gitRepoName . '" \ + --title "' . $prTitle . '" \ + --body "' . $prBody . '" \ + --base "' . $repoBranch . '" \ + --head "' . $gitBranch . '" \ + 2>&1'; + + $prUrl = ''; + \exec($prCommand, $prOutput); + $prOutput = \implode("\n", $prOutput); + + if (\str_contains($prOutput, 'already exists')) { + Console::warning("PR already exists for branch {$gitBranch}"); + // Try to get the existing PR URL + \exec('cd ' . $target . ' && gh pr view --repo "' . $gitRepoName . '" --json url -q .url 2>&1', $existingPrOutput); + $prUrl = \trim(\implode("\n", $existingPrOutput)); + } else { + $prUrl = \trim($prOutput); + } + + // Clean up temp directory + \exec('chmod -R u+w ' . $target . ' && rm -rf ' . $target); + Console::success("Removed temp directory '{$target}'"); + + if (!empty($prUrl)) { + Console::log(''); + Console::log('Pull Request Summary'); + Console::log("Specs PR: {$prUrl}"); + Console::log(''); + } + } } } diff --git a/src/Appwrite/Platform/Workers/Migrations.php b/src/Appwrite/Platform/Workers/Migrations.php index 94cbcf341c..5e729af98f 100644 --- a/src/Appwrite/Platform/Workers/Migrations.php +++ b/src/Appwrite/Platform/Workers/Migrations.php @@ -8,6 +8,7 @@ use Appwrite\Event\Realtime; use Appwrite\Event\StatsUsage; use Appwrite\Extend\Exception; use Appwrite\Template\Template; +use Utopia\Compression\Compression; use Utopia\Config\Config; use Utopia\Console; use Utopia\Database\Database; @@ -39,7 +40,6 @@ use Utopia\Migration\Sources\Supabase; use Utopia\Migration\Transfer; use Utopia\Platform\Action; use Utopia\Queue\Message; -use Utopia\Storage\Compression\Compression; use Utopia\Storage\Device; use Utopia\System\System; diff --git a/src/Appwrite/Utopia/Response/Model/Bucket.php b/src/Appwrite/Utopia/Response/Model/Bucket.php index aece4cf850..d66a27be97 100644 --- a/src/Appwrite/Utopia/Response/Model/Bucket.php +++ b/src/Appwrite/Utopia/Response/Model/Bucket.php @@ -4,7 +4,7 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; -use Utopia\Storage\Compression\Compression; +use Utopia\Compression\Compression; class Bucket extends Model { diff --git a/src/Appwrite/Utopia/Response/Model/File.php b/src/Appwrite/Utopia/Response/Model/File.php index c4e36286ea..9b3e6ff618 100644 --- a/src/Appwrite/Utopia/Response/Model/File.php +++ b/src/Appwrite/Utopia/Response/Model/File.php @@ -4,8 +4,8 @@ namespace Appwrite\Utopia\Response\Model; use Appwrite\Utopia\Response; use Appwrite\Utopia\Response\Model; +use Utopia\Compression\Compression; use Utopia\Database\Document; -use Utopia\Storage\Compression\Compression; class File extends Model { diff --git a/src/Appwrite/Utopia/Response/Model/Project.php b/src/Appwrite/Utopia/Response/Model/Project.php index c516aab73f..cd33a29685 100644 --- a/src/Appwrite/Utopia/Response/Model/Project.php +++ b/src/Appwrite/Utopia/Response/Model/Project.php @@ -283,6 +283,12 @@ class Project extends Model 'example' => ['vip'], 'array' => true, ]) + ->addRule('status', [ + 'type' => self::TYPE_STRING, + 'description' => 'Project status.', + 'default' => 'active', + 'example' => 'active', + ]) ; $services = Config::getParam('services', []); diff --git a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php index 31fd935ab4..2909ba47f4 100644 --- a/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php +++ b/tests/e2e/Services/Projects/ProjectsConsoleClientTest.php @@ -57,6 +57,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertNotEmpty($response['body']['$id']); $this->assertEquals('Project Test', $response['body']['name']); $this->assertEquals($team['body']['$id'], $response['body']['teamId']); + $this->assertEquals(PROJECT_STATUS_ACTIVE, $response['body']['status']); $this->assertArrayHasKey('platforms', $response['body']); $this->assertArrayHasKey('webhooks', $response['body']); $this->assertArrayHasKey('keys', $response['body']); @@ -76,6 +77,7 @@ class ProjectsConsoleClientTest extends Scope $this->assertNotEmpty($response['body']['$id']); $this->assertEquals('Project Test', $response['body']['name']); $this->assertEquals($team['body']['$id'], $response['body']['teamId']); + $this->assertEquals(PROJECT_STATUS_ACTIVE, $response['body']['status']); $this->assertArrayHasKey('platforms', $response['body']); $this->assertArrayHasKey('webhooks', $response['body']); $this->assertArrayHasKey('keys', $response['body']); diff --git a/tests/resources/coredns/Corefile b/tests/resources/coredns/Corefile index b156ba336d..7b312e8498 100644 --- a/tests/resources/coredns/Corefile +++ b/tests/resources/coredns/Corefile @@ -19,7 +19,7 @@ webapp.com { template IN CNAME { match "^stage-function\.webapp\.com\.$" - answer "{{ .Name }} 60 IN CNAME functions.localhost." + answer "{{ .Name }} 60 IN CNAME cname.localhost." fallthrough }