Merge branch 'main' into cicd-fix-float-to-int-conversion-warning-at-appwrite-doctor
This commit is contained in:
commit
1043e2f810
17 changed files with 289 additions and 97 deletions
19
.github/workflows/check-dependencies.yml
vendored
Normal file
19
.github/workflows/check-dependencies.yml
vendored
Normal file
|
@ -0,0 +1,19 @@
|
|||
name: Check dependencies
|
||||
|
||||
# Adapted from https://google.github.io/osv-scanner/github-action/#scan-on-pull-request
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main, 1.*.x]
|
||||
merge_group:
|
||||
branches: [main, 1.*.x]
|
||||
|
||||
permissions:
|
||||
# Require writing security events to upload SARIF file to security tab
|
||||
security-events: write
|
||||
# Only need to read contents
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
scan-pr:
|
||||
uses: "google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@v1.7.1"
|
2
.github/workflows/stale.yml
vendored
2
.github/workflows/stale.yml
vendored
|
@ -18,6 +18,6 @@ jobs:
|
|||
days-before-close: 14
|
||||
remove-stale-when-updated: true
|
||||
close-issue-message: "This issue has been closed due to inactivity. If you still require assistance, please provide the requested information."
|
||||
close-issue-reason: "not-planned"
|
||||
close-issue-reason: "not_planned"
|
||||
operations-per-run: 100
|
||||
only-labels: "question"
|
||||
|
|
137
CONTRIBUTING.md
137
CONTRIBUTING.md
|
@ -301,6 +301,143 @@ This will allow the Appwrite community to sufficiently discuss the new feature v
|
|||
|
||||
This is also important for the Appwrite lead developers to be able to provide technical input and potentially a different emphasis regarding the feature design and architecture. Some bigger features might need to go through our [RFC process](https://github.com/appwrite/rfc).
|
||||
|
||||
## Adding New Usage Metrics
|
||||
|
||||
These are the current metrics we collect usage stats for:
|
||||
|
||||
| Metric | Description |
|
||||
|--------|-------------------------------------------------|
|
||||
| teams | Total number of teams per project |
|
||||
| users | Total number of users per project|
|
||||
| executions | Total number of executions per project |
|
||||
| databases | Total number of databases per project |
|
||||
| collections | Total number of collections per project |
|
||||
| {databaseInternalId}.collections | Total number of collections per database|
|
||||
| documents | Total number of documents per project |
|
||||
| {databaseInternalId}.{collectionInternalId}.documents | Total number of documents per collection |
|
||||
| buckets | Total number of buckets per project |
|
||||
| files | Total number of files per project |
|
||||
| {bucketInternalId}.files.storage | Sum of files.storage per bucket (in bytes) |
|
||||
| functions | Total number of functions per project |
|
||||
| deployments | Total number of deployments per project |
|
||||
| builds | Total number of builds per project |
|
||||
| {resourceType}.{resourceInternalId}.deployments | Total number of deployments per function |
|
||||
| executions | Total number of executions per project |
|
||||
| {functionInternalId}.executions | Total number of executions per function |
|
||||
| files.storage | Sum of files storage per project (in bytes) |
|
||||
| deployments.storage | Sum of deployments storage per project (in bytes) |
|
||||
| {resourceType}.{resourceInternalId}.deployments.storage | Sum of deployments storage per function (in bytes) |
|
||||
| builds.storage | Sum of builds storage per project (in bytes) |
|
||||
| builds.compute | Sum of compute duration per project (in seconds) |
|
||||
| {functionInternalId}.builds.storage | Sum of builds storage per function (in bytes) |
|
||||
| {functionInternalId}.builds.compute | Sum of compute duration per function (in seconds) |
|
||||
| network.requests | Total number of network requests per project |
|
||||
| executions.compute | Sum of compute duration per project (in seconds) |
|
||||
| network.inbound | Sum of network inbound traffic per project (in bytes)|
|
||||
| network.outbound | Sum of network outbound traffic per project (in bytes)|
|
||||
|
||||
> Note: The curly brackets in the metric name represents a template and is replaced with a value when the metric is processed.
|
||||
|
||||
Metrics are collected within 3 scopes Daily, monthly, an infinity. Adding new usage metric in order to aggregate usage stats is very simple, but very much dependent on where do you want to collect
|
||||
statistics ,via API or via background worker. For both cases you will need to add a `const` variable in `app/init.php` under the usage metrics list using the naming convention `METRIC_<RESOURCE_NAME>` as shown below.
|
||||
|
||||
```php
|
||||
// Usage metrics
|
||||
const METRIC_FUNCTIONS = 'functions';
|
||||
const METRIC_DEPLOYMENTS = 'deployments';
|
||||
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
|
||||
```
|
||||
|
||||
Next follow the appropriate steps below depending on whether you're adding the metric to the API or the worker.
|
||||
|
||||
**API**
|
||||
|
||||
In file `app/controllers/shared/api.php` On the database listener, add to an existing or create a new switch case. Add a call to the usage worker with your new metric const like so:
|
||||
|
||||
```php
|
||||
case $document->getCollection() === 'teams':
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_TEAMS, $value); // per project
|
||||
break;
|
||||
```
|
||||
There are cases when you need to handle metric that has a parent entity, like buckets.
|
||||
Files are linked to a parent bucket, you should verify you remove the files stats when you delete a bucket.
|
||||
|
||||
In that case you need also to handle children removal using addReduce() method call.
|
||||
|
||||
```php
|
||||
|
||||
case $document->getCollection() === 'buckets': //buckets
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_BUCKETS, $value); // per project
|
||||
if ($event === Database::EVENT_DOCUMENT_DELETE) {
|
||||
$queueForUsage
|
||||
->addReduce($document);
|
||||
}
|
||||
break;
|
||||
|
||||
```
|
||||
|
||||
In addition, you will also need to add some logic to the `reduce()` method of the Usage worker located in `/src/Appwrite/Platform/Workers/Usage.php`, like so:
|
||||
|
||||
```php
|
||||
case $document->getCollection() === 'buckets':
|
||||
$files = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES)));
|
||||
$storage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{bucketInternalId}', $document->getInternalId(), METRIC_BUCKET_ID_FILES_STORAGE)));
|
||||
|
||||
if (!empty($files['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_FILES,
|
||||
'value' => ($files['value'] * -1),
|
||||
];
|
||||
}
|
||||
|
||||
if (!empty($storage['value'])) {
|
||||
$metrics[] = [
|
||||
'key' => METRIC_FILES_STORAGE,
|
||||
'value' => ($storage['value'] * -1),
|
||||
];
|
||||
}
|
||||
break;
|
||||
```
|
||||
|
||||
**Background worker**
|
||||
|
||||
You need to inject the usage queue in the desired worker on the constructor method
|
||||
```php
|
||||
/**
|
||||
* @throws Exception
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->desc('Functions worker')
|
||||
->groups(['functions'])
|
||||
->inject('message')
|
||||
->inject('dbForProject')
|
||||
->inject('queueForFunctions')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForUsage')
|
||||
->inject('log')
|
||||
->callback(fn (Message $message, Database $dbForProject, Func $queueForFunctions, Event $queueForEvents, Usage $queueForUsage, Log $log) => $this->action($message, $dbForProject, $queueForFunctions, $queueForEvents, $queueForUsage, $log));
|
||||
}
|
||||
```
|
||||
|
||||
and then trigger the queue with the new metric like so:
|
||||
|
||||
```php
|
||||
$queueForUsage
|
||||
->addMetric(METRIC_BUILDS, 1)
|
||||
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
|
||||
->addMetric(METRIC_BUILDS_COMPUTE, (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS), 1)
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE), $build->getAttribute('size', 0))
|
||||
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE), (int)$build->getAttribute('duration', 0) * 1000)
|
||||
->setProject($project)
|
||||
->trigger();
|
||||
```
|
||||
|
||||
|
||||
## Build
|
||||
|
||||
To build a new version of the Appwrite server, all you need to do is run the build.sh file like this:
|
||||
|
|
112
README-CN.md
112
README-CN.md
|
@ -11,6 +11,7 @@
|
|||
</p>
|
||||
|
||||
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[![We're Hiring](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers)
|
||||
[![Hacktoberfest](https://img.shields.io/static/v1?label=hacktoberfest&message=friendly&color=191120&style=flat-square)](https://hacktoberfest.appwrite.io)
|
||||
[![Discord](https://img.shields.io/discord/564160730845151244?label=discord&style=flat-square)](https://appwrite.io/discord?r=Github)
|
||||
|
@ -25,7 +26,7 @@
|
|||
|
||||
[**Appwrite 云公开测试版!立即注册!**](https://cloud.appwrite.io)
|
||||
|
||||
Appwrite是一个基于Docker的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
|
||||
Appwrite 是一个基于 Docker 的端到端开发者平台,其容器化的微服务库可应用于网页端,移动端,以及后端。Appwrite 通过视觉化界面简化了从零开始编写 API 的繁琐过程,在保证软件安全的前提下为开发者创造了一个高效的开发环境。
|
||||
|
||||
Appwrite 可以提供给开发者用户验证,外部授权,用户数据读写检索,文件储存,图像处理,云函数计算,[等多种服务](https://appwrite.io/docs).
|
||||
|
||||
|
@ -93,7 +94,6 @@ docker run -it --rm `
|
|||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
||||
|
||||
需要自定义容器构架,请查看我们的 Docker [环境变量](https://appwrite.io/docs/environment-variables) 文档。您还可以参考我们的 [docker-compose.yml](https://appwrite.io/install/compose) 和 [.env](https://appwrite.io/install/env) 文件手动设置环境。
|
||||
|
||||
### 从旧版本升级
|
||||
|
@ -104,71 +104,73 @@ docker run -it --rm `
|
|||
|
||||
开始使用 Appwrite 只需要在控制台创建一个新项目,选择开发平台,然后抓取我们的开发套件。您可以从以下的教程中找到你喜欢的平台开始使用 Appwrite。
|
||||
|
||||
| 类别 | 技术 |
|
||||
|---------------------|------|
|
||||
| **Web 应用** | [Web 快速开始](/docs/quick-starts/web) |
|
||||
| | [Next.js 快速开始](/docs/quick-starts/nextjs) |
|
||||
| | [React 快速开始](/docs/quick-starts/react) |
|
||||
| | [Vue.js 快速开始](/docs/quick-starts/vue) |
|
||||
| | [Nuxt 快速开始](/docs/quick-starts/nuxt) |
|
||||
| | [SvelteKit 快速开始](/docs/quick-starts/sveltekit) |
|
||||
| | [Refine 快速开始](/docs/quick-starts/refine) |
|
||||
| | [Angular 快速开始](/docs/quick-starts/angular) |
|
||||
| **苹果于安卓应用** | [React Native 快速开始](/docs/quick-starts/react-native) |
|
||||
| | [Flutter 快速开始](/docs/quick-starts/flutter) |
|
||||
| | [Apple 快速开始](/docs/quick-starts/apple) |
|
||||
| | [Android 快速开始](/docs/quick-starts/android) |
|
||||
| **服务器** | [Node.js 快速开始](/docs/quick-starts/node) |
|
||||
| | [Python 快速开始](/docs/quick-starts/python) |
|
||||
| | [.NET 快速开始](/docs/quick-starts/dotnet) |
|
||||
| | [Dart 快速开始](/docs/quick-starts/dart) |
|
||||
| | [Ruby 快速开始](/docs/quick-starts/ruby) |
|
||||
| | [Deno 快速开始](/docs/quick-starts/deno) |
|
||||
| | [PHP 快速开始](/docs/quick-starts/php) |
|
||||
| | [Kotlin 快速开始](/docs/quick-starts/kotlin) |
|
||||
| | [Swift 快速开始](/docs/quick-starts/swift) |
|
||||
| 类别 | 技术 |
|
||||
| ------------------ | --------------------------------------------------------------------------- |
|
||||
| **Web 应用** | [Web 快速开始](https://appwrite.io/docs/quick-starts/web) |
|
||||
| | [Next.js 快速开始](https://appwrite.io/docs/quick-starts/nextjs) |
|
||||
| | [React 快速开始](https://appwrite.io/docs/quick-starts/react) |
|
||||
| | [Vue.js 快速开始](https://appwrite.io/docs/quick-starts/vue) |
|
||||
| | [Nuxt 快速开始](https://appwrite.io/docs/quick-starts/nuxt) |
|
||||
| | [SvelteKit 快速开始](https://appwrite.io/docs/quick-starts/sveltekit) |
|
||||
| | [Refine 快速开始](https://appwrite.io/docs/quick-starts/refine) |
|
||||
| | [Angular 快速开始](https://appwrite.io/docs/quick-starts/angular) |
|
||||
| **苹果于安卓应用** | [React Native 快速开始](https://appwrite.io/docs/quick-starts/react-native) |
|
||||
| | [Flutter 快速开始](https://appwrite.io/docs/quick-starts/flutter) |
|
||||
| | [Apple 快速开始](https://appwrite.io/docs/quick-starts/apple) |
|
||||
| | [Android 快速开始](https://appwrite.io/docs/quick-starts/android) |
|
||||
| **服务器** | [Node.js 快速开始](https://appwrite.io/docs/quick-starts/node) |
|
||||
| | [Python 快速开始](https://appwrite.io/docs/quick-starts/python) |
|
||||
| | [.NET 快速开始](https://appwrite.io/docs/quick-starts/dotnet) |
|
||||
| | [Dart 快速开始](https://appwrite.io/docs/quick-starts/dart) |
|
||||
| | [Ruby 快速开始](https://appwrite.io/docs/quick-starts/ruby) |
|
||||
| | [Deno 快速开始](https://appwrite.io/docs/quick-starts/deno) |
|
||||
| | [PHP 快速开始](https://appwrite.io/docs/quick-starts/php) |
|
||||
| | [Kotlin 快速开始](https://appwrite.io/docs/quick-starts/kotlin) |
|
||||
| | [Swift 快速开始](https://appwrite.io/docs/quick-starts/swift) |
|
||||
|
||||
### 软件服务
|
||||
|
||||
* [**帐户**](https://appwrite.io/docs/references/cloud/client-web/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
|
||||
* [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
|
||||
* [**团队**](https://appwrite.io/docs/references/cloud/client-web/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
|
||||
* [**数据库**](https://appwrite.io/docs/references/cloud/client-web/databases) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
|
||||
* [**贮存**](https://appwrite.io/docs/references/cloud/client-web/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
|
||||
* [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
|
||||
* [**消息传递**](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) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
|
||||
如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
|
||||
- [**帐户**](https://appwrite.io/docs/references/cloud/client-web/account) -管理当前用户的帐户和登录方式。跟踪和管理用户 Session,登录设备,登录方法和查看相关记录。
|
||||
- [**用户**](https://appwrite.io/docs/server/users) - 在以管理员模式登录时管理和列出所有用户。
|
||||
- [**团队**](https://appwrite.io/docs/references/cloud/client-web/teams) - 管理用户分组。邀请成员,管理团队中的用户权限和用户角色。
|
||||
- [**数据库**](https://appwrite.io/docs/references/cloud/client-web/databases) - 管理数据库文档和文档集。用检索界面来对文档和文档集进行读取,创建,更新,和删除。
|
||||
- [**贮存**](https://appwrite.io/docs/references/cloud/client-web/storage) - 管理文件的阅读、创建、删除和预览。设置文件的预览来满足程序的个性化需求。所有文件都由 ClamAV 扫描并安全存储和加密。
|
||||
- [**云函数**](https://appwrite.io/docs/server/functions) - 在安全,隔离的环境中运行自定义代码。这些代码可以被事件,CRON,或者手动操作触发。
|
||||
- [**消息传递**](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) -管理用户头像、国家旗帜、浏览器图标、信用卡符号,和生成二维码。
|
||||
如需完整的 API 界面文档,请访问 [https://appwrite.io/docs](https://appwrite.io/docs)。如需更多教程、新闻和公告,请订阅我们的 [博客](https://medium.com/appwrite-io) 和 加入我们的[Discord 社区](https://discord.gg/GSeTUeA)。
|
||||
|
||||
### 开发套件
|
||||
|
||||
以下是当前支持的平台和语言列表。如果您想帮助我们为您选择的平台添加支持,您可以访问我们的 [SDK 生成器](https://github.com/appwrite/sdk-generator) 项目并查看我们的 [贡献指南](https://github.com/appwrite/sdk-generator/blob/master/CONTRIBUTING.md)。
|
||||
|
||||
#### 客户端
|
||||
* ✅ [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
|
||||
* ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
|
||||
* ✅ [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护)
|
||||
* ✅ [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
|
||||
* ✅ [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护)
|
||||
|
||||
- ✅ [Web](https://github.com/appwrite/sdk-for-web) (由 Appwrite 团队维护)
|
||||
- ✅ [Flutter](https://github.com/appwrite/sdk-for-flutter) (由 Appwrite 团队维护)
|
||||
- ✅ [Apple](https://github.com/appwrite/sdk-for-apple) (由 Appwrite 团队维护)
|
||||
- ✅ [Android](https://github.com/appwrite/sdk-for-android) (由 Appwrite 团队维护)
|
||||
- ✅ [React Native](https://github.com/appwrite/sdk-for-react-native) - **公测** (由 Appwrite 团队维护)
|
||||
|
||||
#### 服务器
|
||||
* ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
|
||||
* ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
|
||||
* ✅ [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护)
|
||||
* ✅ [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护)
|
||||
* ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
|
||||
* ✅ [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
|
||||
* ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护)
|
||||
* ✅ [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护)
|
||||
* ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
|
||||
|
||||
- ✅ [NodeJS](https://github.com/appwrite/sdk-for-node) (由 Appwrite 团队维护)
|
||||
- ✅ [PHP](https://github.com/appwrite/sdk-for-php) (由 Appwrite 团队维护)
|
||||
- ✅ [Dart](https://github.com/appwrite/sdk-for-dart) (由 Appwrite 团队维护)
|
||||
- ✅ [Deno](https://github.com/appwrite/sdk-for-deno) (由 Appwrite 团队维护)
|
||||
- ✅ [Ruby](https://github.com/appwrite/sdk-for-ruby) (由 Appwrite 团队维护)
|
||||
- ✅ [Python](https://github.com/appwrite/sdk-for-python) (由 Appwrite 团队维护)
|
||||
- ✅ [Kotlin](https://github.com/appwrite/sdk-for-kotlin) (由 Appwrite 团队维护)
|
||||
- ✅ [Swift](https://github.com/appwrite/sdk-for-swift) (由 Appwrite 团队维护)
|
||||
- ✅ [.NET](https://github.com/appwrite/sdk-for-dotnet) - **公测** (由 Appwrite 团队维护)
|
||||
|
||||
#### 开发者社区
|
||||
* ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
|
||||
* ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
|
||||
|
||||
找不到需要的的 SDK? - 欢迎通过发起PR来帮助我们完善Appwrite的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
|
||||
- ✅ [Appcelerator Titanium](https://github.com/m1ga/ti.appwrite) (维护者 [Michael Gangolf](https://github.com/m1ga/))
|
||||
- ✅ [Godot Engine](https://github.com/GodotNuts/appwrite-sdk) (维护者 [fenix-hub @GodotNuts](https://github.com/fenix-hub))
|
||||
|
||||
找不到需要的的 SDK? - 欢迎通过发起 PR 来帮助我们完善 Appwrite 的软件生态环境 [SDK 生成器](https://github.com/appwrite/sdk-generator)!
|
||||
|
||||
## 软件架构
|
||||
|
||||
|
@ -180,13 +182,13 @@ Appwrite API 界面层利用后台缓存和任务委派来提供极速的响应
|
|||
|
||||
## 贡献代码
|
||||
|
||||
为了确保正确审查,所有代码贡献 - 包括来自具有直接提交更改权限的贡献者 - 都必须提交PR请求并在合并分支之前得到核心开发人员的批准。
|
||||
为了确保正确审查,所有代码贡献 - 包括来自具有直接提交更改权限的贡献者 - 都必须提交 PR 请求并在合并分支之前得到核心开发人员的批准。
|
||||
|
||||
我们欢迎所有人提交PR!如果您愿意提供帮助,可以在 [贡献指南](CONTRIBUTING.md) 中了解有关如何为项目做出贡献的更多信息。
|
||||
我们欢迎所有人提交 PR!如果您愿意提供帮助,可以在 [贡献指南](CONTRIBUTING.md) 中了解有关如何为项目做出贡献的更多信息。
|
||||
|
||||
## 安全
|
||||
|
||||
为了保护您的隐私,请避免在GitHub 上发布安全问题。发送问题至 security@appwrite.io,我们将为您做更细致的解答。
|
||||
为了保护您的隐私,请避免在 GitHub 上发布安全问题。发送问题至 security@appwrite.io,我们将为您做更细致的解答。
|
||||
|
||||
## 订阅我们
|
||||
|
||||
|
|
47
README.md
47
README.md
|
@ -10,7 +10,6 @@
|
|||
<br />
|
||||
</p>
|
||||
|
||||
|
||||
<!-- [![Build Status](https://img.shields.io/travis/com/appwrite/appwrite?style=flat-square)](https://travis-ci.com/appwrite/appwrite) -->
|
||||
|
||||
[![We're Hiring](https://img.shields.io/static/v1?label=We're&message=Hiring&color=blue&style=flat-square)](https://appwrite.io/company/careers)
|
||||
|
@ -142,29 +141,29 @@ Choose from one of the providers below:
|
|||
|
||||
Getting started with Appwrite is as easy as creating a new project, choosing your platform, and integrating its SDK into your code. You can easily get started with your platform of choice by reading one of our Getting Started tutorials.
|
||||
|
||||
| Platform | Technology |
|
||||
|--------------------|------------|
|
||||
| **Web app** | [Quick start for Web](/docs/quick-starts/web) |
|
||||
| | [Quick start for Next.js](/docs/quick-starts/nextjs) |
|
||||
| | [Quick start for React](/docs/quick-starts/react) |
|
||||
| | [Quick start for Vue.js](/docs/quick-starts/vue) |
|
||||
| | [Quick start for Nuxt](/docs/quick-starts/nuxt) |
|
||||
| | [Quick start for SvelteKit](/docs/quick-starts/sveltekit) |
|
||||
| | [Quick start for Refine](/docs/quick-starts/refine) |
|
||||
| | [Quick start for Angular](/docs/quick-starts/angular) |
|
||||
| **Mobile and Native** | [Quick start for React Native](/docs/quick-starts/react-native) |
|
||||
| | [Quick start for Flutter](/docs/quick-starts/flutter) |
|
||||
| | [Quick start for Apple](/docs/quick-starts/apple) |
|
||||
| | [Quick start for Android](/docs/quick-starts/android) |
|
||||
| **Server** | [Quick start for Node.js](/docs/quick-starts/node) |
|
||||
| | [Quick start for Python](/docs/quick-starts/python) |
|
||||
| | [Quick start for .NET](/docs/quick-starts/dotnet) |
|
||||
| | [Quick start for Dart](/docs/quick-starts/dart) |
|
||||
| | [Quick start for Ruby](/docs/quick-starts/ruby) |
|
||||
| | [Quick start for Deno](/docs/quick-starts/deno) |
|
||||
| | [Quick start for PHP](/docs/quick-starts/php) |
|
||||
| | [Quick start for Kotlin](/docs/quick-starts/kotlin) |
|
||||
| | [Quick start for Swift](/docs/quick-starts/swift) |
|
||||
| Platform | Technology |
|
||||
| --------------------- | ---------------------------------------------------------------------------------- |
|
||||
| **Web app** | [Quick start for Web](https://appwrite.io/docs/quick-starts/web) |
|
||||
| | [Quick start for Next.js](https://appwrite.io/docs/quick-starts/nextjs) |
|
||||
| | [Quick start for React](https://appwrite.io/docs/quick-starts/react) |
|
||||
| | [Quick start for Vue.js](https://appwrite.io/docs/quick-starts/vue) |
|
||||
| | [Quick start for Nuxt](https://appwrite.io/docs/quick-starts/nuxt) |
|
||||
| | [Quick start for SvelteKit](https://appwrite.io/docs/quick-starts/sveltekit) |
|
||||
| | [Quick start for Refine](https://appwrite.io/docs/quick-starts/refine) |
|
||||
| | [Quick start for Angular](https://appwrite.io/docs/quick-starts/angular) |
|
||||
| **Mobile and Native** | [Quick start for React Native](https://appwrite.io/docs/quick-starts/react-native) |
|
||||
| | [Quick start for Flutter](https://appwrite.io/docs/quick-starts/flutter) |
|
||||
| | [Quick start for Apple](https://appwrite.io/docs/quick-starts/apple) |
|
||||
| | [Quick start for Android](https://appwrite.io/docs/quick-starts/android) |
|
||||
| **Server** | [Quick start for Node.js](https://appwrite.io/docs/quick-starts/node) |
|
||||
| | [Quick start for Python](https://appwrite.io/docs/quick-starts/python) |
|
||||
| | [Quick start for .NET](https://appwrite.io/docs/quick-starts/dotnet) |
|
||||
| | [Quick start for Dart](https://appwrite.io/docs/quick-starts/dart) |
|
||||
| | [Quick start for Ruby](https://appwrite.io/docs/quick-starts/ruby) |
|
||||
| | [Quick start for Deno](https://appwrite.io/docs/quick-starts/deno) |
|
||||
| | [Quick start for PHP](https://appwrite.io/docs/quick-starts/php) |
|
||||
| | [Quick start for Kotlin](https://appwrite.io/docs/quick-starts/kotlin) |
|
||||
| | [Quick start for Swift](https://appwrite.io/docs/quick-starts/swift) |
|
||||
|
||||
### Products
|
||||
|
||||
|
|
|
@ -4,7 +4,7 @@ return [
|
|||
'amex' => ['name' => 'American Express', 'path' => __DIR__ . '/credit-cards/amex.png'],
|
||||
'argencard' => ['name' => 'Argencard', 'path' => __DIR__ . '/credit-cards/argencard.png'],
|
||||
'cabal' => ['name' => 'Cabal', 'path' => __DIR__ . '/credit-cards/cabal.png'],
|
||||
'censosud' => ['name' => 'Consosud', 'path' => __DIR__ . '/credit-cards/consosud.png'],
|
||||
'cencosud' => ['name' => 'Cencosud', 'path' => __DIR__ . '/credit-cards/cencosud.png'],
|
||||
'diners' => ['name' => 'Diners Club', 'path' => __DIR__ . '/credit-cards/diners.png'],
|
||||
'discover' => ['name' => 'Discover', 'path' => __DIR__ . '/credit-cards/discover.png'],
|
||||
'elo' => ['name' => 'Elo', 'path' => __DIR__ . '/credit-cards/elo.png'],
|
||||
|
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 46 KiB |
|
@ -11,7 +11,7 @@ return [
|
|||
/** General Errors */
|
||||
Exception::GENERAL_UNKNOWN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN,
|
||||
'description' => 'An unknown error has occured. Please check the logs for more information.',
|
||||
'description' => 'An unknown error has occurred. Please check the logs for more information.',
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::GENERAL_MOCK => [
|
||||
|
@ -284,7 +284,7 @@ return [
|
|||
],
|
||||
Exception::USER_CHALLENGE_REQUIRED => [
|
||||
'name' => Exception::USER_CHALLENGE_REQUIRED,
|
||||
'description' => 'A recently succeessful challenge is required to complete this action. A challenge is considered recent for 5 minutes.',
|
||||
'description' => 'A recently successful challenge is required to complete this action. A challenge is considered recent for 5 minutes.',
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::USER_OAUTH2_BAD_REQUEST => [
|
||||
|
@ -489,7 +489,7 @@ return [
|
|||
],
|
||||
Exception::REPOSITORY_NOT_FOUND => [
|
||||
'name' => Exception::REPOSITORY_NOT_FOUND,
|
||||
'description' => 'Repository with the requested ID could not be found. Check to see if the ID is correct, or create the respository.',
|
||||
'description' => 'Repository with the requested ID could not be found. Check to see if the ID is correct, or create the repository.',
|
||||
'code' => 404,
|
||||
],
|
||||
Exception::PROVIDER_CONTRIBUTION_CONFLICT => [
|
||||
|
@ -499,7 +499,7 @@ return [
|
|||
],
|
||||
Exception::GENERAL_PROVIDER_FAILURE => [
|
||||
'name' => Exception::GENERAL_PROVIDER_FAILURE,
|
||||
'description' => 'VCS (Version Control System) provider failed to proccess the request. We believe this is an error with the VCS provider. Try again, or contact support for more information.',
|
||||
'description' => 'VCS (Version Control System) provider failed to process the request. We believe this is an error with the VCS provider. Try again, or contact support for more information.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
|
|
|
@ -123,7 +123,8 @@ $createSession = function (string $userId, string $secret, Request $request, Res
|
|||
Authorization::skip(fn () => $dbForProject->deleteDocument('tokens', $verifiedToken->getId()));
|
||||
$dbForProject->purgeCachedDocument('users', $user->getId());
|
||||
|
||||
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL) {
|
||||
// Magic URL + Email OTP
|
||||
if ($verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_MAGIC_URL || $verifiedToken->getAttribute('type') === Auth::TOKEN_TYPE_EMAIL) {
|
||||
$user->setAttribute('emailVerification', true);
|
||||
}
|
||||
|
||||
|
|
|
@ -1282,7 +1282,7 @@ App::get('/v1/functions/:functionId/deployments')
|
|||
}
|
||||
|
||||
// Set resource queries
|
||||
$queries[] = Query::equal('resourceId', [$function->getId()]);
|
||||
$queries[] = Query::equal('resourceInternalId', [$function->getInternalId()]);
|
||||
$queries[] = Query::equal('resourceType', ['functions']);
|
||||
|
||||
/**
|
||||
|
|
|
@ -49,6 +49,12 @@ App::post('/v1/proxy/rules')
|
|||
if ($domain === $mainDomain) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.');
|
||||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
if (str_ends_with($domain, $functionsDomain)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions domain or it\'s subdomain to specific resource. Please use different domain.');
|
||||
}
|
||||
|
||||
if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.');
|
||||
}
|
||||
|
|
|
@ -837,7 +837,7 @@ $register->set('pools', function () {
|
|||
/**
|
||||
* Get Resource
|
||||
*
|
||||
* Creation could be reused accross connection types like database, cache, queue, etc.
|
||||
* Creation could be reused across connection types like database, cache, queue, etc.
|
||||
*
|
||||
* Resource assignment to an adapter will happen below.
|
||||
*/
|
||||
|
@ -847,7 +847,7 @@ $register->set('pools', function () {
|
|||
$resource = function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDOProxy(function () use ($dsnHost, $dsnPort, $dsnUser, $dsnPass, $dsnDatabase) {
|
||||
return new PDO("mysql:host={$dsnHost};port={$dsnPort};dbname={$dsnDatabase};charset=utf8mb4", $dsnUser, $dsnPass, array(
|
||||
// No need to set PDO::ATTR_ERRMODE it is overwitten in PDOProxy
|
||||
// No need to set PDO::ATTR_ERRMODE it is overwritten in PDOProxy
|
||||
PDO::ATTR_TIMEOUT => 3, // Seconds
|
||||
PDO::ATTR_PERSISTENT => true,
|
||||
PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
|
||||
|
|
|
@ -103,7 +103,7 @@ class CalcTierStats extends Action
|
|||
|
||||
return;
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
@ -129,7 +129,7 @@ class CalcTierStats extends Action
|
|||
$data = $this->getData($project, $dbForConsole, $dbForProject);
|
||||
$csv->insertOne($data);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
|
|
@ -50,7 +50,7 @@ class CreateInfMetric extends Action
|
|||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->getUsageData($dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
@ -72,7 +72,7 @@ class CreateInfMetric extends Action
|
|||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->getUsageData($dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
|
|
@ -42,7 +42,7 @@ class PatchRecreateRepositoriesDocuments extends Action
|
|||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
@ -64,7 +64,7 @@ class PatchRecreateRepositoriesDocuments extends Action
|
|||
$dbForProject = call_user_func($getProjectDB, $project);
|
||||
$this->recreateRepositories($dbForConsole, $dbForProject, $project);
|
||||
} catch (\Throwable $th) {
|
||||
Console::error("Unexpected error occured with Project ID {$projectId}");
|
||||
Console::error("Unexpected error occurred with Project ID {$projectId}");
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
|
|
|
@ -202,6 +202,8 @@ trait AccountBase
|
|||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals($userId, $response['body']['$id']);
|
||||
$this->assertEquals($userId, $response['body']['$id']);
|
||||
$this->assertTrue($response['body']['emailVerification']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', array_merge([
|
||||
'origin' => 'http://localhost',
|
||||
|
|
|
@ -2,17 +2,43 @@
|
|||
|
||||
namespace Tests\E2E\Services\Projects;
|
||||
|
||||
use Tests\E2E\Client;
|
||||
use Tests\E2E\Scopes\ProjectCustom;
|
||||
use Tests\E2E\Scopes\Scope;
|
||||
use Tests\E2E\Scopes\SideServer;
|
||||
use Utopia\System\System;
|
||||
|
||||
class ProjectsCustomServerTest extends Scope
|
||||
{
|
||||
use ProjectCustom;
|
||||
use SideServer;
|
||||
|
||||
public function testMock()
|
||||
// Domains
|
||||
|
||||
public function testCreateProjectRule()
|
||||
{
|
||||
$this->assertEquals(true, true);
|
||||
$headers = array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
]);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [
|
||||
'resourceType' => 'api',
|
||||
'domain' => 'api.appwrite.test',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $response['headers']['status-code']);
|
||||
|
||||
// prevent functions domain
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [
|
||||
'resourceType' => 'api',
|
||||
'domain' => $functionsDomain,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue