Compare commits

..
1 Commits
Author SHA1 Message Date
ArnabChatterjee20k ede1c3d769 added tests for the sequence filter 2025-07-18 10:55:31 +05:30
7128 changed files with 14195 additions and 178833 deletions
+2 -3
View File
@@ -85,9 +85,8 @@ _APP_COMPUTE_MAINTENANCE_INTERVAL=600
_APP_COMPUTE_RUNTIMES_NETWORK=runtimes
_APP_EXECUTOR_SECRET=your-secret-key
_APP_EXECUTOR_HOST=http://exc1/v1
_APP_BROWSER_HOST=http://appwrite-browser:3000/v1
_APP_FUNCTIONS_RUNTIMES=node-22
_APP_SITES_RUNTIMES=static-1,node-22
_APP_FUNCTIONS_RUNTIMES=php-8.0,node-18.0,python-3.9,ruby-3.1
_APP_SITES_RUNTIMES=static-1,node-22,flutter-3.32
_APP_MAINTENANCE_INTERVAL=86400
_APP_MAINTENANCE_START_TIME=12:00
_APP_MAINTENANCE_RETENTION_CACHE=2592000
+4 -87
View File
@@ -146,7 +146,7 @@ jobs:
Avatars,
Console,
Databases/Legacy,
Databases/Grids,
Databases/Tables,
Functions,
FunctionsSchedule,
GraphQL,
@@ -179,7 +179,6 @@ jobs:
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's|^_APP_BROWSER_HOST=.*|_APP_BROWSER_HOST=http://invalid-browser/v1|' .env
docker compose up -d
sleep 30
@@ -200,7 +199,7 @@ jobs:
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
e2e_shared_mode_test:
name: E2E Shared Mode Service Test
@@ -216,7 +215,7 @@ jobs:
Avatars,
Console,
Databases/Legacy,
Databases/Grids,
Databases/Tables,
Functions,
FunctionsSchedule,
GraphQL,
@@ -280,7 +279,7 @@ jobs:
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys,screenshots
appwrite test /usr/src/code/tests/e2e/Services/${{ matrix.service }} --debug --exclude-group devKeys
e2e_dev_keys:
name: E2E Service Test (Dev Keys)
@@ -361,85 +360,3 @@ jobs:
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Projects --debug --group=devKeys
e2e_screenshots_keys:
name: E2E Service Test (Site Screenshots)
runs-on: ubuntu-latest
needs: setup
steps:
- name: checkout
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
- name: Run Site tests with browser connected in dedicated table mode
run: |
echo "Keeping original value of _APP_BROWSER_HOST"
echo "Using project tables"
export _APP_DATABASE_SHARED_TABLES=
export _APP_DATABASE_SHARED_TABLES_V1=
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
e2e_screenshots_shared_mode:
name: E2E Shared Mode Service Test (Site Screenshots)
runs-on: ubuntu-latest
needs: [ setup, check_database_changes ]
if: needs.check_database_changes.outputs.database_changed == 'true'
strategy:
fail-fast: false
matrix:
tables-mode: [
'Shared V1',
'Shared V2',
]
steps:
- name: checkout
uses: actions/checkout@v4
- name: Load Cache
uses: actions/cache@v4
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
docker load --input /tmp/${{ env.IMAGE }}.tar
sed -i 's/_APP_OPTIONS_ABUSE=disabled/_APP_OPTIONS_ABUSE=enabled/' .env
docker compose up -d
sleep 30
- name: Run Site tests with browser connected in ${{ matrix.tables-mode }} table mode
run: |
echo "Keeping original value of _APP_BROWSER_HOST"
if [ "${{ matrix.tables-mode }}" == "Shared V1" ]; then
echo "Using shared tables V1"
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=database_db_main
elif [ "${{ matrix.tables-mode }}" == "Shared V2" ]; then
echo "Using shared tables V2"
export _APP_DATABASE_SHARED_TABLES=database_db_main
export _APP_DATABASE_SHARED_TABLES_V1=
fi
docker compose exec -T \
-e _APP_DATABASE_SHARED_TABLES \
-e _APP_DATABASE_SHARED_TABLES_V1 \
appwrite test /usr/src/code/tests/e2e/Services/Sites --debug --group=screenshots
+1 -1
View File
@@ -16,5 +16,5 @@ app/sdks
dev/yasd_init.php
.phpunit.result.cache
Makefile
appwrite.config.json
appwrite.json
/.zed/
+1 -25
View File
@@ -9,31 +9,7 @@ You can [find issues using this query](https://github.com/search?q=org%3Aappwrit
## How to Start?
Welcome! We're excited that you're interested in contributing to Appwrite. To make sure your time is valued and your contributions are successful, please follow these steps before writing any code:
### 🔍 Step 1: Find an Issue
Browse open issues and look for ones labeled [good first issue](https://github.com/search?q=org%3Aappwrite+is%3Aopen+type%3Aissue+label%3A%22good+first+issue%22&type=issues) or [help wanted](https://github.com/search?q=org%3Aappwrite+is%3Aopen+type%3Aissue+label%3A%22help+wanted%22&type=issues).
If you're not sure which issue to pick, ask in our [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) on Discord.
### 📝 Step 2: Ask to Be Assigned
Before working on an issue, comment on the GitHub issue asking to be assigned. This prevents multiple people working on the same task.
Then, create a thread in the [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) on Discord with a link to the issue.
Our team is small and may not see your GitHub comment right away - posting in the [maintainers channel](https://discord.com/channels/564160730845151244/636852860709240842) ensures it gets seen.
### 💬 Step 3: Dont Submit Random PRs
If you're not working on an assigned issue, create a GitHub issue first.
PRs submitted without context or discussion may not align with our roadmap and may be closed without review.
### ⚠️ Please Note
Were a very small team managing a large project. Many PRs are submitted, and while we appreciate every effort, we can only review contributions that follow the process above. This helps us keep things fair and organized.
If you are worried or dont know where to start, check out the next section that explains what kind of help we could use and where you can get involved. You can send your questions to [@appwrite on Twitter](https://twitter.com/appwrite) or to anyone from the [Appwrite team on Discord](https://appwrite.io/discord). You can also submit an issue, and a maintainer can guide you!
## Code of Conduct
+4 -6
View File
@@ -26,7 +26,7 @@
[**Appwrite 云公开测试版!立即注册!**](https://cloud.appwrite.io)
Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,原生应用,以及后端。它既包含后端服务器,也提供了用于部署静态和服务器端渲染前端的完全集成托管解决方案。Appwrite 通过视觉化界面简化了从零开始构建现代应用的复杂性和重复性,让您能够更快地构建安全的全栈应用
Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存,图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
@@ -72,7 +72,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
### Windows
@@ -84,7 +84,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
#### PowerShell
@@ -94,7 +94,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
@@ -179,8 +179,6 @@ docker run -it --rm `
- [**消息传递**](https://appwrite.io/docs/references/cloud/client-web/messaging) - 使用 Appwrite 消息传递功能通过推送通知、电子邮件和短信与用户进行通信。
- [**语言适配**](https://appwrite.io/docs/references/cloud/client-web/locale) - 根据用户所在的的国家和地区做出合适的语言适配。
- [**头像**](https://appwrite.io/docs/references/cloud/client-web/avatars) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
- [**MCP**](https://appwrite.io/docs/tooling/mcp) - 使用 Appwrite 的模型上下文协议(Model Context Protocol)服务器,允许大语言模型(LLM)和 AI 工具(如 Claude Desktop、Cursor 和 Windsurf Editor)通过自然语言直接与您的 Appwrite 项目交互。
- [**站点**](https://appwrite.io/docs/products/sites) - 直接从 Appwrite 开发、部署和扩展您的 Web 应用程序,与您的后端一起。
如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
### 开发套件
+12 -17
View File
@@ -1,14 +1,12 @@
> We just announced Auto-increment support for Appwrite Databases - [Learn more](https://appwrite.io/blog/post/announcing-auto-increment-support)
> [Get started with Appwrite](https://apwr.dev/appcloud)
> [Join the Init kick off event 19th of May: The future of Appwrite with Founder & CEO Eldad Fux](https://www.youtube.com/watch?v=1g8tuogsp7A)
<br />
<p align="center">
<a href="https://appwrite.io" target="_blank"><img src="./public/images/banner.png" alt="Appwrite banner, with logo and text saying "Build Like a Team of Hundreds"></a>
<br />
<br />
<b>Appwrite is an all-in-one development platform for Web, Mobile, and Flutter applications. Use built-in backend infrastructure and web hosting, all from a single place. Built with the open source community and optimized for developer experience in the coding languages you love.</b>
<b>Appwrite is a backend platform for developing Web, Mobile, and Flutter applications. Built with the open source community and optimized for developer experience in the coding languages you love.</b>
<br />
<br />
</p>
@@ -27,7 +25,7 @@
English | [简体中文](README-CN.md)
Appwrite is an end-to-end platform for building Web, Mobile, Native, or Backend apps, packaged as a set of Docker microservices. It includes both a backend server and a fully integrated hosting solution for deploying static and server-side rendered frontends. Appwrite abstracts the complexity and repetitiveness required to build modern apps from scratch and allows you to build secure, full-stack applications faster.
Appwrite is an end-to-end backend server for Web, Mobile, Native, or Backend apps packaged as a set of Docker<nobr> microservices. Appwrite abstracts the complexity and repetitiveness required to build a modern backend API from scratch and allows you to build secure apps faster.
Using Appwrite, you can easily integrate your app with user authentication and multiple sign-in methods, a database for storing and querying users and team data, storage and file management, image manipulation, Cloud Functions, messaging, and [more services](https://appwrite.io/docs).
@@ -44,7 +42,7 @@ Find out more at: [https://appwrite.io](https://appwrite.io).
Table of Contents:
- [Installation \& Setup](#installation--setup)
- [Getting Started](#getting-started)
- [Self-Hosting](#self-hosting)
- [Unix](#unix)
- [Windows](#windows)
@@ -64,9 +62,8 @@ Table of Contents:
- [Follow Us](#follow-us)
- [License](#license)
## Installation & Setup
The easiest way to get started with Appwrite is by [signing up for Appwrite Cloud](https://cloud.appwrite.io/). While Appwrite Cloud is in public beta, you can build with Appwrite completely free, and we won't collect your credit card information.
## Getting Started
The easiest way to get started with Appwrite is by [signing up for Appwrite Cloud](https://cloud.appwrite.io/). While Appwrite Cloud is in public beta, you can build with Appwrite completely free, and we won't collect you credit card information.
## Self-Hosting
@@ -81,7 +78,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
### Windows
@@ -93,7 +90,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
#### PowerShell
@@ -103,7 +100,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.8.0
appwrite/appwrite:1.7.4
```
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
@@ -131,19 +128,19 @@ Choose from one of the providers below:
<td align="center" width="100" height="100">
<a href="https://gitpod.io/#https://github.com/appwrite/integration-for-gitpod">
<img width="50" height="39" src="public/images/integrations/gitpod-logo.svg" alt="Gitpod Logo" />
<br /><sub><b>Gitpod</b></sub></a>
<br /><sub><b>Gitpod</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://www.linode.com/marketplace/apps/appwrite/appwrite/">
<img width="50" height="39" src="public/images/integrations/akamai-logo.svg" alt="Akamai Logo" />
<br /><sub><b>Akamai Compute</b></sub></a>
<br /><sub><b>Akamai Compute</b></sub></a>
</a>
</td>
<td align="center" width="100" height="100">
<a href="https://aws.amazon.com/marketplace/pp/prodview-2hiaeo2px4md6">
<img width="50" height="39" src="public/images/integrations/aws-logo.svg" alt="AWS Logo" />
<br /><sub><b>AWS Marketplace</b></sub></a>
<br /><sub><b>AWS Marketplace</b></sub></a>
</a>
</td>
</tr>
@@ -189,8 +186,6 @@ Getting started with Appwrite is as easy as creating a new project, choosing you
- [**Realtime**](https://appwrite.io/docs/realtime) - Listen to real-time events for any of your Appwrite services including users, storage, functions, databases, and more.
- [**Locale**](https://appwrite.io/docs/references/cloud/client-web/locale) - Track your user's location and manage your app locale-based data.
- [**Avatars**](https://appwrite.io/docs/references/cloud/client-web/avatars) - Manage your users' avatars, countries' flags, browser icons, and credit card symbols. Generate QR codes from links or plaintext strings.
- [**MCP**](https://appwrite.io/docs/tooling/mcp) - Use Appwrite's Model Context Protocol (MCP) server to allow LLMs and AI tools like Claude Desktop, Cursor, and Windsurf Editor to directly interact with your Appwrite project through natural language.
- [**Sites**](https://appwrite.io/docs/products/sites) - Develop, deploy, and scale your web applications directly from Appwrite, alongside your backend.
For the complete API documentation, visit [https://appwrite.io/docs](https://appwrite.io/docs). For more tutorials, news and announcements check out our [blog](https://medium.com/appwrite-io) and [Discord Server](https://discord.gg/GSeTUeA).
+3 -9
View File
@@ -191,15 +191,9 @@ CLI::setResource('getLogsDB', function (Group $pools, Cache $cache) {
CLI::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
CLI::setResource('publisherDatabases', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
CLI::setResource('publisherMigrations', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
CLI::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
CLI::setResource('publisherRedis', function () {
// Stub
});
CLI::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
}, ['publisher']);
-9
View File
@@ -51,15 +51,6 @@ return [
'default' => null,
'array' => false,
],
[
'$id' => ID::custom('type'),
'type' => Database::VAR_STRING,
'size' => 128,
'required' => true,
'signed' => true,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -120,11 +120,6 @@
</head>
<body>
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
{{preview}}
<div>{{previewWhitespace}}</div>
</div>
<div class="main">
<table>
<tr>
@@ -121,11 +121,6 @@
<body style="direction: {{direction}}">
<div style="display: none; overflow: hidden; max-height: 0; max-width: 0; opacity: 0; line-height: 1px;">
{{preview}}
<div>{{previewWhitespace}}</div>
</div>
<div style="max-width:650px; word-wrap: break-word; overflow-wrap: break-word;
word-break: normal; margin:0 auto;">
<table style="margin-top: 32px">
@@ -9,4 +9,4 @@
<p>If you have trouble with the sender's image, ensure it is set in the <a href="https://gravatar.com/">Gravatar database</a>.</p>
<p style="margin-block-end: 0;">Best regards,</p>
<p style="margin-block-start: 0;">Appwrite team</p>
<p style="margin-block-start: 0;">Appwrtite team</p>
@@ -14,7 +14,7 @@
<table border="0" cellspacing="0" cellpadding="0" style="padding-top: 10px; padding-bottom: 10px; margin-top: 32px">
<tr>
<td style="border-radius: 8px; display: block; width: 100%;">
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #2D2D31; border-radius: 8px; padding: 9px 14px; border: 1px solid #414146; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
<a class="mobile-full-width" rel="noopener" target="_blank" href="{{host}}{{path}}" style="font-size: 14px; font-family: Inter; color: #ffffff; text-decoration: none; background-color: #FD366E; border-radius: 8px; padding: 9px 14px; border: 1px solid #FD366E; display: inline-block; text-align:center; box-sizing: border-box;">Webhook settings</a>
</td>
</tr>
</table>
+1 -9
View File
@@ -4,7 +4,6 @@
"settings.direction": "ltr",
"emails.sender": "%s Team",
"emails.verification.subject": "Account Verification",
"emails.verification.preview": "Verify your email to activate your {{project}} account.",
"emails.verification.hello": "Hello {{user}},",
"emails.verification.body": "Follow this link to verify your email address to your {{b}}{{project}}{{/b}} account.",
"emails.verification.footer": "If you didnt ask to verify this address, you can ignore this message.",
@@ -12,7 +11,6 @@
"emails.verification.buttonText": "Confirm email address",
"emails.verification.signature": "{{project}} team",
"emails.magicSession.subject": "{{project}} Login",
"emails.magicSession.preview": "Sign in to {{project}} with your secure link. Expires in 1 hour.",
"emails.magicSession.hello": "Hello {{user}},",
"emails.magicSession.optionButton": "Click the button below to securely sign in to your {{b}}{{project}}{{/b}} account. This link will expire in 1 hour.",
"emails.magicSession.buttonText": "Sign in to {{project}}",
@@ -22,7 +20,6 @@
"emails.magicSession.thanks": "Thanks,",
"emails.magicSession.signature": "{{project}} team",
"emails.sessionAlert.subject": "Security alert: new session on your {{project}} account",
"emails.sessionAlert.preview": "New login detected on {{project}} at {{time}} UTC.",
"emails.sessionAlert.hello": "Hello {{user}},",
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, {{b}}on {{date}}, {{year}} at {{time}} UTC{{/b}}.\nHere are the details of the new session: ",
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
@@ -32,7 +29,6 @@
"emails.sessionAlert.thanks": "Thanks,",
"emails.sessionAlert.signature": "{{project}} team",
"emails.otpSession.subject": "OTP for {{project}} Login",
"emails.otpSession.preview": "Use OTP {{otp}} to sign in to {{project}}. Expires in 15 minutes.",
"emails.otpSession.hello": "Hello {{user}},",
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
"emails.otpSession.clientInfo": "This sign in was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the sign in, you can safely ignore this email.",
@@ -40,14 +36,12 @@
"emails.otpSession.thanks": "Thanks,",
"emails.otpSession.signature": "{{project}} team",
"emails.mfaChallenge.subject": "Verification Code for {{project}}",
"emails.mfaChallenge.preview": "Use code {{otp}} for two-step verification in {{project}}. Expires in 15 minutes.",
"emails.mfaChallenge.hello": "Hello {{user}},",
"emails.mfaChallenge.description": "Enter the following code to confirm your two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.",
"emails.mfaChallenge.description": "Enter the following verification code to verify your email and activate two-step verification in {{b}}{{project}}{{/b}}. This code will expire in 15 minutes.",
"emails.mfaChallenge.clientInfo": "This verification code was requested using {{b}}{{agentClient}}{{/b}} on {{b}}{{agentDevice}}{{/b}} {{b}}{{agentOs}}{{/b}}. If you didn't request the verification code, you can safely ignore this email.",
"emails.mfaChallenge.thanks": "Thanks,",
"emails.mfaChallenge.signature": "{{project}} team",
"emails.recovery.subject": "Password Reset",
"emails.recovery.preview": "Reset your {{project}} password using the link.",
"emails.recovery.hello": "Hello {{user}},",
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
"emails.recovery.footer": "If you didn't ask to reset your password, you can ignore this message.",
@@ -55,7 +49,6 @@
"emails.recovery.buttonText": "Reset password",
"emails.recovery.signature": "{{project}} team",
"emails.invitation.subject": "Invitation to %s Team at %s",
"emails.invitation.preview": "{{owner}} invited you to join {{team}} at {{project}}",
"emails.invitation.hello": "Hello {{user}},",
"emails.invitation.body": "This mail was sent to you because {{b}}{{owner}}{{/b}} wanted to invite you to become a member of the {{b}}{{team}}{{/b}} team at {{b}}{{project}}{{/b}}.",
"emails.invitation.footer": "If you are not interested, you can ignore this message.",
@@ -63,7 +56,6 @@
"emails.invitation.buttonText": "Accept invite to {{team}}",
"emails.invitation.signature": "{{project}} team",
"emails.certificate.subject": "Certificate failure for %s",
"emails.certificate.preview": "Your domain %s certificate generation has failed.",
"emails.certificate.hello": "Hello,",
"emails.certificate.body": "Certificate for your domain '{{domain}}' could not be generated. This is attempt no. {{attempt}}, and the failure was caused by: {{error}}",
"emails.certificate.footer": "Your previous certificate will be valid for 30 days since the first failure. We highly recommend investigating this case, otherwise your domain will end up without a valid SSL communication.",
+21 -44
View File
@@ -11,7 +11,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '18.2.0',
'version' => '18.1.0',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -25,7 +25,6 @@ return [
'gitRepoName' => 'sdk-for-web',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/web/CHANGELOG.md'),
'demos' => [
[
'icon' => 'react.svg',
@@ -60,7 +59,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '17.1.0',
'version' => '17.0.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -74,12 +73,11 @@ return [
'gitRepoName' => 'sdk-for-flutter',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/flutter/CHANGELOG.md'),
],
[
'key' => 'apple',
'name' => 'Apple',
'version' => '10.2.0',
'version' => '10.1.1',
'url' => 'https://github.com/appwrite/sdk-for-apple',
'package' => 'https://github.com/appwrite/sdk-for-apple',
'enabled' => true,
@@ -93,7 +91,6 @@ return [
'gitRepoName' => 'sdk-for-apple',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/apple/CHANGELOG.md'),
],
[
'key' => 'objective-c',
@@ -111,12 +108,11 @@ return [
'gitRepoName' => 'sdk-for-objective-c',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/objective-c/CHANGELOG.md'),
],
[
'key' => 'android',
'name' => 'Android',
'version' => '8.2.0',
'version' => '8.1.0',
'url' => 'https://github.com/appwrite/sdk-for-android',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-android',
'enabled' => true,
@@ -134,12 +130,11 @@ return [
'Kotlin' => 'kotlin',
'Java' => 'java',
],
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/android/CHANGELOG.md'),
],
[
'key' => 'react-native',
'name' => 'React Native',
'version' => '0.11.0',
'version' => '0.10.0',
'url' => 'https://github.com/appwrite/sdk-for-react-native',
'package' => 'https://npmjs.com/package/react-native-appwrite',
'enabled' => true,
@@ -153,7 +148,6 @@ return [
'gitRepoName' => 'sdk-for-react-native',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/react-native/CHANGELOG.md'),
],
[
'key' => 'graphql',
@@ -173,7 +167,6 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
],
[
'key' => 'rest',
@@ -193,7 +186,6 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
],
],
],
@@ -207,8 +199,8 @@ return [
[
'key' => 'web',
'name' => 'Console',
'version' => '0.1.0',
'url' => '',
'version' => '1.7.0',
'url' => 'https://github.com/appwrite/sdk-for-console',
'package' => '',
'enabled' => true,
'beta' => false,
@@ -217,16 +209,15 @@ return [
'family' => APP_PLATFORM_CONSOLE,
'prism' => 'javascript',
'source' => \realpath(__DIR__ . '/../sdks/console-web'),
'gitUrl' => '',
'gitUrl' => 'git@github.com:appwrite/sdk-for-console.git',
'gitBranch' => 'dev',
'gitRepoName' => '',
'gitUserName' => '',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/console/CHANGELOG.md'),
'gitRepoName' => 'sdk-for-console',
'gitUserName' => 'appwrite',
],
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '8.3.0',
'version' => '8.1.0',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -240,8 +231,6 @@ return [
'gitRepoName' => 'sdk-for-cli',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'repoBranch' => 'master',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/cli/CHANGELOG.md'),
'exclude' => [
'services' => [
['name' => 'assistant'],
@@ -261,7 +250,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '17.2.0',
'version' => '17.0.0',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -275,12 +264,11 @@ return [
'gitRepoName' => 'sdk-for-node',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/nodejs/CHANGELOG.md'),
],
[
'key' => 'deno',
'name' => 'Deno',
'version' => '15.1.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-deno',
'package' => 'https://deno.land/x/appwrite',
'enabled' => true,
@@ -294,12 +282,11 @@ return [
'gitRepoName' => 'sdk-for-deno',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/deno/CHANGELOG.md'),
],
[
'key' => 'php',
'name' => 'PHP',
'version' => '15.1.0',
'version' => '15.0.0',
'url' => 'https://github.com/appwrite/sdk-for-php',
'package' => 'https://packagist.org/packages/appwrite/appwrite',
'enabled' => true,
@@ -313,12 +300,11 @@ return [
'gitRepoName' => 'sdk-for-php',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/php/CHANGELOG.md'),
],
[
'key' => 'python',
'name' => 'Python',
'version' => '11.1.0',
'version' => '11.0.0',
'url' => 'https://github.com/appwrite/sdk-for-python',
'package' => 'https://pypi.org/project/appwrite/',
'enabled' => true,
@@ -332,12 +318,11 @@ return [
'gitRepoName' => 'sdk-for-python',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/python/CHANGELOG.md'),
],
[
'key' => 'ruby',
'name' => 'Ruby',
'version' => '16.1.0',
'version' => '16.0.0',
'url' => 'https://github.com/appwrite/sdk-for-ruby',
'package' => 'https://rubygems.org/gems/appwrite',
'enabled' => true,
@@ -351,12 +336,11 @@ return [
'gitRepoName' => 'sdk-for-ruby',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/ruby/CHANGELOG.md'),
],
[
'key' => 'go',
'name' => 'Go',
'version' => '0.9.0',
'version' => '0.8.0',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
@@ -370,12 +354,11 @@ return [
'gitRepoName' => 'sdk-for-go',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/go/CHANGELOG.md'),
],
[
'key' => 'dotnet',
'name' => '.NET',
'version' => '0.15.0',
'version' => '0.13.0',
'url' => 'https://github.com/appwrite/sdk-for-dotnet',
'package' => 'https://www.nuget.org/packages/Appwrite',
'enabled' => true,
@@ -389,12 +372,11 @@ return [
'gitRepoName' => 'sdk-for-dotnet',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dotnet/CHANGELOG.md'),
],
[
'key' => 'dart',
'name' => 'Dart',
'version' => '16.2.0',
'version' => '16.0.0',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
@@ -408,12 +390,11 @@ return [
'gitRepoName' => 'sdk-for-dart',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/dart/CHANGELOG.md'),
],
[
'key' => 'kotlin',
'name' => 'Kotlin',
'version' => '9.1.0',
'version' => '9.0.0',
'url' => 'https://github.com/appwrite/sdk-for-kotlin',
'package' => 'https://search.maven.org/artifact/io.appwrite/sdk-for-kotlin',
'enabled' => true,
@@ -431,12 +412,11 @@ return [
'Kotlin' => 'kotlin',
'Java' => 'java',
],
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/kotlin/CHANGELOG.md'),
],
[
'key' => 'swift',
'name' => 'Swift',
'version' => '10.2.0',
'version' => '10.1.0',
'url' => 'https://github.com/appwrite/sdk-for-swift',
'package' => 'https://github.com/appwrite/sdk-for-swift',
'enabled' => true,
@@ -450,7 +430,6 @@ return [
'gitRepoName' => 'sdk-for-swift',
'gitUserName' => 'appwrite',
'gitBranch' => 'dev',
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/swift/CHANGELOG.md'),
],
[
'key' => 'graphql',
@@ -470,7 +449,6 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/graphql/CHANGELOG.md'),
],
[
'key' => 'rest',
@@ -490,7 +468,6 @@ return [
'gitUserName' => '',
'gitBranch' => '',
'isSDK' => false,
'changelog' => \realpath(__DIR__ . '/../../docs/sdks/rest/CHANGELOG.md'),
],
],
],
+1 -14
View File
@@ -55,7 +55,7 @@ return [
],
'databases' => [
'key' => 'databases',
'name' => 'Databases (legacy)',
'name' => 'Databases',
'subtitle' => 'The Databases service allows you to create structured collections of documents, query and filter lists of documents',
'description' => '/docs/services/databases.md',
'controller' => '', // Uses modules
@@ -66,19 +66,6 @@ return [
'optional' => true,
'icon' => '/images/services/databases.png',
],
'tables' => [
'key' => 'tables',
'name' => 'Tables',
'subtitle' => 'The Tables service allows you to create structured tables of rows, query and filter lists of rows',
'description' => '/docs/services/tables.md',
'controller' => '', // Uses modules
'sdk' => true,
'docs' => true,
'docsUrl' => 'https://appwrite.io/docs/client/tables',
'tests' => false,
'optional' => true,
'icon' => '/images/services/databases.png',
],
'locale' => [
'key' => 'locale',
'name' => 'Locale',
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+2 -2
View File
@@ -827,7 +827,7 @@ return [
],
[
'name' => '_APP_FUNCTIONS_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.config.json.',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.json.',
'introduction' => '0.7.0',
'default' => '900',
'required' => false,
@@ -1070,7 +1070,7 @@ return [
'variables' => [
[
'name' => '_APP_SITES_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new site. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the sites\'s settings or in appwrite.config.json.',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new site. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the sites\'s settings or in appwrite.json.',
'introduction' => '1.7.0',
'default' => '900',
'required' => false,
+34 -136
View File
@@ -71,7 +71,6 @@ $oauthDefaultFailure = '/console/auth/oauth2/failure';
function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails)
{
$subject = $locale->getText("emails.sessionAlert.subject");
$preview = $locale->getText("emails.sessionAlert.preview");
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
@@ -133,16 +132,6 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
->setSmtpSenderName($senderName);
}
// session alerts should always have a client name!
$clientName = $session->getAttribute('clientName');
if (empty($clientName)) {
// fallback to the user agent and then unknown!
$userAgent = $session->getAttribute('userAgent');
$clientName = !empty($userAgent) ? $userAgent : 'UNKNOWN';
$session->setAttribute('clientName', $clientName);
}
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
'date' => (new \DateTime())->format('F j'),
@@ -159,7 +148,6 @@ function sendSessionAlert(Locale $locale, Document $user, Document $project, Doc
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($email)
@@ -1201,16 +1189,9 @@ App::get('/v1/account/sessions/oauth2/:provider')
->inject('response')
->inject('project')
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$protocol = $request->getProtocol();
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
if (!$providerEnabled) {
@@ -1235,20 +1216,12 @@ App::get('/v1/account/sessions/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$redirectBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$redirectBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$redirectBase .= ':' . $port;
}
if (empty($success)) {
$success = $redirectBase . $oauthDefaultSuccess;
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
}
if (empty($failure)) {
$failure = $redirectBase . $oauthDefaultFailure;
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
}
$oauth2 = new $className($appId, $appSecret, $callback, [
@@ -1278,14 +1251,9 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->inject('request')
->inject('response')
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$params = $request->getParams();
$params['project'] = $projectId;
@@ -1294,7 +1262,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query($params));
});
@@ -1314,14 +1282,8 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
->inject('request')
->inject('response')
->action(function (string $projectId, string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$domain = $request->getHostname();
$protocol = $request->getProtocol();
$params = $request->getParams();
$params['project'] = $projectId;
@@ -1330,7 +1292,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
->redirect($callbackBase . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
->redirect($protocol . '://' . $domain . '/v1/account/sessions/oauth2/' . $provider . '/redirect?'
. \http_build_query($params));
});
@@ -1354,24 +1316,15 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
->inject('request')
->inject('response')
->inject('project')
->inject('platforms')
->inject('devKey')
->inject('user')
->inject('dbForProject')
->inject('geodb')
->inject('queueForEvents')
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, array $platforms, Document $devKey, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
->action(function (string $provider, string $code, string $state, string $error, string $error_description, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Event $queueForEvents) use ($oauthDefaultSuccess) {
$protocol = $request->getProtocol();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
$redirect = new Redirect($platforms);
$validateURL = new URL();
$appId = $project->getAttribute('oAuthProviders', [])[$provider . 'Appid'] ?? '';
$appSecret = $project->getAttribute('oAuthProviders', [])[$provider . 'Secret'] ?? '{}';
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
@@ -1398,11 +1351,11 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$state = $defaultState;
}
if ($devKey->isEmpty() && !$redirect->isValid($state['success'])) {
if (!$validateURL->isValid($state['success'])) {
throw new Exception(Exception::PROJECT_INVALID_SUCCESS_URL);
}
if ($devKey->isEmpty() && !empty($state['failure']) && !$redirect->isValid($state['failure'])) {
if (!empty($state['failure']) && !$validateURL->isValid($state['failure'])) {
throw new Exception(Exception::PROJECT_INVALID_FAILURE_URL);
}
$failure = [];
@@ -1469,13 +1422,13 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$name = '';
$nameOAuth = $oauth2->getUserName($accessToken);
$userParam = $request->getParam('user');
$userParam = \json_decode($request->getParam('user'), true);
if (!empty($nameOAuth)) {
$name = $nameOAuth;
} elseif ($userParam !== null) {
$userDecoded = \json_decode($userParam, true);
if (isset($userDecoded['name']['firstName']) && isset($userDecoded['name']['lastName'])) {
$name = $userDecoded['name']['firstName'] . ' ' . $userDecoded['name']['lastName'];
} elseif (is_array($userParam)) {
$nameParam = $userParam['name'];
if (is_array($nameParam) && isset($nameParam['firstName']) && isset($nameParam['lastName'])) {
$name = $nameParam['firstName'] . ' ' . $nameParam['lastName'];
}
}
$email = $oauth2->getUserEmail($accessToken);
@@ -1793,6 +1746,8 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
$state['success']['query'] = URLParser::unparseQuery($query);
$state['success'] = URLParser::unparse($state['success']);
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), $duration));
$response
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
->addHeader('Pragma', 'no-cache')
@@ -1830,16 +1785,9 @@ App::get('/v1/account/tokens/oauth2/:provider')
->inject('response')
->inject('project')
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$port = $request->getPort();
$callbackBase = $protocol . '://' . $request->getHostname();
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$protocol = $request->getProtocol();
$callback = $callbackBase . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
$providerEnabled = $project->getAttribute('oAuthProviders', [])[$provider . 'Enabled'] ?? false;
if (!$providerEnabled) {
@@ -1864,20 +1812,12 @@ App::get('/v1/account/tokens/oauth2/:provider')
throw new Exception(Exception::PROJECT_PROVIDER_UNSUPPORTED);
}
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$redirectBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$redirectBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$redirectBase .= ':' . $port;
}
if (empty($success)) {
$success = $redirectBase . $oauthDefaultSuccess;
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
}
if (empty($failure)) {
$failure = $redirectBase . $oauthDefaultFailure;
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
}
$oauth2 = new $className($appId, $appSecret, $callback, [
@@ -2020,16 +1960,7 @@ App::post('/v1/account/tokens/magic-url')
$dbForProject->purgeCachedDocument('users', $user->getId());
if (empty($url)) {
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') === 'disabled' ? 'http' : 'https';
$host = System::getEnv('_APP_CONSOLE_DOMAIN', System::getEnv('_APP_DOMAIN', ''));
$port = $request->getPort();
$callbackBase = $protocol . '://' . $host;
if ($protocol === 'https' && $port !== '443') {
$callbackBase .= ':' . $port;
} elseif ($protocol === 'http' && $port !== '80') {
$callbackBase .= ':' . $port;
}
$url = $callbackBase . '/console/auth/magic-url';
$url = $request->getProtocol() . '://' . $request->getHostname() . '/console/auth/magic-url';
}
$url = Template::parseURL($url);
@@ -2037,7 +1968,6 @@ App::post('/v1/account/tokens/magic-url')
$url = Template::unParseURL($url);
$subject = $locale->getText("emails.magicSession.subject");
$preview = $locale->getText("emails.magicSession.preview");
$customTemplate = $project->getAttribute('templates', [])['email.magicSession-' . $locale->default] ?? [];
$detector = new Detector($request->getUserAgent('UNKNOWN'));
@@ -2126,7 +2056,6 @@ App::post('/v1/account/tokens/magic-url')
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($email)
@@ -2239,30 +2168,7 @@ App::post('/v1/account/tokens/email')
]);
$user->removeAttribute('$sequence');
$user = Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
try {
$target = Authorization::skip(fn () => $dbForProject->createDocument('targets', new Document([
'$permissions' => [
Permission::read(Role::user($user->getId())),
Permission::update(Role::user($user->getId())),
Permission::delete(Role::user($user->getId())),
],
'userId' => $user->getId(),
'userInternalId' => $user->getSequence(),
'providerType' => MESSAGE_TYPE_EMAIL,
'identifier' => $email,
])));
$user->setAttribute('targets', [...$user->getAttribute('targets', []), $target]);
} catch (Duplicate) {
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if (!$existingTarget->isEmpty()) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
$dbForProject->purgeCachedDocument('users', $user->getId());
Authorization::skip(fn () => $dbForProject->createDocument('users', $user));
}
$tokenSecret = Auth::codeGenerator(6);
@@ -2291,7 +2197,6 @@ App::post('/v1/account/tokens/email')
$dbForProject->purgeCachedDocument('users', $user->getId());
$subject = $locale->getText("emails.otpSession.subject");
$preview = $locale->getText("emails.otpSession.preview");
$customTemplate = $project->getAttribute('templates', [])['email.otpSession-' . $locale->default] ?? [];
$detector = new Detector($request->getUserAgent('UNKNOWN'));
@@ -2377,7 +2282,6 @@ App::post('/v1/account/tokens/email')
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($email)
@@ -3284,7 +3188,7 @@ App::post('/v1/account/recovery')
throw new Exception(Exception::USER_BLOCKED);
}
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY));
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_RECOVERY);
$secret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_RECOVERY);
$recovery = new Document([
@@ -3316,7 +3220,6 @@ App::post('/v1/account/recovery')
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
$body = $locale->getText("emails.recovery.body");
$subject = $locale->getText("emails.recovery.subject");
$preview = $locale->getText("emails.recovery.preview");
$customTemplate = $project->getAttribute('templates', [])['email.recovery-' . $locale->default] ?? [];
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
@@ -3391,7 +3294,6 @@ App::post('/v1/account/recovery')
->setBody($body)
->setVariables($emailVariables)
->setSubject($subject)
->setPreview($preview)
->trigger();
$recovery->setAttribute('secret', $secret);
@@ -3543,7 +3445,7 @@ App::post('/v1/account/verification')
}
$verificationSecret = Auth::tokenGenerator(Auth::TOKEN_LENGTH_VERIFICATION);
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$verification = new Document([
'$id' => ID::unique(),
@@ -3573,7 +3475,6 @@ App::post('/v1/account/verification')
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
$body = $locale->getText("emails.verification.body");
$preview = $locale->getText("emails.verification.preview");
$subject = $locale->getText("emails.verification.subject");
$customTemplate = $project->getAttribute('templates', [])['email.verification-' . $locale->default] ?? [];
@@ -3646,7 +3547,6 @@ App::post('/v1/account/verification')
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($user->getAttribute('email'))
@@ -3793,7 +3693,7 @@ App::post('/v1/account/verification/phone')
}
$secret ??= Auth::codeGenerator();
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$verification = new Document([
'$id' => ID::unique(),
@@ -4401,7 +4301,7 @@ App::post('/v1/account/mfa/challenge')
->inject('plan')
->action(function (string $factor, Response $response, Database $dbForProject, Document $user, Locale $locale, Document $project, Request $request, Event $queueForEvents, Messaging $queueForMessaging, Mail $queueForMails, callable $timelimit, StatsUsage $queueForStatsUsage, array $plan) {
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM));
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$code = Auth::codeGenerator();
$challenge = new Document([
'userId' => $user->getId(),
@@ -4492,7 +4392,6 @@ App::post('/v1/account/mfa/challenge')
}
$subject = $locale->getText("emails.mfaChallenge.subject");
$preview = $locale->getText("emails.mfaChallenge.preview");
$customTemplate = $project->getAttribute('templates', [])['email.mfaChallenge-' . $locale->default] ?? [];
$detector = new Detector($request->getUserAgent('UNKNOWN'));
@@ -4569,7 +4468,6 @@ App::post('/v1/account/mfa/challenge')
$queueForMails
->setSubject($subject)
->setPreview($preview)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($user->getAttribute('email'))
+9 -33
View File
@@ -10,7 +10,6 @@ use Appwrite\URL\URL as URLParse;
use Appwrite\Utopia\Response;
use chillerlan\QRCode\QRCode;
use chillerlan\QRCode\QROptions;
use enshrined\svgSanitize\Sanitizer as SvgSanitizer;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
@@ -363,8 +362,7 @@ App::get('/v1/avatars/favicon')
$client = new Client();
try {
$res = $client
->setAllowRedirects(true)
->setMaxRedirects(5)
->setAllowRedirects(false)
->setUserAgent(\sprintf(
APP_USERAGENT,
System::getEnv('_APP_VERSION', 'UNKNOWN'),
@@ -375,11 +373,15 @@ App::get('/v1/avatars/favicon')
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
if ($res->getStatusCode() !== 200) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
}
$doc = new DOMDocument();
$doc->strictErrorChecking = false;
@$doc->loadHTML($res->getBody());
$links = $doc->getElementsByTagName('link') ?? [];
$links = $doc->getElementsByTagName('link');
$outputHref = '';
$outputExt = '';
$space = 0;
@@ -397,12 +399,6 @@ App::get('/v1/avatars/favicon')
$ext = \pathinfo(\parse_url($absolute, PHP_URL_PATH), PATHINFO_EXTENSION);
switch ($ext) {
case 'svg':
// SVG icons are prioritized by assigning the maximum possible value.
$space = PHP_INT_MAX;
$outputHref = $absolute;
$outputExt = $ext;
break;
case 'ico':
case 'png':
case 'jpg':
@@ -441,8 +437,7 @@ App::get('/v1/avatars/favicon')
$client = new Client();
try {
$res = $client
->setAllowRedirects(true)
->setMaxRedirects(5)
->setAllowRedirects(false)
->fetch($outputHref);
} catch (\Throwable) {
throw new Exception(Exception::AVATAR_REMOTE_URL_FAILED);
@@ -454,33 +449,14 @@ App::get('/v1/avatars/favicon')
$data = $res->getBody();
if ('ico' === $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
if (
empty($data) ||
stripos($data, '<html') === 0 ||
stripos($data, '<!doc') === 0
) {
if ('ico' == $outputExt) { // Skip crop, Imagick isn\'t supporting icon files
if (empty($data) || (\mb_substr($data, 0, 5) === '<html') || \mb_substr($data, 0, 5) === '<!doc') {
throw new Exception(Exception::AVATAR_ICON_NOT_FOUND, 'Favicon not found');
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/x-icon')
->file($data);
return;
}
if ('svg' === $outputExt) { // Skip crop, Imagick isn\'t supporting svg files
$sanitizer = new SvgSanitizer();
$sanitizer->minify(true);
$cleanSvg = $sanitizer->sanitize($data);
if ($cleanSvg === false) {
throw new \Exception('SVG sanitization failed');
}
$response
->addHeader('Cache-Control', 'private, max-age=2592000') // 30 days
->setContentType('image/svg+xml')
->file($cleanSvg);
return;
}
$image = new Image($data);
+17 -6
View File
@@ -522,11 +522,17 @@ App::get('/v1/health/queue/databases')
))
->param('name', 'database_db_main', new Text(256), 'Queue name for which to check the queue size', true)
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisherDatabases')
->inject('publisher')
->inject('publisherRedis')
->inject('response')
->action(function (string $name, int|string $threshold, Publisher $publisherDatabases, Response $response) {
->action(function (string $name, int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
$threshold = \intval($threshold);
$size = $publisherDatabases->getQueueSize(new Queue($name));
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'databases');
$size = $isRedisFallback
? $publisherRedis->getQueueSize(new Queue($name))
: $publisher->getQueueSize(new Queue($name));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
@@ -653,12 +659,17 @@ App::get('/v1/health/queue/migrations')
contentType: ContentType::JSON
))
->param('threshold', 5000, new Integer(true), 'Queue size threshold. When hit (equal or higher), endpoint returns server error. Default value is 5000.', true)
->inject('publisherMigrations')
->inject('publisher')
->inject('publisherRedis')
->inject('response')
->action(function (int|string $threshold, Publisher $publisherMigrations, Response $response) {
->action(function (int|string $threshold, Publisher $publisher, ?Publisher $publisherRedis, Response $response) {
$threshold = \intval($threshold);
$size = $publisherMigrations->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
$isRedisFallback = \str_contains(System::getEnv('_APP_WORKER_REDIS_FALLBACK', ''), 'migrations');
$size = $isRedisFallback
? $publisherRedis->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME))
: $publisher->getQueueSize(new Queue(Event::MIGRATIONS_QUEUE_NAME));
if ($size >= $threshold) {
throw new Exception(Exception::HEALTH_QUEUE_SIZE_EXCEEDED, "Queue size threshold hit. Current size is {$size} and threshold is {$threshold}.");
+10 -47
View File
@@ -2176,7 +2176,7 @@ App::post('/v1/projects/:projectId/smtp/tests')
->setSmtpSenderName($senderName)
->setRecipient($email)
->setName('')
->setBodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
->setbodyTemplate(__DIR__ . '/../../config/locale/templates/email-base-styled.tpl')
->setBody($template->render())
->setVariables([])
->setSubject($subject)
@@ -2269,53 +2269,16 @@ App::get('/v1/projects/:projectId/templates/email/:type/:locale')
$localeObj = new Locale($locale);
if (is_null($template)) {
/**
* different templates, different placeholders.
*/
$templateConfigs = [
'magicSession' => [
'file' => 'email-magic-url.tpl',
'placeholders' => ['optionButton', 'buttonText', 'optionUrl', 'clientInfo', 'securityPhrase']
],
'mfaChallenge' => [
'file' => 'email-mfa-challenge.tpl',
'placeholders' => ['description', 'clientInfo']
],
'otpSession' => [
'file' => 'email-otp.tpl',
'placeholders' => ['description', 'clientInfo', 'securityPhrase']
],
'sessionAlert' => [
'file' => 'email-session-alert.tpl',
'placeholders' => ['body', 'listDevice', 'listIpAddress', 'listCountry', 'footer']
],
];
// fallback to the base template.
$config = $templateConfigs[$type] ?? [
'file' => 'email-inner-base.tpl',
'placeholders' => ['buttonText', 'body', 'footer']
];
$templateString = file_get_contents(__DIR__ . '/../../config/locale/templates/' . $config['file']);
// We use `fromString` due to the replace above
$message = Template::fromString($templateString);
// Set type-specific parameters
foreach ($config['placeholders'] as $param) {
$escapeHtml = !in_array($param, ['clientInfo', 'body', 'footer', 'description']);
$message->setParam("{{{$param}}}", $localeObj->getText("emails.{$type}.{$param}"), escapeHtml: $escapeHtml);
}
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-inner-base.tpl');
$message
// common placeholders on all the templates
->setParam('{{hello}}', $localeObj->getText("emails.{$type}.hello"))
->setParam('{{footer}}', $localeObj->getText("emails.{$type}.footer"))
->setParam('{{body}}', $localeObj->getText('emails.' . $type . '.body'), escapeHtml: false)
->setParam('{{thanks}}', $localeObj->getText("emails.{$type}.thanks"))
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"));
// `useContent: false` will strip new lines!
$message = $message->render(useContent: true);
->setParam('{{buttonText}}', $localeObj->getText("emails.{$type}.buttonText"))
->setParam('{{signature}}', $localeObj->getText("emails.{$type}.signature"))
->setParam('{{direction}}', $localeObj->getText('settings.direction'));
$message = $message->render();
$template = [
'message' => $message,
@@ -2560,7 +2523,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
->param('enabled', false, new Boolean(), 'Update authentication session invalidation status. Use this endpoint to enable or disable session invalidation on password change')
->inject('response')
->inject('dbForPlatform')
->action(function (string $projectId, bool $enabled, Response $response, Database $dbForPlatform) {
->action(function (string $projectId, bool $invalidateSessions, Response $response, Database $dbForPlatform) {
$project = $dbForPlatform->getDocument('projects', $projectId);
@@ -2569,7 +2532,7 @@ App::patch('/v1/projects/:projectId/auth/session-invalidation')
}
$auths = $project->getAttribute('auths', []);
$auths['invalidateSessions'] = $enabled;
$auths['invalidateSessions'] = $invalidateSessions;
$dbForPlatform->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
+11 -16
View File
@@ -93,7 +93,7 @@ App::post('/v1/storage/buckets')
$bucketId = $bucketId === 'unique()' ? ID::unique() : $bucketId;
// Map aggregate permissions into the multiple permissions they represent.
$permissions = Permission::aggregate($permissions) ?? [];
$permissions = Permission::aggregate($permissions);
$compression ??= Compression::NONE;
$encryption ??= true;
try {
@@ -146,7 +146,7 @@ App::post('/v1/storage/buckets')
$bucket = $dbForProject->getDocument('buckets', $bucketId);
$dbForProject->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes, permissions: $permissions, documentSecurity: $fileSecurity);
$dbForProject->createCollection('bucket_' . $bucket->getSequence(), $attributes, $indexes, permissions: $permissions ?? [], documentSecurity: $fileSecurity);
} catch (DuplicateException) {
throw new Exception(Exception::STORAGE_BUCKET_ALREADY_EXISTS);
}
@@ -967,7 +967,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Imagick extension is missing');
}
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -988,7 +987,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
if ($fileSecurity && !$valid && !$isToken) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
@@ -1159,7 +1157,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Request $request, Response $response, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -1177,10 +1175,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
@@ -1320,7 +1317,6 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
->inject('resourceToken')
->inject('deviceForFiles')
->action(function (string $bucketId, string $fileId, ?string $token, Response $response, Request $request, Database $dbForProject, string $mode, Document $resourceToken, Device $deviceForFiles) {
/* @type Document $bucket */
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
@@ -1338,10 +1334,9 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
throw new Exception(Exception::USER_UNAUTHORIZED);
}
if ($fileSecurity && !$valid && !$isToken) {
if ($fileSecurity && !$valid) {
$file = $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId);
} else {
/* @type Document $file */
$file = Authorization::skip(fn () => $dbForProject->getDocument('bucket_' . $bucket->getSequence(), $fileId));
}
@@ -1870,7 +1865,7 @@ App::get('/v1/storage/usage')
$total = [];
Authorization::skip(function () use ($dbForProject, $days, $metrics, &$stats, &$total) {
foreach ($metrics as $metric) {
$result = $dbForProject->findOne('stats', [
$result = $dbForProject->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -1899,7 +1894,7 @@ App::get('/v1/storage/usage')
};
foreach ($metrics as $metric) {
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
@@ -1917,8 +1912,8 @@ App::get('/v1/storage/usage')
'filesTotal' => $usage[$metrics[1]]['total'],
'filesStorageTotal' => $usage[$metrics[2]]['total'],
'buckets' => $usage[$metrics[0]]['data'],
'files' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
'files' => $usage[$metrics[1]]['data'],
'storage' => $usage[$metrics[2]]['data'],
]), Response::MODEL_USAGE_STORAGE);
});
@@ -1970,7 +1965,7 @@ App::get('/v1/storage/:bucketId/usage')
? $dbForLogs
: $dbForProject;
$result = $db->findOne('stats', [
$result = $db->findOne('stats', [
Query::equal('metric', [$metric]),
Query::equal('period', ['inf'])
]);
@@ -2000,7 +1995,7 @@ App::get('/v1/storage/:bucketId/usage')
};
foreach ($metrics as $metric) {
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['total'] = $stats[$metric]['total'];
$usage[$metric]['data'] = [];
$leap = time() - ($days['limit'] * $days['factor']);
while ($leap < time()) {
-2
View File
@@ -657,7 +657,6 @@ App::post('/v1/teams/:teamId/memberships')
$projectName = $project->isEmpty() ? 'Console' : $project->getAttribute('name', '[APP-NAME]');
$body = $locale->getText("emails.invitation.body");
$preview = $locale->getText("emails.invitation.preview");
$subject = \sprintf($locale->getText("emails.invitation.subject"), $team->getAttribute('name'), $projectName);
$customTemplate = $project->getAttribute('templates', [])['email.invitation-' . $locale->default] ?? [];
@@ -730,7 +729,6 @@ App::post('/v1/teams/:teamId/memberships')
$queueForMails
->setSubject($subject)
->setBody($body)
->setPreview($preview)
->setRecipient($invitee->getAttribute('email'))
->setName($invitee->getAttribute('name', ''))
->setVariables($emailVariables)
+4 -6
View File
@@ -1137,7 +1137,6 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:pro
$repository['pushedAt'] = $repository['pushed_at'] ?? '';
$repository['organization'] = $installation->getAttribute('organization', '');
$repository['provider'] = $installation->getAttribute('provider', '');
$repository['defaultBranch'] = $repository['default_branch'] ?? '';
$response->dynamic(new Document($repository), Response::MODEL_PROVIDER_REPOSITORY);
});
@@ -1235,8 +1234,7 @@ App::post('/v1/vcs/github/events')
$providerRepositoryUrl = $parsedPayload["repositoryUrl"] ?? '';
$providerCommitHash = $parsedPayload["commitHash"] ?? '';
$providerRepositoryOwner = $parsedPayload["owner"] ?? '';
$providerCommitAuthorName = $parsedPayload["headCommitAuthorName"] ?? '';
$providerCommitAuthorEmail = $parsedPayload["headCommitAuthorEmail"] ?? '';
$providerCommitAuthor = $parsedPayload["headCommitAuthor"] ?? '';
$providerCommitAuthorUrl = $parsedPayload["authorUrl"] ?? '';
$providerCommitMessage = $parsedPayload["headCommitMessage"] ?? '';
$providerCommitUrl = $parsedPayload["headCommitUrl"] ?? '';
@@ -1249,9 +1247,9 @@ App::post('/v1/vcs/github/events')
Query::limit(100),
]));
// create new deployment only on push (not committed by us) and not when branch is created or deleted
if ($providerCommitAuthorEmail !== APP_VCS_GITHUB_EMAIL && !$providerBranchCreated && !$providerBranchDeleted) {
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthorName, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request);
// create new deployment only on push and not when branch is created or deleted
if (!$providerBranchCreated && !$providerBranchDeleted) {
$createGitDeployments($github, $providerInstallationId, $repositories, $providerBranch, $providerBranchUrl, $providerRepositoryName, $providerRepositoryUrl, $providerRepositoryOwner, $providerCommitHash, $providerCommitAuthor, $providerCommitAuthorUrl, $providerCommitMessage, $providerCommitUrl, '', false, $dbForPlatform, $queueForBuilds, $getProjectDB, $request);
}
} elseif ($event == $github::EVENT_INSTALLATION) {
if ($parsedPayload["action"] == "deleted") {
+5 -25
View File
@@ -854,7 +854,7 @@ App::init()
}
if (version_compare($requestFormat, '1.8.0', '<')) {
$dbForProject = $getProjectDB($project);
$request->addFilter(new RequestV20($dbForProject, $request->getParams()));
$request->addFilter(new RequestV20($dbForProject, $route));
}
}
@@ -975,8 +975,6 @@ App::init()
)
);
$warnings = [];
/*
* Response format
*/
@@ -998,7 +996,7 @@ App::init()
$response->addFilter(new ResponseV20());
}
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
$warnings[] = "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks";
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
}
}
@@ -1035,22 +1033,6 @@ App::init()
$response->addHeader('Access-Control-Allow-Origin', '*');
}
/**
* Deprecation Warning
*/
$sdk = $route->getLabel('sdk', false);
$deprecationWarning = 'This route is deprecated. See the updated documentation for improved compatibility and migration details.';
$sdkItems = is_array($sdk) ? $sdk : (!empty($sdk) ? [$sdk] : []);
foreach ($sdkItems as $sdkItem) {
if ($sdkItem->isDeprecated()) {
$warnings[] = $deprecationWarning;
}
}
if (!empty($warnings)) {
$response->addHeader('X-Appwrite-Warning', implode(';', $warnings));
}
/*
* Validate Client Domain - Check to avoid CSRF attack
* Adding Appwrite API domains to allow XDOMAIN communication
@@ -1287,7 +1269,7 @@ App::error()
$action = 'UNKNOWN_NAMESPACE.UNKNOWN.METHOD';
if (!empty($sdk)) {
/** @var \Appwrite\SDK\Method $sdk */
/** @var Appwrite\SDK\Method $sdk */
$action = $sdk->getNamespace() . '.' . $sdk->getMethodName();
}
@@ -1407,10 +1389,9 @@ App::get('/robots.txt')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
@@ -1441,10 +1422,9 @@ App::get('/humans.txt')
->inject('apiKey')
->action(function (App $utopia, SwooleRequest $swooleRequest, Request $request, Response $response, Log $log, Database $dbForPlatform, callable $getProjectDB, Event $queueForEvents, StatsUsage $queueForStatsUsage, Func $queueForFunctions, Executor $executor, Reader $geodb, callable $isResourceBlocked, string $previewHostname, ?Key $apiKey) {
$host = $request->getHostname() ?? '';
$consoleDomain = System::getEnv('_APP_CONSOLE_DOMAIN', '');
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if (($host === $consoleDomain || $host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
if (($host === $mainDomain || $host === 'localhost') && empty($previewHostname)) {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
+26
View File
@@ -129,6 +129,32 @@ App::get('/v1/mock/tests/general/oauth2/failure')
]);
});
App::patch('/v1/mock/functions-v2')
->desc('Update Function Version to V2 (outdated code syntax)')
->groups(['mock', 'api', 'functions'])
->label('scope', 'functions.write')
->label('docs', false)
->param('functionId', '', new UID(), 'Function ID.')
->inject('response')
->inject('dbForProject')
->action(function (string $functionId, Response $response, Database $dbForProject) {
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
}
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$dbForProject->updateDocument('functions', $function->getId(), $function->setAttribute('version', 'v2'));
$response->noContent();
});
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
+4 -8
View File
@@ -31,7 +31,6 @@ use Utopia\Database\Helpers\Role;
use Utopia\Database\Validator\Authorization;
use Utopia\Queue\Publisher;
use Utopia\System\System;
use Utopia\Telemetry\Adapter as Telemetry;
use Utopia\Validator\WhiteList;
$parseLabel = function (string $label, array $responsePayload, array $requestParams, Document $user) {
@@ -422,8 +421,7 @@ App::init()
->inject('apiKey')
->inject('plan')
->inject('devKey')
->inject('telemetry')
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey, Telemetry $telemetry) use ($usageDatabaseListener, $eventDatabaseListener) {
->action(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Publisher $publisher, Event $queueForEvents, Messaging $queueForMessaging, Audit $queueForAudits, Delete $queueForDeletes, EventDatabase $queueForDatabase, Build $queueForBuilds, StatsUsage $queueForStatsUsage, Database $dbForProject, callable $timelimit, Document $resourceToken, string $mode, ?Key $apiKey, array $plan, Document $devKey) use ($usageDatabaseListener, $eventDatabaseListener) {
$route = $utopia->getRoute();
@@ -556,7 +554,6 @@ App::init()
));
$useCache = $route->getLabel('cache', false);
$storageCacheOperationsCounter = $telemetry->createCounter('storage.cache.operations.load');
if ($useCache) {
$route = $utopia->match($request);
$isImageTransformation = $route->getPath() === '/v1/storage/buckets/:bucketId/files/:fileId/preview';
@@ -579,6 +576,7 @@ App::init()
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$isToken = !$resourceToken->isEmpty() && $resourceToken->getAttribute('bucketInternalId') === $bucket->getSequence();
$isAPIKey = Auth::isAppUser(Authorization::getRoles());
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
if ($bucket->isEmpty() || (!$bucket->getAttribute('enabled') && !$isAppUser && !$isPrivilegedUser)) {
@@ -622,12 +620,10 @@ App::init()
->addHeader('Cache-Control', sprintf('private, max-age=%d', $timestamp))
->addHeader('X-Appwrite-Cache', 'hit')
->setContentType($cacheLog->getAttribute('mimeType'));
$storageCacheOperationsCounter->add(1, ['result' => 'hit']);
if (!$isImageTransformation || !$isDisabled) {
$response->send($data);
}
} else {
$storageCacheOperationsCounter->add(1, ['result' => 'miss']);
$response
->addHeader('Cache-Control', 'no-cache, no-store, must-revalidate')
->addHeader('Pragma', 'no-cache')
@@ -805,8 +801,8 @@ App::shutdown()
Console::info("Triggering database event: \n" . \json_encode([
'projectId' => $project->getId(),
'databaseId' => $queueForDatabase->getDatabase()?->getId(),
'tableId' => $queueForDatabase->getTable()?->getId() ?? $queueForDatabase->getCollection()?->getId(),
'rowId' => $queueForDatabase->getRow()?->getId() ?? $queueForDatabase->getDocument()?->getId(),
'collectionId' => $queueForDatabase->getCollection()?->getId(),
'documentId' => $queueForDatabase->getDocument()?->getId(),
]));
$queueForDatabase->trigger();
}
+2 -4
View File
@@ -37,8 +37,8 @@ const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_RESOURCE_TOKEN_ACCESS = 24 * 60 * 60; // 24 hours
const APP_FILE_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4321;
const APP_VERSION_STABLE = '1.8.0';
const APP_CACHE_BUSTER = 4320;
const APP_VERSION_STABLE = '1.7.4';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -80,8 +80,6 @@ const APP_COMPUTE_SPECIFICATION_DEFAULT = Specification::S_1VCPU_512MB;
const APP_PLATFORM_SERVER = 'server';
const APP_PLATFORM_CLIENT = 'client';
const APP_PLATFORM_CONSOLE = 'console';
const APP_VCS_GITHUB_USERNAME = 'Appwrite';
const APP_VCS_GITHUB_EMAIL = 'team@appwrite.io';
// Database Reconnect
const DATABASE_RECONNECT_SLEEP = 2;
+12 -33
View File
@@ -80,27 +80,15 @@ App::setResource('localeCodes', function () {
App::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
App::setResource('publisherDatabases', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
App::setResource('publisherMigrations', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
App::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
App::setResource('publisherRedis', function () {
// Stub
});
App::setResource('consumer', function (Group $pools) {
return new BrokerPool(consumer: $pools->get('consumer'));
}, ['pools']);
App::setResource('consumerDatabases', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
App::setResource('consumerMigrations', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
App::setResource('consumerStatsUsage', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
App::setResource('consumerRedis', function () {
// Stub
});
App::setResource('queueForMessaging', function (Publisher $publisher) {
return new Messaging($publisher);
}, ['publisher']);
@@ -234,12 +222,12 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
Auth::$unique = $session['id'] ?? '';
Auth::$secret = $session['secret'] ?? '';
$user = new Document([]);
if (!empty(Auth::$unique)) {
if ($mode === APP_MODE_ADMIN) {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} elseif (!$project->isEmpty()) {
if ($mode === APP_MODE_ADMIN) {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} else {
if ($project->isEmpty()) {
$user = new Document([]);
} else {
if ($project->getId() === 'console') {
$user = $dbForPlatform->getDocument('users', Auth::$unique);
} else {
@@ -849,20 +837,11 @@ App::setResource('team', function (Document $project, Database $dbForPlatform, A
$teamInternalId = $p->getAttribute('teamInternalId', '');
} elseif ($path === '/v1/projects') {
$teamId = $request->getParam('teamId', '');
if (empty($teamId)) {
return new Document([]);
}
$team = Authorization::skip(fn () => $dbForPlatform->getDocument('teams', $teamId));
return $team;
}
}
if (empty($teamInternalId)) {
return new Document([]);
}
$team = Authorization::skip(function () use ($dbForPlatform, $teamInternalId) {
return $dbForPlatform->findOne('teams', [
Query::equal('$sequence', [$teamInternalId]),
+1 -1
View File
@@ -177,7 +177,7 @@ $image = $this->getParam('image', '');
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:6.2.0
image: <?php echo $organization; ?>/console:6.0.13
restart: unless-stopped
networks:
- appwrite
+6 -22
View File
@@ -247,33 +247,17 @@ Server::setResource('publisher', function (Group $pools) {
return new BrokerPool(publisher: $pools->get('publisher'));
}, ['pools']);
Server::setResource('publisherDatabases', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
Server::setResource('publisherMigrations', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
Server::setResource('publisherStatsUsage', function (BrokerPool $publisher) {
return $publisher;
}, ['publisher']);
Server::setResource('publisherRedis', function () {
// Stub
});
Server::setResource('consumer', function (Group $pools) {
return new BrokerPool(consumer: $pools->get('consumer'));
}, ['pools']);
Server::setResource('consumerDatabases', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
Server::setResource('consumerMigrations', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
Server::setResource('consumerStatsUsage', function (BrokerPool $consumer) {
return $consumer;
}, ['consumer']);
Server::setResource('consumerRedis', function () {
// Stub
});
Server::setResource('queueForStatsUsage', function (Publisher $publisher) {
return new StatsUsage($publisher);
+7 -8
View File
@@ -12,8 +12,8 @@
],
"scripts": {
"test": "vendor/bin/phpunit",
"lint": "vendor/bin/pint --test --config pint.json",
"format": "vendor/bin/pint --config pint.json",
"lint": "vendor/bin/pint --test",
"format": "vendor/bin/pint",
"bench": "vendor/bin/phpbench run --report=benchmark",
"check": "./vendor/bin/phpstan analyse -c phpstan.neon"
},
@@ -52,7 +52,7 @@
"utopia-php/cache": "0.13.*",
"utopia-php/cli": "0.15.*",
"utopia-php/config": "0.2.*",
"utopia-php/database": "0.75.*",
"utopia-php/database": "0.71.*",
"utopia-php/detector": "0.1.*",
"utopia-php/domains": "0.8.*",
"utopia-php/dsn": "0.2.1",
@@ -62,7 +62,7 @@
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.18.*",
"utopia-php/migration": "0.14.*",
"utopia-php/migration": "0.10.*",
"utopia-php/orchestration": "0.9.*",
"utopia-php/platform": "0.7.*",
"utopia-php/pools": "0.8.*",
@@ -73,7 +73,7 @@
"utopia-php/swoole": "0.8.*",
"utopia-php/system": "0.9.*",
"utopia-php/telemetry": "0.1.*",
"utopia-php/vcs": "0.11.*",
"utopia-php/vcs": "0.10.*",
"utopia-php/websocket": "0.3.*",
"matomo/device-detector": "6.1.*",
"dragonmantank/cron-expression": "3.3.*",
@@ -82,12 +82,11 @@
"adhocore/jwt": "1.1.*",
"spomky-labs/otphp": "^10.0",
"webonyx/graphql-php": "14.11.*",
"league/csv": "9.14.*",
"enshrined/svg-sanitize": "0.21.*"
"league/csv": "9.14.*"
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.*.*",
"appwrite/sdk-generator": "0.41.*",
"phpunit/phpunit": "9.*",
"swoole/ide-helper": "5.1.2",
"phpstan/phpstan": "1.8.*",
Generated
+108 -180
View File
@@ -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": "ced183858d8fde850249c5c34f6f1b73",
"content-hash": "55bc52686a08d64930e6af7411ac0654",
"packages": [
{
"name": "adhocore/jwt",
@@ -69,16 +69,16 @@
},
{
"name": "appwrite/appwrite",
"version": "15.1.0",
"version": "11.1.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-for-php.git",
"reference": "c438b3885071ac7c0329199dce5e6f6a24dd215b"
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/c438b3885071ac7c0329199dce5e6f6a24dd215b",
"reference": "c438b3885071ac7c0329199dce5e6f6a24dd215b",
"url": "https://api.github.com/repos/appwrite/sdk-for-php/zipball/1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"reference": "1d043f543acdb17b9fdb440b1b2dd208e400bad3",
"shasum": ""
},
"require": {
@@ -104,10 +104,10 @@
"support": {
"email": "team@appwrite.io",
"issues": "https://github.com/appwrite/sdk-for-php/issues",
"source": "https://github.com/appwrite/sdk-for-php/tree/15.1.0",
"source": "https://github.com/appwrite/sdk-for-php/tree/11.1.0",
"url": "https://appwrite.io/support"
},
"time": "2025-08-01T04:50:51+00:00"
"time": "2024-06-26T07:03:23+00:00"
},
{
"name": "appwrite/php-clamav",
@@ -628,51 +628,6 @@
],
"time": "2023-08-10T19:36:49+00:00"
},
{
"name": "enshrined/svg-sanitize",
"version": "0.21.0",
"source": {
"type": "git",
"url": "https://github.com/darylldoyle/svg-sanitizer.git",
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/darylldoyle/svg-sanitizer/zipball/5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"reference": "5e477468fac5c5ce933dce53af3e8e4e58dcccc9",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"php": "^7.1 || ^8.0"
},
"require-dev": {
"phpunit/phpunit": "^6.5 || ^8.5"
},
"type": "library",
"autoload": {
"psr-4": {
"enshrined\\svgSanitize\\": "src"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"GPL-2.0-or-later"
],
"authors": [
{
"name": "Daryll Doyle",
"email": "daryll@enshrined.co.uk"
}
],
"description": "An SVG sanitizer for PHP",
"support": {
"issues": "https://github.com/darylldoyle/svg-sanitizer/issues",
"source": "https://github.com/darylldoyle/svg-sanitizer/tree/0.21.0"
},
"time": "2025-01-13T09:32:25+00:00"
},
{
"name": "giggsey/libphonenumber-for-php-lite",
"version": "8.13.36",
@@ -1228,16 +1183,16 @@
},
{
"name": "open-telemetry/context",
"version": "1.3.0",
"version": "1.2.1",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/context.git",
"reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc"
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc",
"reference": "4d5d98f1d4311a55b8d07e3d4c06d2430b4e6efc",
"url": "https://api.github.com/repos/opentelemetry-php/context/zipball/1eb2b837ee9362db064a6b65d5ecce15a9f9f020",
"reference": "1eb2b837ee9362db064a6b65d5ecce15a9f9f020",
"shasum": ""
},
"require": {
@@ -1283,7 +1238,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-08-04T03:25:06+00:00"
"time": "2025-05-07T23:36:50+00:00"
},
{
"name": "open-telemetry/exporter-otlp",
@@ -1414,16 +1369,16 @@
},
{
"name": "open-telemetry/sdk",
"version": "1.7.0",
"version": "1.6.0",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sdk.git",
"reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac"
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/86287cf30fd6549444d7b8f7d8758d92e24086ac",
"reference": "86287cf30fd6549444d7b8f7d8758d92e24086ac",
"url": "https://api.github.com/repos/opentelemetry-php/sdk/zipball/1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
"reference": "1c0371794e4c0700afd4a9d4d8511cb5e3f78e6a",
"shasum": ""
},
"require": {
@@ -1442,7 +1397,7 @@
"ramsey/uuid": "^3.0 || ^4.0",
"symfony/polyfill-mbstring": "^1.23",
"symfony/polyfill-php82": "^1.26",
"tbachert/spi": "^1.0.5"
"tbachert/spi": "^1.0.1"
},
"suggest": {
"ext-gmp": "To support unlimited number of synchronous metric readers",
@@ -1456,9 +1411,6 @@
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderHttpConfig",
"OpenTelemetry\\API\\Instrumentation\\Configuration\\General\\ConfigEnv\\EnvComponentLoaderPeerConfig"
],
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\ResolverInterface": [
"OpenTelemetry\\SDK\\Common\\Configuration\\Resolver\\SdkConfigurationResolver"
],
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\HookManagerInterface": [
"OpenTelemetry\\API\\Instrumentation\\AutoInstrumentation\\ExtensionHookManager"
]
@@ -1507,20 +1459,20 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-08-06T03:07:06+00:00"
"time": "2025-06-19T23:36:51+00:00"
},
{
"name": "open-telemetry/sem-conv",
"version": "1.36.0",
"version": "1.32.1",
"source": {
"type": "git",
"url": "https://github.com/opentelemetry-php/sem-conv.git",
"reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a"
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/60dd18fd21d45e6f4234ecab89c14021b6e3de9a",
"reference": "60dd18fd21d45e6f4234ecab89c14021b6e3de9a",
"url": "https://api.github.com/repos/opentelemetry-php/sem-conv/zipball/94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
"reference": "94daa85ea61a8e2b7e1b0af6be0e875bedda7c22",
"shasum": ""
},
"require": {
@@ -1564,7 +1516,7 @@
"issues": "https://github.com/open-telemetry/opentelemetry-php/issues",
"source": "https://github.com/open-telemetry/opentelemetry-php"
},
"time": "2025-08-04T03:22:08+00:00"
"time": "2025-06-24T02:32:27+00:00"
},
{
"name": "paragonie/constant_time_encoding",
@@ -2595,16 +2547,16 @@
},
{
"name": "symfony/http-client",
"version": "v7.3.2",
"version": "v7.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/http-client.git",
"reference": "1c064a0c67749923483216b081066642751cc2c7"
"reference": "4403d87a2c16f33345dca93407a8714ee8c05a64"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/http-client/zipball/1c064a0c67749923483216b081066642751cc2c7",
"reference": "1c064a0c67749923483216b081066642751cc2c7",
"url": "https://api.github.com/repos/symfony/http-client/zipball/4403d87a2c16f33345dca93407a8714ee8c05a64",
"reference": "4403d87a2c16f33345dca93407a8714ee8c05a64",
"shasum": ""
},
"require": {
@@ -2670,7 +2622,7 @@
"http"
],
"support": {
"source": "https://github.com/symfony/http-client/tree/v7.3.2"
"source": "https://github.com/symfony/http-client/tree/v7.3.1"
},
"funding": [
{
@@ -2681,16 +2633,12 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-06-28T07:58:39+00:00"
},
{
"name": "symfony/http-client-contracts",
@@ -3545,16 +3493,16 @@
},
{
"name": "utopia-php/database",
"version": "0.75.2",
"version": "0.71.9",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "669f38dc1686fbbbafbdadc266e0c3c112d8a70c"
"reference": "eb2f759020bba617e99dd67973a9bd949b47f54e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/669f38dc1686fbbbafbdadc266e0c3c112d8a70c",
"reference": "669f38dc1686fbbbafbdadc266e0c3c112d8a70c",
"url": "https://api.github.com/repos/utopia-php/database/zipball/eb2f759020bba617e99dd67973a9bd949b47f54e",
"reference": "eb2f759020bba617e99dd67973a9bd949b47f54e",
"shasum": ""
},
"require": {
@@ -3595,9 +3543,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.75.2"
"source": "https://github.com/utopia-php/database/tree/0.71.9"
},
"time": "2025-08-05T08:33:18+00:00"
"time": "2025-07-02T16:37:41+00:00"
},
{
"name": "utopia-php/detector",
@@ -3994,16 +3942,16 @@
},
{
"name": "utopia-php/messaging",
"version": "0.18.2",
"version": "0.18.1",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/messaging.git",
"reference": "0d364edacf4d4867964c7e17f653031dd39394bf"
"reference": "5d1245207a61d7ca065daddad7ac5f1d5640152f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/0d364edacf4d4867964c7e17f653031dd39394bf",
"reference": "0d364edacf4d4867964c7e17f653031dd39394bf",
"url": "https://api.github.com/repos/utopia-php/messaging/zipball/5d1245207a61d7ca065daddad7ac5f1d5640152f",
"reference": "5d1245207a61d7ca065daddad7ac5f1d5640152f",
"shasum": ""
},
"require": {
@@ -4039,26 +3987,26 @@
],
"support": {
"issues": "https://github.com/utopia-php/messaging/issues",
"source": "https://github.com/utopia-php/messaging/tree/0.18.2"
"source": "https://github.com/utopia-php/messaging/tree/0.18.1"
},
"time": "2025-07-21T18:27:03+00:00"
"time": "2025-06-26T18:26:07+00:00"
},
{
"name": "utopia-php/migration",
"version": "0.14.2",
"version": "0.10.4",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/migration.git",
"reference": "025b9953aca5c34dbb4afacacf9ef0e3159e42be"
"reference": "0c85917482db172b3ccdc0704e42af3c1cc89361"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/025b9953aca5c34dbb4afacacf9ef0e3159e42be",
"reference": "025b9953aca5c34dbb4afacacf9ef0e3159e42be",
"url": "https://api.github.com/repos/utopia-php/migration/zipball/0c85917482db172b3ccdc0704e42af3c1cc89361",
"reference": "0c85917482db172b3ccdc0704e42af3c1cc89361",
"shasum": ""
},
"require": {
"appwrite/appwrite": "15.*",
"appwrite/appwrite": "11.*",
"ext-curl": "*",
"ext-openssl": "*",
"php": ">=8.1",
@@ -4095,9 +4043,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/migration/issues",
"source": "https://github.com/utopia-php/migration/tree/0.14.2"
"source": "https://github.com/utopia-php/migration/tree/0.10.4"
},
"time": "2025-08-07T04:52:14+00:00"
"time": "2025-07-02T18:31:09+00:00"
},
{
"name": "utopia-php/orchestration",
@@ -4639,16 +4587,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.11.0",
"version": "0.10.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "0e665eaa7d906168525bf6aac50b6bcc3e4fe528"
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/0e665eaa7d906168525bf6aac50b6bcc3e4fe528",
"reference": "0e665eaa7d906168525bf6aac50b6bcc3e4fe528",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/b358439dc387f6097019eb83ebb9fc258fe9da05",
"reference": "b358439dc387f6097019eb83ebb9fc258fe9da05",
"shasum": ""
},
"require": {
@@ -4682,9 +4630,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.11.0"
"source": "https://github.com/utopia-php/vcs/tree/0.10.5"
},
"time": "2025-07-23T13:54:58+00:00"
"time": "2025-06-10T15:01:16+00:00"
},
{
"name": "utopia-php/websocket",
@@ -4862,16 +4810,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.41.29",
"version": "0.41.11",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "4af563f3b0879747efc8434eb8ed8bf97e75039f"
"reference": "60122cb613a5a1c82667ecc2217e351654a8d404"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/4af563f3b0879747efc8434eb8ed8bf97e75039f",
"reference": "4af563f3b0879747efc8434eb8ed8bf97e75039f",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/60122cb613a5a1c82667ecc2217e351654a8d404",
"reference": "60122cb613a5a1c82667ecc2217e351654a8d404",
"shasum": ""
},
"require": {
@@ -4907,9 +4855,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/0.41.29"
"source": "https://github.com/appwrite/sdk-generator/tree/0.41.11"
},
"time": "2025-08-04T04:34:45+00:00"
"time": "2025-07-04T09:56:24+00:00"
},
{
"name": "doctrine/annotations",
@@ -5136,16 +5084,16 @@
},
{
"name": "laravel/pint",
"version": "v1.24.0",
"version": "v1.23.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a"
"reference": "9ab851dba4faa51a3c3223dd3d07044129021024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"reference": "0345f3b05f136801af8c339f9d16ef29e6b4df8a",
"url": "https://api.github.com/repos/laravel/pint/zipball/9ab851dba4faa51a3c3223dd3d07044129021024",
"reference": "9ab851dba4faa51a3c3223dd3d07044129021024",
"shasum": ""
},
"require": {
@@ -5156,7 +5104,7 @@
"php": "^8.2.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.82.2",
"friendsofphp/php-cs-fixer": "^3.76.0",
"illuminate/view": "^11.45.1",
"larastan/larastan": "^3.5.0",
"laravel-zero/framework": "^11.45.0",
@@ -5201,7 +5149,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2025-07-10T18:09:32+00:00"
"time": "2025-07-03T10:37:47+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -5328,16 +5276,16 @@
},
{
"name": "myclabs/deep-copy",
"version": "1.13.4",
"version": "1.13.1",
"source": {
"type": "git",
"url": "https://github.com/myclabs/DeepCopy.git",
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a"
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a",
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c",
"reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c",
"shasum": ""
},
"require": {
@@ -5376,7 +5324,7 @@
],
"support": {
"issues": "https://github.com/myclabs/DeepCopy/issues",
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.4"
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.1"
},
"funding": [
{
@@ -5384,20 +5332,20 @@
"type": "tidelift"
}
],
"time": "2025-08-01T08:46:24+00:00"
"time": "2025-04-29T12:36:36+00:00"
},
{
"name": "nikic/php-parser",
"version": "v5.6.0",
"version": "v5.5.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56"
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/221b0d0fdf1369c71047ad1d18bb5880017bbc56",
"reference": "221b0d0fdf1369c71047ad1d18bb5880017bbc56",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
"reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
"shasum": ""
},
"require": {
@@ -5440,9 +5388,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.6.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0"
},
"time": "2025-07-27T20:03:57+00:00"
"time": "2025-05-31T08:24:38+00:00"
},
{
"name": "phar-io/manifest",
@@ -7310,16 +7258,16 @@
},
{
"name": "symfony/console",
"version": "v7.3.2",
"version": "v7.3.1",
"source": {
"type": "git",
"url": "https://github.com/symfony/console.git",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1"
"reference": "9e27aecde8f506ba0fd1d9989620c04a87697101"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/console/zipball/5f360ebc65c55265a74d23d7fe27f957870158a1",
"reference": "5f360ebc65c55265a74d23d7fe27f957870158a1",
"url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101",
"reference": "9e27aecde8f506ba0fd1d9989620c04a87697101",
"shasum": ""
},
"require": {
@@ -7384,7 +7332,7 @@
"terminal"
],
"support": {
"source": "https://github.com/symfony/console/tree/v7.3.2"
"source": "https://github.com/symfony/console/tree/v7.3.1"
},
"funding": [
{
@@ -7395,29 +7343,25 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-30T17:13:41+00:00"
"time": "2025-06-27T19:55:54+00:00"
},
{
"name": "symfony/filesystem",
"version": "v7.3.2",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/filesystem.git",
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd"
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd",
"reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd",
"url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb",
"shasum": ""
},
"require": {
@@ -7454,7 +7398,7 @@
"description": "Provides basic utilities for the filesystem",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/filesystem/tree/v7.3.2"
"source": "https://github.com/symfony/filesystem/tree/v7.3.0"
},
"funding": [
{
@@ -7465,29 +7409,25 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-07T08:17:47+00:00"
"time": "2024-10-25T15:15:23+00:00"
},
{
"name": "symfony/finder",
"version": "v7.3.2",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/finder.git",
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe"
"reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe",
"reference": "2a6614966ba1074fa93dae0bc804227422df4dfe",
"url": "https://api.github.com/repos/symfony/finder/zipball/ec2344cf77a48253bbca6939aa3d2477773ea63d",
"reference": "ec2344cf77a48253bbca6939aa3d2477773ea63d",
"shasum": ""
},
"require": {
@@ -7522,7 +7462,7 @@
"description": "Finds files and directories via an intuitive fluent interface",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/finder/tree/v7.3.2"
"source": "https://github.com/symfony/finder/tree/v7.3.0"
},
"funding": [
{
@@ -7533,29 +7473,25 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-15T13:41:35+00:00"
"time": "2024-12-30T19:00:26+00:00"
},
{
"name": "symfony/options-resolver",
"version": "v7.3.2",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/options-resolver.git",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37"
"reference": "afb9a8038025e5dbc657378bfab9198d75f10fca"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/119bcf13e67dbd188e5dbc74228b1686f66acd37",
"reference": "119bcf13e67dbd188e5dbc74228b1686f66acd37",
"url": "https://api.github.com/repos/symfony/options-resolver/zipball/afb9a8038025e5dbc657378bfab9198d75f10fca",
"reference": "afb9a8038025e5dbc657378bfab9198d75f10fca",
"shasum": ""
},
"require": {
@@ -7593,7 +7529,7 @@
"options"
],
"support": {
"source": "https://github.com/symfony/options-resolver/tree/v7.3.2"
"source": "https://github.com/symfony/options-resolver/tree/v7.3.0"
},
"funding": [
{
@@ -7604,16 +7540,12 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-15T11:36:08+00:00"
"time": "2025-04-04T13:12:05+00:00"
},
{
"name": "symfony/polyfill-ctype",
@@ -7992,16 +7924,16 @@
},
{
"name": "symfony/string",
"version": "v7.3.2",
"version": "v7.3.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/string.git",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca"
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/string/zipball/42f505aff654e62ac7ac2ce21033818297ca89ca",
"reference": "42f505aff654e62ac7ac2ce21033818297ca89ca",
"url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125",
"reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125",
"shasum": ""
},
"require": {
@@ -8059,7 +7991,7 @@
"utf8"
],
"support": {
"source": "https://github.com/symfony/string/tree/v7.3.2"
"source": "https://github.com/symfony/string/tree/v7.3.0"
},
"funding": [
{
@@ -8070,16 +8002,12 @@
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://github.com/nicolas-grekas",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2025-07-10T08:47:49+00:00"
"time": "2025-04-20T20:19:01+00:00"
},
{
"name": "textalk/websocket",
+2 -3
View File
@@ -214,7 +214,7 @@ services:
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:6.2.0
image: appwrite/console:6.1.2
restart: unless-stopped
networks:
- appwrite
@@ -456,7 +456,6 @@ services:
- redis
- mariadb
environment:
- _APP_BROWSER_HOST
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
@@ -978,7 +977,7 @@ services:
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES,$_APP_SITES_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v5
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v5
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
@@ -4,7 +4,9 @@ import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
.setSession("") // The user session to authenticate with
.setKey("") //
.setJWT("<YOUR_JWT>"); // Your secret JSON Web Token
Databases databases = new Databases(client);
@@ -1,27 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.decrementDocumentAttribute(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
"", // attribute
0, // value (optional)
0, // min (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -1,27 +0,0 @@
import io.appwrite.Client;
import io.appwrite.coroutines.CoroutineCallback;
import io.appwrite.services.Databases;
Client client = new Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>"); // Your project ID
Databases databases = new Databases(client);
databases.incrementDocumentAttribute(
"<DATABASE_ID>", // databaseId
"<COLLECTION_ID>", // collectionId
"<DOCUMENT_ID>", // documentId
"", // attribute
0, // value (optional)
0, // max (optional)
new CoroutineCallback<>((result, error) -> {
if (error != null) {
error.printStackTrace();
return;
}
Log.d("Appwrite", result.toString());
})
);
@@ -4,7 +4,9 @@ import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setSession("") // The user session to authenticate with
.setKey("") //
.setJWT("<YOUR_JWT>") // Your secret JSON Web Token
val databases = Databases(client)
@@ -1,18 +0,0 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.decrementDocumentAttribute(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
documentId = "<DOCUMENT_ID>",
attribute = "",
value = 0, // (optional)
min = 0, // (optional)
)
@@ -1,18 +0,0 @@
import io.appwrite.Client
import io.appwrite.coroutines.CoroutineCallback
import io.appwrite.services.Databases
val client = Client(context)
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
val databases = Databases(client)
val result = databases.incrementDocumentAttribute(
databaseId = "<DATABASE_ID>",
collectionId = "<COLLECTION_ID>",
documentId = "<DOCUMENT_ID>",
attribute = "",
value = 0, // (optional)
max = 0, // (optional)
)
@@ -2,7 +2,9 @@ import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
.setSession("") // The user session to authenticate with
.setKey("") //
.setJWT("<YOUR_JWT>") // Your secret JSON Web Token
let databases = Databases(client)
@@ -1,17 +0,0 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let databases = Databases(client)
let document = try await databases.decrementDocumentAttribute(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documentId: "<DOCUMENT_ID>",
attribute: "",
value: 0, // optional
min: 0 // optional
)
@@ -1,17 +0,0 @@
import Appwrite
let client = Client()
.setEndpoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.setProject("<YOUR_PROJECT_ID>") // Your project ID
let databases = Databases(client)
let document = try await databases.incrementDocumentAttribute(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documentId: "<DOCUMENT_ID>",
attribute: "",
value: 0, // optional
max: 0 // optional
)
@@ -2,7 +2,9 @@ import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
.setSession('') // The user session to authenticate with
.setKey('') //
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
Databases databases = Databases(client);
@@ -1,16 +0,0 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Databases databases = Databases(client);
Document result = await databases.decrementDocumentAttribute(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
attribute: '',
value: 0, // optional
min: 0, // optional
);
@@ -1,16 +0,0 @@
import 'package:appwrite/appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
Databases databases = Databases(client);
Document result = await databases.incrementDocumentAttribute(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
attribute: '',
value: 0, // optional
max: 0, // optional
);
@@ -7,7 +7,6 @@ mutation {
permissions: ["read("any")"]
) {
_id
_sequence
_collectionId
_databaseId
_createdAt
@@ -1,19 +0,0 @@
mutation {
databasesDecrementDocumentAttribute(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documentId: "<DOCUMENT_ID>",
attribute: "",
value: 0,
min: 0
) {
_id
_sequence
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
@@ -1,19 +0,0 @@
mutation {
databasesIncrementDocumentAttribute(
databaseId: "<DATABASE_ID>",
collectionId: "<COLLECTION_ID>",
documentId: "<DOCUMENT_ID>",
attribute: "",
value: 0,
max: 0
) {
_id
_sequence
_collectionId
_databaseId
_createdAt
_updatedAt
_permissions
data
}
}
@@ -7,7 +7,6 @@ mutation {
permissions: ["read("any")"]
) {
_id
_sequence
_collectionId
_databaseId
_createdAt
@@ -7,7 +7,6 @@ mutation {
permissions: ["read("any")"]
) {
_id
_sequence
_collectionId
_databaseId
_createdAt
@@ -2,7 +2,9 @@ import { Client, Databases } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
.setSession('') // The user session to authenticate with
.setKey('') //
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
const databases = new Databases(client);
@@ -1,18 +0,0 @@
import { Client, Databases } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.decrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // min (optional)
);
console.log(result);
@@ -1,18 +0,0 @@
import { Client, Databases } from "react-native-appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.incrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // max (optional)
);
console.log(result);
@@ -1,12 +0,0 @@
PATCH /v1/databases/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/decrement HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Session:
X-Appwrite-JWT: <YOUR_JWT>
{
"value": 0,
"min": 0
}
@@ -1,12 +0,0 @@
PATCH /v1/databases/{databaseId}/collections/{collectionId}/documents/{documentId}/{attribute}/increment HTTP/1.1
Host: cloud.appwrite.io
Content-Type: application/json
X-Appwrite-Response-Format: 1.7.0
X-Appwrite-Project: <YOUR_PROJECT_ID>
X-Appwrite-Session:
X-Appwrite-JWT: <YOUR_JWT>
{
"value": 0,
"max": 0
}
@@ -2,7 +2,9 @@ import { Client, Databases } from "appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
.setSession('') // The user session to authenticate with
.setKey('') //
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
const databases = new Databases(client);
@@ -1,18 +0,0 @@
import { Client, Databases } from "appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.decrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // min (optional)
);
console.log(result);
@@ -1,18 +0,0 @@
import { Client, Databases } from "appwrite";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.incrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // max (optional)
);
console.log(result);
@@ -1,7 +0,0 @@
appwrite databases decrementDocumentAttribute \
--databaseId <DATABASE_ID> \
--collectionId <COLLECTION_ID> \
--documentId <DOCUMENT_ID> \
--attribute '' \
@@ -1,7 +0,0 @@
appwrite databases incrementDocumentAttribute \
--databaseId <DATABASE_ID> \
--collectionId <COLLECTION_ID> \
--documentId <DOCUMENT_ID> \
--attribute '' \
@@ -2,7 +2,9 @@ import { Client, Databases } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
.setSession('') //
.setKey('<YOUR_API_KEY>') // Your secret API key
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
const databases = new Databases(client);
@@ -2,7 +2,7 @@ import { Client, Databases } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
@@ -1,18 +0,0 @@
import { Client, Databases } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.decrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // min (optional)
);
console.log(result);
@@ -1,18 +0,0 @@
import { Client, Databases } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.incrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // max (optional)
);
console.log(result);
@@ -1,17 +0,0 @@
import { Client, Databases } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>'); // Your project ID
const databases = new Databases(client);
const result = await databases.upsertDocument(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
{}, // data
["read("any")"] // permissions (optional)
);
console.log(result);
@@ -9,7 +9,7 @@ const databases = new Databases(client);
const result = await databases.upsertDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents
[] // documents (optional)
);
console.log(result);
@@ -1,4 +1,4 @@
import { Client, Proxy, , ProxyResourceType } from "@appwrite.io/console";
import { Client, Proxy, } from "@appwrite.io/console";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
@@ -9,9 +9,7 @@ const proxy = new Proxy(client);
const result = await proxy.createRedirectRule(
'', // domain
'https://example.com', // url
.MovedPermanently301, // statusCode
'<RESOURCE_ID>', // resourceId
ProxyResourceType.Site // resourceType
.MovedPermanently301 // statusCode
);
console.log(result);
@@ -9,8 +9,7 @@ const vcs = new Vcs(client);
const result = await vcs.getRepositoryContents(
'<INSTALLATION_ID>', // installationId
'<PROVIDER_REPOSITORY_ID>', // providerRepositoryId
'<PROVIDER_ROOT_DIRECTORY>', // providerRootDirectory (optional)
'<PROVIDER_REFERENCE>' // providerReference (optional)
'<PROVIDER_ROOT_DIRECTORY>' // providerRootDirectory (optional)
);
console.log(result);
@@ -2,8 +2,9 @@ import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
.setSession('') // The user session to authenticate with
.setKey('<YOUR_API_KEY>') // Your secret API key
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
Databases databases = Databases(client);
@@ -2,7 +2,6 @@ import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
@@ -1,17 +0,0 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
Document result = await databases.decrementDocumentAttribute(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
attribute: '',
value: 0, // (optional)
min: 0, // (optional)
);
@@ -1,17 +0,0 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
Databases databases = Databases(client);
Document result = await databases.incrementDocumentAttribute(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
attribute: '',
value: 0, // (optional)
max: 0, // (optional)
);
@@ -1,16 +0,0 @@
import 'package:dart_appwrite/dart_appwrite.dart';
Client client = Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
Databases databases = Databases(client);
Document result = await databases.upsertDocument(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documentId: '<DOCUMENT_ID>',
data: {},
permissions: ["read("any")"], // (optional)
);
@@ -10,5 +10,5 @@ Databases databases = Databases(client);
DocumentList result = await databases.upsertDocuments(
databaseId: '<DATABASE_ID>',
collectionId: '<COLLECTION_ID>',
documents: [],
documents: [], // (optional)
);
@@ -2,8 +2,9 @@ import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
.setSession('') // The user session to authenticate with
.setKey('<YOUR_API_KEY>') // Your secret API key
.setJWT('<YOUR_JWT>'); // Your secret JSON Web Token
const databases = new Databases(client);
@@ -2,7 +2,6 @@ import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
@@ -1,17 +0,0 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
const response = await databases.decrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // min (optional)
);
@@ -1,17 +0,0 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setKey('<YOUR_API_KEY>'); // Your secret API key
const databases = new Databases(client);
const response = await databases.incrementDocumentAttribute(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
'', // attribute
null, // value (optional)
null // max (optional)
);
@@ -1,16 +0,0 @@
import { Client, Databases } from "https://deno.land/x/appwrite/mod.ts";
const client = new Client()
.setEndpoint('https://<REGION>.cloud.appwrite.io/v1') // Your API Endpoint
.setProject('<YOUR_PROJECT_ID>') // Your project ID
.setSession(''); // The user session to authenticate with
const databases = new Databases(client);
const response = await databases.upsertDocument(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
'<DOCUMENT_ID>', // documentId
{}, // data
["read("any")"] // permissions (optional)
);
@@ -10,5 +10,5 @@ const databases = new Databases(client);
const response = await databases.upsertDocuments(
'<DATABASE_ID>', // databaseId
'<COLLECTION_ID>', // collectionId
[] // documents
[] // documents (optional)
);
@@ -4,8 +4,9 @@ using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetSession(""); // The user session to authenticate with
.SetSession("") // The user session to authenticate with
.SetKey("<YOUR_API_KEY>") // Your secret API key
.SetJWT("<YOUR_JWT>"); // Your secret JSON Web Token
Databases databases = new Databases(client);
@@ -4,7 +4,6 @@ using Appwrite.Services;
Client client = new Client()
.SetEndPoint("https://<REGION>.cloud.appwrite.io/v1") // Your API Endpoint
.SetProject("<YOUR_PROJECT_ID>") // Your project ID
.SetKey("<YOUR_API_KEY>"); // Your secret API key
Databases databases = new Databases(client);

Some files were not shown because too many files have changed in this diff Show More