mirror of
https://github.com/appwrite/appwrite
synced 2026-05-22 16:38:32 +00:00
Merge branch 'appwrite:master' into feat-XXX-authentik-oauth
This commit is contained in:
commit
bee6a7a2b3
264 changed files with 3669 additions and 4970 deletions
34
.github/workflows/linter.yml
vendored
Normal file
34
.github/workflows/linter.yml
vendored
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
name: "Linter"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
tests:
|
||||
name: Linter
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
# We must fetch at least the immediate parents so that if this is
|
||||
# a pull request then we can checkout the head.
|
||||
fetch-depth: 2
|
||||
|
||||
# If this run was triggered by a pull request event, then checkout
|
||||
# the head of the pull request instead of the merge commit.
|
||||
- run: git checkout HEAD^2
|
||||
if: ${{ github.event_name == 'pull_request' }}
|
||||
|
||||
- name: Setup PHP
|
||||
uses: shivammathur/setup-php@v2
|
||||
with:
|
||||
php-version: '8.0'
|
||||
|
||||
- name: Install dependencies
|
||||
uses: php-actions/composer@v6
|
||||
with:
|
||||
php_version: '8.0'
|
||||
args: --profile --ignore-platform-reqs
|
||||
|
||||
- name: Run Linter
|
||||
run: ./vendor/bin/phpcs -p
|
||||
13
.gitpod.Dockerfile
vendored
13
.gitpod.Dockerfile
vendored
|
|
@ -1,13 +0,0 @@
|
|||
FROM gitpod/workspace-full
|
||||
|
||||
# Disable current PHP installation
|
||||
RUN sudo a2dismod php7.4
|
||||
RUN sudo a2dismod mpm_prefork
|
||||
|
||||
# Install apache2 (PHP install requires to do this first)
|
||||
RUN sudo apt --yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install apache2
|
||||
|
||||
# Update to PHP 8.0 with unattended installation
|
||||
RUN sudo apt --yes install software-properties-common && sudo add-apt-repository ppa:ondrej/php -y
|
||||
RUN sudo apt update
|
||||
RUN sudo apt --yes -o Dpkg::Options::="--force-confdef" -o Dpkg::Options::="--force-confold" install php8.0
|
||||
19
.gitpod.yml
19
.gitpod.yml
|
|
@ -1,10 +1,17 @@
|
|||
image:
|
||||
file: .gitpod.Dockerfile
|
||||
|
||||
tasks:
|
||||
- init: docker-compose pull &&
|
||||
docker-compose build &&
|
||||
docker run --rm --interactive --tty --volume $PWD:/app composer update --ignore-platform-reqs --optimize-autoloader --no-plugins --no-scripts --prefer-dist
|
||||
- name: Run Appwrite Docker Stack
|
||||
init: |
|
||||
docker compose pull
|
||||
docker compose build
|
||||
command: |
|
||||
docker run --rm --interactive --tty \
|
||||
--volume $PWD:/app \
|
||||
composer update \
|
||||
--ignore-platform-reqs \
|
||||
--optimize-autoloader \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
|
||||
ports:
|
||||
- port: 8080
|
||||
|
|
|
|||
177
CONTRIBUTING.md
177
CONTRIBUTING.md
|
|
@ -12,11 +12,12 @@ Help us keep Appwrite open and inclusive. Please read and follow our [Code of Co
|
|||
|
||||
## Submit a Pull Request 🚀
|
||||
|
||||
Branch naming convention is as following
|
||||
Branch naming convention is as following
|
||||
|
||||
`TYPE-ISSUE_ID-DESCRIPTION`
|
||||
|
||||
example:
|
||||
|
||||
```
|
||||
doc-548-submit-a-pull-request-section-to-contribution-guide
|
||||
```
|
||||
|
|
@ -29,32 +30,55 @@ When `TYPE` can be:
|
|||
- **fix** - a bug fix
|
||||
- **refactor** - code change that neither fixes a bug nor adds a feature
|
||||
|
||||
**All PRs must include a commit message with the changes description!**
|
||||
**All PRs must include a commit message with the changes description!**
|
||||
|
||||
For the initial start, fork the project and use git clone command to download the repository to your computer. A standard procedure for working on an issue would be to:
|
||||
|
||||
1. `git pull`, before creating a new branch, pull the changes from upstream. Your master needs to be up to date.
|
||||
|
||||
```
|
||||
$ git pull
|
||||
```
|
||||
|
||||
2. Create new branch from `master` like: `doc-548-submit-a-pull-request-section-to-contribution-guide`<br/>
|
||||
|
||||
```
|
||||
$ git checkout -b [name_of_your_new_branch]
|
||||
```
|
||||
|
||||
3. Work - commit - repeat ( be sure to be in your branch )
|
||||
|
||||
4. Push changes to GitHub
|
||||
4. Before you push your changes, make sure your code follows the `PSR12` coding standards , which is the standard Appwrite follows currently. You can easily do this by running the formatter.
|
||||
|
||||
```bash
|
||||
./vendor/bin/phpcbf <your file path>
|
||||
```
|
||||
|
||||
Now, go a step further by running the linter by the following command to manually fix the issues the formatter wasn't able to fix.
|
||||
|
||||
```bash
|
||||
./vendor/bin/phpcs <your file path>
|
||||
```
|
||||
|
||||
This will give you a list of errors for you to rectify , if there is an instance you need more information on the errors being displayed you can pass in additional command line arguments. More list of available arguments can be found [here](https://github.com/squizlabs/PHP_CodeSniffer/wiki/Usage). A very useful command line argument is `--report=diff`. This will give you the expected changes by the linter for easy fixing of formatting issues.
|
||||
|
||||
```bash
|
||||
./vendor/bin/phpcs --report=diff <your file path>
|
||||
```
|
||||
|
||||
5. Push changes to GitHub
|
||||
|
||||
```
|
||||
$ git push origin [name_of_your_new_branch]
|
||||
```
|
||||
|
||||
5. Submit your changes for review
|
||||
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
|
||||
6. Start a Pull Request
|
||||
Now submit the pull request and click on `Create pull request`.
|
||||
7. Get a code review approval/reject
|
||||
8. After approval, merge your PR
|
||||
9. GitHub will automatically delete the branch after the merge is done. (they can still be restored).
|
||||
6. Submit your changes for review
|
||||
If you go to your repository on GitHub, you'll see a `Compare & pull request` button. Click on that button.
|
||||
7. Start a Pull Request
|
||||
Now submit the pull request and click on `Create pull request`.
|
||||
8. Get a code review approval/reject
|
||||
9. After approval, merge your PR
|
||||
10. GitHub will automatically delete the branch after the merge is done. (they can still be restored).
|
||||
|
||||
## Setup From Source
|
||||
|
||||
|
|
@ -90,18 +114,20 @@ Appwrite uses an internal micro-framework called Litespeed.js to build simple UI
|
|||
|
||||
After finishing the installation process, you can start writing and editing code.
|
||||
|
||||
|
||||
#### Advanced Topics
|
||||
|
||||
We love to create issues that are good for beginners and label them as `good first issue` or `hacktoberfest`, but some more advanced topics might require extra knowledge. Below is a list of links you can use to learn more about some of the more advance topics that will help you master the Appwrite codebase.
|
||||
|
||||
##### Tools and Libs
|
||||
|
||||
- [Docker](https://www.docker.com/get-started)
|
||||
- [PHP FIG](https://www.php-fig.org/) - [PSR-1](https://www.php-fig.org/psr/psr-1/) and [PSR-4](https://www.php-fig.org/psr/psr-4/)
|
||||
- [PHP FIG](https://www.php-fig.org/) - [PSR-12](https://www.php-fig.org/psr/psr-12/)
|
||||
- [PHP Swoole](https://www.swoole.co.uk/)
|
||||
|
||||
Learn more at our [Technology Stack](#technology-stack) section.
|
||||
|
||||
##### Network and Protocols
|
||||
|
||||
- [OSI Model](https://en.wikipedia.org/wiki/OSI_model)
|
||||
- [TCP vs UDP](https://www.guru99.com/tcp-vs-udp-understanding-the-difference.html#:~:text=TCP%20is%20a%20connection%2Doriented,speed%20of%20UDP%20is%20faster&text=TCP%20does%20error%20checking%20and,but%20it%20discards%20erroneous%20packets.)
|
||||
- [HTTP](https://en.wikipedia.org/wiki/Hypertext_Transfer_Protocol)
|
||||
|
|
@ -110,10 +136,12 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
|||
- [gRPC](https://en.wikipedia.org/wiki/GRPC)
|
||||
|
||||
##### Architecture
|
||||
|
||||
- [Microservices vs Monolithic](https://www.mulesoft.com/resources/api/microservices-vs-monolithic#:~:text=Microservices%20architecture%20vs%20monolithic%20architecture&text=A%20monolithic%20application%20is%20built%20as%20a%20single%20unit.&text=To%20make%20any%20alterations%20to,formally%20with%20business%2Doriented%20APIs.)
|
||||
- [MVVM](https://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93viewmodel) - Appwrite console architecture
|
||||
|
||||
##### Security
|
||||
|
||||
- [Appwrite Auth and ACL](https://github.com/appwrite/appwrite/blob/0.7.x/docs/specs/authentication.drawio.svg)
|
||||
- [OAuth](https://en.wikipedia.org/wiki/OAuth)
|
||||
- [Encryption](https://medium.com/searchencrypt/what-is-encryption-how-does-it-work-e8f20e340537#:~:text=Encryption%20is%20a%20process%20that,%2C%20or%20decrypt%2C%20the%20information.)
|
||||
|
|
@ -124,8 +152,8 @@ Learn more at our [Technology Stack](#technology-stack) section.
|
|||
Appwrite's current structure is a combination of both [Monolithic](https://en.wikipedia.org/wiki/Monolithic_application) and [Microservice](https://en.wikipedia.org/wiki/Microservices) architectures, but our final goal, as we grow, is to be using only microservices.
|
||||
|
||||
---
|
||||

|
||||
---
|
||||
|
||||
## 
|
||||
|
||||
### File Structure
|
||||
|
||||
|
|
@ -204,15 +232,15 @@ Appwrite stack is combined from a variety of open-source technologies and tools.
|
|||
|
||||
### Other Technologies
|
||||
|
||||
* Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data)
|
||||
* MariaDB - for database storage and queries
|
||||
* InfluxDB - for managing stats and time-series based data
|
||||
* Statsd - for sending data over UDP protocol (using Telegraf)
|
||||
* ClamAV - for validating and scanning storage files
|
||||
* Imagemagick - for manipulating and managing image media files.
|
||||
* Webp - for better compression of images on supporting clients
|
||||
* SMTP - for sending email messages and alerts
|
||||
* Resque - for managing data queues and scheduled tasks over a Redis server
|
||||
- Redis - for managing cache and in-memory data (currently, we do not use Redis for persistent data)
|
||||
- MariaDB - for database storage and queries
|
||||
- InfluxDB - for managing stats and time-series based data
|
||||
- Statsd - for sending data over UDP protocol (using Telegraf)
|
||||
- ClamAV - for validating and scanning storage files
|
||||
- Imagemagick - for manipulating and managing image media files.
|
||||
- Webp - for better compression of images on supporting clients
|
||||
- SMTP - for sending email messages and alerts
|
||||
- Resque - for managing data queues and scheduled tasks over a Redis server
|
||||
|
||||
## Package Managers
|
||||
|
||||
|
|
@ -224,7 +252,7 @@ Appwrite uses [PHP's Composer](https://getcomposer.org/) for managing dependenci
|
|||
|
||||
## Coding Standards
|
||||
|
||||
Appwrite is following the [PHP-FIG standards](https://www.php-fig.org/). Currently, we are using both PSR-0 and PSR-4 for coding standards and autoloading standards. Soon we will also review the project for support with PSR-12 (Extended Coding Style).
|
||||
Appwrite is following the [PHP-FIG standards](https://www.php-fig.org/). Currently, we are using both PSR-0 and PSR-12 for coding standards and autoloading standards.
|
||||
|
||||
We use prettier for our JS coding standards and auto-formatting our code.
|
||||
|
||||
|
|
@ -236,14 +264,14 @@ We wish Appwrite will be as easy to set up and in a single, localhost, and easy
|
|||
|
||||
When contributing code, please take into account the following considerations:
|
||||
|
||||
* Response Time
|
||||
* Throughput
|
||||
* Requests per Seconds
|
||||
* Network Usage
|
||||
* Memory Usage
|
||||
* Browser Rendering
|
||||
* Background Jobs
|
||||
* Task Execution Time
|
||||
- Response Time
|
||||
- Throughput
|
||||
- Requests per Seconds
|
||||
- Network Usage
|
||||
- Memory Usage
|
||||
- Browser Rendering
|
||||
- Background Jobs
|
||||
- Task Execution Time
|
||||
|
||||
## Security & Privacy
|
||||
|
||||
|
|
@ -280,6 +308,7 @@ Before running the command, make sure you have proper write permissions to the A
|
|||
```bash
|
||||
docker buildx build --platform linux/amd64,linux/arm64,linux/arm/v6,linux/arm/v7,linux/arm64/v8,linux/ppc64le,linux/s390x -t appwrite/appwrite:dev --push .
|
||||
```
|
||||
|
||||
**Build Functions Runtimes**
|
||||
|
||||
The Runtimes for all supported cloud functions (multicore builds) can be found at the [open-runtimes/open-runtimes](https://github.com/open-runtimes/open-runtimes) repository.
|
||||
|
|
@ -299,11 +328,11 @@ For generating a new console SDK follow the next steps:
|
|||
|
||||
Things to remember when releasing SDKs
|
||||
|
||||
* Update the Changelogs in **docs/sdks** (right now only Dart and Flutter are using these)
|
||||
* Update **GETTING_STARTED.md** in **docs/sdks** for each SDKs if any changes in the related APIs in there
|
||||
* Update SDK versions as required on **app/config/platforms.php**
|
||||
* Generate SDKs using the command `php app/cli.php sdks` and follow the instructions
|
||||
* Release new tags on GitHub repository for each SDKs
|
||||
- Update the Changelogs in **docs/sdks** (right now only Dart and Flutter are using these)
|
||||
- Update **GETTING_STARTED.md** in **docs/sdks** for each SDKs if any changes in the related APIs in there
|
||||
- Update SDK versions as required on **app/config/platforms.php**
|
||||
- Generate SDKs using the command `php app/cli.php sdks` and follow the instructions
|
||||
- Release new tags on GitHub repository for each SDKs
|
||||
|
||||
## Debug
|
||||
|
||||
|
|
@ -315,13 +344,13 @@ First, you need to create an init file. Duplicate **dev/yasd_init.php.stub** fil
|
|||
|
||||
```json
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9005,
|
||||
"pathMappings": {
|
||||
"/usr/src/code": "${workspaceRoot}"
|
||||
},
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9005,
|
||||
"pathMappings": {
|
||||
"/usr/src/code": "${workspaceRoot}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
|
|
@ -354,65 +383,62 @@ To run end-2-end tests for a spcific service use:
|
|||
```bash
|
||||
docker-compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
|
||||
```
|
||||
|
||||
## Benchmarking
|
||||
|
||||
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.
|
||||
|
||||
```
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
```
|
||||
Options:
|
||||
-c, --connections <N> Connections to keep open
|
||||
-d, --duration <T> Duration of test
|
||||
-t, --threads <N> Number of threads to use
|
||||
|
||||
-s, --script <S> Load Lua script file
|
||||
-H, --header <H> Add header to request
|
||||
--latency Print latency statistics
|
||||
--timeout <T> Socket/request timeout
|
||||
-v, --version Print version details
|
||||
```
|
||||
|
||||
```bash
|
||||
docker run --rm skandyla/wrk -t3 -c100 -d30 https://[APPWRITE_HOSTNAME_OR_IP]
|
||||
```
|
||||
|
||||
## Code Maintenance
|
||||
## Code Maintenance
|
||||
|
||||
We use some automation tools to help us keep a healthy codebase.
|
||||
|
||||
Improve PHP execution time by using [fully-qualified function calls](https://veewee.github.io/blog/optimizing-php-performance-by-fq-function-calls/):
|
||||
**Run Formatter:**
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix src/ --rules=native_function_invocation --allow-risky=yes
|
||||
# Run on all files
|
||||
./vendor/bin/phpcbf
|
||||
# Run on single file or folder
|
||||
./vendor/bin/phpcbf <your file path>
|
||||
```
|
||||
|
||||
Coding Standards:
|
||||
**Run Linter:**
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix app/controllers --rules='{"braces": {"allow_single_line_closure": true}}'
|
||||
```
|
||||
|
||||
```bash
|
||||
php-cs-fixer fix src --rules='{"braces": {"allow_single_line_closure": true}}'
|
||||
```
|
||||
|
||||
Static Code Analysis:
|
||||
|
||||
```bash
|
||||
docker-compose exec appwrite /usr/src/code/vendor/bin/psalm
|
||||
# Run on all files
|
||||
./vendor/bin/phpcs
|
||||
# Run on single file or folder
|
||||
./vendor/bin/phpcs <your file path>
|
||||
```
|
||||
|
||||
## Tutorials
|
||||
|
||||
From time to time, our team will add tutorials that will help contributors find their way in the Appwrite source code. Below is a list of currently available tutorials:
|
||||
|
||||
* [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
* [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
|
||||
* [Running in Production](./docs/tutorials/running-in-production.md)
|
||||
* [Adding Storage Adapter](./docs/tutorials/add-storage-adapter.md)
|
||||
- [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
- [Appwrite Environment Variables](./docs/tutorials/environment-variables.md)
|
||||
- [Running in Production](./docs/tutorials/running-in-production.md)
|
||||
- [Adding Storage Adapter](./docs/tutorials/add-storage-adapter.md)
|
||||
|
||||
## Other Ways to Help
|
||||
|
||||
Pull requests are great, but there are many other areas where you can help Appwrite.
|
||||
Pull requests are great, but there are many other areas where you can help Appwrite.
|
||||
|
||||
### Blogging & Speaking
|
||||
|
||||
|
|
@ -437,4 +463,3 @@ Submitting documentation updates, enhancements, designs, or bug fixes. Spelling
|
|||
### Helping Someone
|
||||
|
||||
Searching for Appwrite on Discord, GitHub, or StackOverflow and helping someone else who needs help. You can also help by teaching others how to contribute to Appwrite's repo!
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
require_once __DIR__.'/init.php';
|
||||
require_once __DIR__.'/controllers/general.php';
|
||||
|
||||
require_once __DIR__ . '/init.php';
|
||||
require_once __DIR__ . '/controllers/general.php';
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\CLI\CLI;
|
||||
|
|
@ -28,4 +29,4 @@ $cli
|
|||
Console::log(App::getEnv('_APP_VERSION', 'UNKNOWN'));
|
||||
});
|
||||
|
||||
$cli->run();
|
||||
$cli->run();
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?php
|
||||
<?php
|
||||
|
||||
// Auth methods
|
||||
|
||||
|
|
@ -45,4 +45,4 @@ return [
|
|||
'docs' => '',
|
||||
'enabled' => false,
|
||||
],
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,20 +2,20 @@
|
|||
|
||||
return [
|
||||
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
|
||||
'aa' => __DIR__.'/browsers/avant.png',
|
||||
'an' => __DIR__.'/browsers/android-webview-beta.png',
|
||||
'ch' => __DIR__.'/browsers/chrome.png',
|
||||
'ci' => __DIR__.'/browsers/chrome.png', //Chrome Mobile iOS
|
||||
'cm' => __DIR__.'/browsers/chrome.png', //Chrome Mobile
|
||||
'cr' => __DIR__.'/browsers/chromium.png',
|
||||
'ff' => __DIR__.'/browsers/firefox.png',
|
||||
'sf' => __DIR__.'/browsers/safari.png',
|
||||
'mf' => __DIR__.'/browsers/safari.png',
|
||||
'ps' => __DIR__.'/browsers/edge.png',
|
||||
'oi' => __DIR__.'/browsers/edge.png',
|
||||
'om' => __DIR__.'/browsers/opera-mini.png',
|
||||
'op' => __DIR__.'/browsers/opera.png',
|
||||
'on' => __DIR__.'/browsers/opera.png',
|
||||
'aa' => __DIR__ . '/browsers/avant.png',
|
||||
'an' => __DIR__ . '/browsers/android-webview-beta.png',
|
||||
'ch' => __DIR__ . '/browsers/chrome.png',
|
||||
'ci' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile iOS
|
||||
'cm' => __DIR__ . '/browsers/chrome.png', //Chrome Mobile
|
||||
'cr' => __DIR__ . '/browsers/chromium.png',
|
||||
'ff' => __DIR__ . '/browsers/firefox.png',
|
||||
'sf' => __DIR__ . '/browsers/safari.png',
|
||||
'mf' => __DIR__ . '/browsers/safari.png',
|
||||
'ps' => __DIR__ . '/browsers/edge.png',
|
||||
'oi' => __DIR__ . '/browsers/edge.png',
|
||||
'om' => __DIR__ . '/browsers/opera-mini.png',
|
||||
'op' => __DIR__ . '/browsers/opera.png',
|
||||
'on' => __DIR__ . '/browsers/opera.png',
|
||||
|
||||
/*
|
||||
'36' => '360 Phone Browser',
|
||||
|
|
|
|||
|
|
@ -1,20 +1,20 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'amex' => __DIR__.'/credit-cards/amex.png',
|
||||
'argencard' => __DIR__.'/credit-cards/argencard.png',
|
||||
'cabal' => __DIR__.'/credit-cards/cabal.png',
|
||||
'censosud' => __DIR__.'/credit-cards/consosud.png',
|
||||
'diners' => __DIR__.'/credit-cards/diners.png',
|
||||
'discover' => __DIR__.'/credit-cards/discover.png',
|
||||
'elo' => __DIR__.'/credit-cards/elo.png',
|
||||
'hipercard' => __DIR__.'/credit-cards/hipercard.png',
|
||||
'jcb' => __DIR__.'/credit-cards/jcb.png',
|
||||
'mastercard' => __DIR__.'/credit-cards/mastercard.png',
|
||||
'naranja' => __DIR__.'/credit-cards/naranja.png',
|
||||
'targeta-shopping' => __DIR__.'/credit-cards/tarjeta-shopping.png',
|
||||
'union-china-pay' => __DIR__.'/credit-cards/union-china-pay.png',
|
||||
'visa' => __DIR__.'/credit-cards/visa.png',
|
||||
'mir' => __DIR__.'/credit-cards/mir.png',
|
||||
'maestro' => __DIR__.'/credit-cards/maestro.png',
|
||||
'amex' => __DIR__ . '/credit-cards/amex.png',
|
||||
'argencard' => __DIR__ . '/credit-cards/argencard.png',
|
||||
'cabal' => __DIR__ . '/credit-cards/cabal.png',
|
||||
'censosud' => __DIR__ . '/credit-cards/consosud.png',
|
||||
'diners' => __DIR__ . '/credit-cards/diners.png',
|
||||
'discover' => __DIR__ . '/credit-cards/discover.png',
|
||||
'elo' => __DIR__ . '/credit-cards/elo.png',
|
||||
'hipercard' => __DIR__ . '/credit-cards/hipercard.png',
|
||||
'jcb' => __DIR__ . '/credit-cards/jcb.png',
|
||||
'mastercard' => __DIR__ . '/credit-cards/mastercard.png',
|
||||
'naranja' => __DIR__ . '/credit-cards/naranja.png',
|
||||
'targeta-shopping' => __DIR__ . '/credit-cards/tarjeta-shopping.png',
|
||||
'union-china-pay' => __DIR__ . '/credit-cards/union-china-pay.png',
|
||||
'visa' => __DIR__ . '/credit-cards/visa.png',
|
||||
'mir' => __DIR__ . '/credit-cards/mir.png',
|
||||
'maestro' => __DIR__ . '/credit-cards/maestro.png',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,198 +1,198 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
'af' => __DIR__.'/flags/af.png',
|
||||
'ao' => __DIR__.'/flags/ao.png',
|
||||
'al' => __DIR__.'/flags/al.png',
|
||||
'ad' => __DIR__.'/flags/ad.png',
|
||||
'ae' => __DIR__.'/flags/ae.png',
|
||||
'ar' => __DIR__.'/flags/ar.png',
|
||||
'am' => __DIR__.'/flags/am.png',
|
||||
'ag' => __DIR__.'/flags/ag.png',
|
||||
'au' => __DIR__.'/flags/au.png',
|
||||
'at' => __DIR__.'/flags/at.png',
|
||||
'az' => __DIR__.'/flags/az.png',
|
||||
'bi' => __DIR__.'/flags/bi.png',
|
||||
'be' => __DIR__.'/flags/be.png',
|
||||
'bj' => __DIR__.'/flags/bj.png',
|
||||
'bf' => __DIR__.'/flags/bf.png',
|
||||
'bd' => __DIR__.'/flags/bd.png',
|
||||
'bg' => __DIR__.'/flags/bg.png',
|
||||
'bh' => __DIR__.'/flags/bh.png',
|
||||
'bs' => __DIR__.'/flags/bs.png',
|
||||
'ba' => __DIR__.'/flags/ba.png',
|
||||
'by' => __DIR__.'/flags/by.png',
|
||||
'bz' => __DIR__.'/flags/bz.png',
|
||||
'bo' => __DIR__.'/flags/bo.png',
|
||||
'br' => __DIR__.'/flags/br.png',
|
||||
'bb' => __DIR__.'/flags/bb.png',
|
||||
'bn' => __DIR__.'/flags/bn.png',
|
||||
'bt' => __DIR__.'/flags/bt.png',
|
||||
'bw' => __DIR__.'/flags/bw.png',
|
||||
'cf' => __DIR__.'/flags/cf.png',
|
||||
'ca' => __DIR__.'/flags/ca.png',
|
||||
'ch' => __DIR__.'/flags/ch.png',
|
||||
'cl' => __DIR__.'/flags/cl.png',
|
||||
'cn' => __DIR__.'/flags/cn.png',
|
||||
'ci' => __DIR__.'/flags/ci.png',
|
||||
'cm' => __DIR__.'/flags/cm.png',
|
||||
'cd' => __DIR__.'/flags/cd.png',
|
||||
'cg' => __DIR__.'/flags/cg.png',
|
||||
'co' => __DIR__.'/flags/co.png',
|
||||
'km' => __DIR__.'/flags/km.png',
|
||||
'cv' => __DIR__.'/flags/cv.png',
|
||||
'cr' => __DIR__.'/flags/cr.png',
|
||||
'cu' => __DIR__.'/flags/cu.png',
|
||||
'cy' => __DIR__.'/flags/cy.png',
|
||||
'cz' => __DIR__.'/flags/cz.png',
|
||||
'de' => __DIR__.'/flags/de.png',
|
||||
'dj' => __DIR__.'/flags/dj.png',
|
||||
'dm' => __DIR__.'/flags/dm.png',
|
||||
'dk' => __DIR__.'/flags/dk.png',
|
||||
'do' => __DIR__.'/flags/do.png',
|
||||
'dz' => __DIR__.'/flags/dz.png',
|
||||
'ec' => __DIR__.'/flags/ec.png',
|
||||
'eg' => __DIR__.'/flags/eg.png',
|
||||
'er' => __DIR__.'/flags/er.png',
|
||||
'es' => __DIR__.'/flags/es.png',
|
||||
'ee' => __DIR__.'/flags/ee.png',
|
||||
'et' => __DIR__.'/flags/et.png',
|
||||
'fi' => __DIR__.'/flags/fi.png',
|
||||
'fj' => __DIR__.'/flags/fj.png',
|
||||
'fr' => __DIR__.'/flags/fr.png',
|
||||
'fm' => __DIR__.'/flags/fm.png',
|
||||
'ga' => __DIR__.'/flags/ga.png',
|
||||
'gb' => __DIR__.'/flags/gb.png',
|
||||
'ge' => __DIR__.'/flags/ge.png',
|
||||
'gh' => __DIR__.'/flags/gh.png',
|
||||
'gn' => __DIR__.'/flags/gn.png',
|
||||
'gm' => __DIR__.'/flags/gm.png',
|
||||
'gw' => __DIR__.'/flags/gw.png',
|
||||
'gq' => __DIR__.'/flags/gq.png',
|
||||
'gr' => __DIR__.'/flags/gr.png',
|
||||
'gd' => __DIR__.'/flags/gd.png',
|
||||
'gt' => __DIR__.'/flags/gt.png',
|
||||
'gy' => __DIR__.'/flags/gy.png',
|
||||
'hn' => __DIR__.'/flags/hn.png',
|
||||
'hr' => __DIR__.'/flags/hr.png',
|
||||
'ht' => __DIR__.'/flags/ht.png',
|
||||
'hu' => __DIR__.'/flags/hu.png',
|
||||
'id' => __DIR__.'/flags/id.png',
|
||||
'in' => __DIR__.'/flags/in.png',
|
||||
'ie' => __DIR__.'/flags/ie.png',
|
||||
'ir' => __DIR__.'/flags/ir.png',
|
||||
'iq' => __DIR__.'/flags/iq.png',
|
||||
'is' => __DIR__.'/flags/is.png',
|
||||
'il' => __DIR__.'/flags/il.png',
|
||||
'it' => __DIR__.'/flags/it.png',
|
||||
'jm' => __DIR__.'/flags/jm.png',
|
||||
'jo' => __DIR__.'/flags/jo.png',
|
||||
'jp' => __DIR__.'/flags/jp.png',
|
||||
'kz' => __DIR__.'/flags/kz.png',
|
||||
'ke' => __DIR__.'/flags/ke.png',
|
||||
'kg' => __DIR__.'/flags/kg.png',
|
||||
'kh' => __DIR__.'/flags/kh.png',
|
||||
'ki' => __DIR__.'/flags/ki.png',
|
||||
'kn' => __DIR__.'/flags/kn.png',
|
||||
'kr' => __DIR__.'/flags/kr.png',
|
||||
'kw' => __DIR__.'/flags/kw.png',
|
||||
'la' => __DIR__.'/flags/la.png',
|
||||
'lb' => __DIR__.'/flags/lb.png',
|
||||
'lr' => __DIR__.'/flags/lr.png',
|
||||
'ly' => __DIR__.'/flags/ly.png',
|
||||
'lc' => __DIR__.'/flags/lc.png',
|
||||
'li' => __DIR__.'/flags/li.png',
|
||||
'lk' => __DIR__.'/flags/lk.png',
|
||||
'ls' => __DIR__.'/flags/ls.png',
|
||||
'lt' => __DIR__.'/flags/ls.png',
|
||||
'lu' => __DIR__.'/flags/lu.png',
|
||||
'lv' => __DIR__.'/flags/lv.png',
|
||||
'ma' => __DIR__.'/flags/ma.png',
|
||||
'mc' => __DIR__.'/flags/mc.png',
|
||||
'md' => __DIR__.'/flags/md.png',
|
||||
'mg' => __DIR__.'/flags/mg.png',
|
||||
'mv' => __DIR__.'/flags/mv.png',
|
||||
'mx' => __DIR__.'/flags/mx.png',
|
||||
'mh' => __DIR__.'/flags/mh.png',
|
||||
'mk' => __DIR__.'/flags/mk.png',
|
||||
'ml' => __DIR__.'/flags/ml.png',
|
||||
'mt' => __DIR__.'/flags/mt.png',
|
||||
'mm' => __DIR__.'/flags/mm.png',
|
||||
'me' => __DIR__.'/flags/me.png',
|
||||
'mn' => __DIR__.'/flags/mn.png',
|
||||
'mz' => __DIR__.'/flags/mz.png',
|
||||
'mr' => __DIR__.'/flags/mr.png',
|
||||
'mu' => __DIR__.'/flags/mu.png',
|
||||
'mw' => __DIR__.'/flags/mw.png',
|
||||
'my' => __DIR__.'/flags/my.png',
|
||||
'na' => __DIR__.'/flags/na.png',
|
||||
'ne' => __DIR__.'/flags/ne.png',
|
||||
'ng' => __DIR__.'/flags/ng.png',
|
||||
'ni' => __DIR__.'/flags/ni.png',
|
||||
'nl' => __DIR__.'/flags/nl.png',
|
||||
'no' => __DIR__.'/flags/no.png',
|
||||
'np' => __DIR__.'/flags/np.png',
|
||||
'nr' => __DIR__.'/flags/nr.png',
|
||||
'nz' => __DIR__.'/flags/nz.png',
|
||||
'om' => __DIR__.'/flags/om.png',
|
||||
'pk' => __DIR__.'/flags/pk.png',
|
||||
'pa' => __DIR__.'/flags/pa.png',
|
||||
'pe' => __DIR__.'/flags/pe.png',
|
||||
'ph' => __DIR__.'/flags/ph.png',
|
||||
'pw' => __DIR__.'/flags/pw.png',
|
||||
'pg' => __DIR__.'/flags/pg.png',
|
||||
'pl' => __DIR__.'/flags/pl.png',
|
||||
'kp' => __DIR__.'/flags/kp.png',
|
||||
'pt' => __DIR__.'/flags/pt.png',
|
||||
'py' => __DIR__.'/flags/py.png',
|
||||
'qa' => __DIR__.'/flags/qa.png',
|
||||
'ro' => __DIR__.'/flags/ro.png',
|
||||
'ru' => __DIR__.'/flags/ru.png',
|
||||
'rw' => __DIR__.'/flags/rw.png',
|
||||
'sa' => __DIR__.'/flags/sa.png',
|
||||
'sd' => __DIR__.'/flags/sd.png',
|
||||
'sn' => __DIR__.'/flags/sn.png',
|
||||
'sg' => __DIR__.'/flags/sg.png',
|
||||
'sb' => __DIR__.'/flags/sb.png',
|
||||
'sl' => __DIR__.'/flags/sl.png',
|
||||
'sv' => __DIR__.'/flags/sv.png',
|
||||
'sm' => __DIR__.'/flags/sm.png',
|
||||
'so' => __DIR__.'/flags/so.png',
|
||||
'rs' => __DIR__.'/flags/rs.png',
|
||||
'ss' => __DIR__.'/flags/ss.png',
|
||||
'st' => __DIR__.'/flags/st.png',
|
||||
'sr' => __DIR__.'/flags/sr.png',
|
||||
'sk' => __DIR__.'/flags/sk.png',
|
||||
'si' => __DIR__.'/flags/si.png',
|
||||
'se' => __DIR__.'/flags/se.png',
|
||||
'sz' => __DIR__.'/flags/sz.png',
|
||||
'sc' => __DIR__.'/flags/sc.png',
|
||||
'sy' => __DIR__.'/flags/sy.png',
|
||||
'td' => __DIR__.'/flags/td.png',
|
||||
'tg' => __DIR__.'/flags/tg.png',
|
||||
'th' => __DIR__.'/flags/th.png',
|
||||
'tj' => __DIR__.'/flags/tj.png',
|
||||
'tm' => __DIR__.'/flags/tm.png',
|
||||
'tl' => __DIR__.'/flags/tl.png',
|
||||
'to' => __DIR__.'/flags/to.png',
|
||||
'tt' => __DIR__.'/flags/tt.png',
|
||||
'tn' => __DIR__.'/flags/tn.png',
|
||||
'tr' => __DIR__.'/flags/tr.png',
|
||||
'tv' => __DIR__.'/flags/tv.png',
|
||||
'tz' => __DIR__.'/flags/tz.png',
|
||||
'ug' => __DIR__.'/flags/ug.png',
|
||||
'ua' => __DIR__.'/flags/ua.png',
|
||||
'uy' => __DIR__.'/flags/uy.png',
|
||||
'us' => __DIR__.'/flags/us.png',
|
||||
'uz' => __DIR__.'/flags/uz.png',
|
||||
'va' => __DIR__.'/flags/va.png',
|
||||
'vc' => __DIR__.'/flags/vc.png',
|
||||
've' => __DIR__.'/flags/ve.png',
|
||||
'vn' => __DIR__.'/flags/vn.png',
|
||||
'vu' => __DIR__.'/flags/vu.png',
|
||||
'ws' => __DIR__.'/flags/ws.png',
|
||||
'ye' => __DIR__.'/flags/ye.png',
|
||||
'za' => __DIR__.'/flags/za.png',
|
||||
'zm' => __DIR__.'/flags/zm.png',
|
||||
'zw' => __DIR__.'/flags/zw.png',
|
||||
'af' => __DIR__ . '/flags/af.png',
|
||||
'ao' => __DIR__ . '/flags/ao.png',
|
||||
'al' => __DIR__ . '/flags/al.png',
|
||||
'ad' => __DIR__ . '/flags/ad.png',
|
||||
'ae' => __DIR__ . '/flags/ae.png',
|
||||
'ar' => __DIR__ . '/flags/ar.png',
|
||||
'am' => __DIR__ . '/flags/am.png',
|
||||
'ag' => __DIR__ . '/flags/ag.png',
|
||||
'au' => __DIR__ . '/flags/au.png',
|
||||
'at' => __DIR__ . '/flags/at.png',
|
||||
'az' => __DIR__ . '/flags/az.png',
|
||||
'bi' => __DIR__ . '/flags/bi.png',
|
||||
'be' => __DIR__ . '/flags/be.png',
|
||||
'bj' => __DIR__ . '/flags/bj.png',
|
||||
'bf' => __DIR__ . '/flags/bf.png',
|
||||
'bd' => __DIR__ . '/flags/bd.png',
|
||||
'bg' => __DIR__ . '/flags/bg.png',
|
||||
'bh' => __DIR__ . '/flags/bh.png',
|
||||
'bs' => __DIR__ . '/flags/bs.png',
|
||||
'ba' => __DIR__ . '/flags/ba.png',
|
||||
'by' => __DIR__ . '/flags/by.png',
|
||||
'bz' => __DIR__ . '/flags/bz.png',
|
||||
'bo' => __DIR__ . '/flags/bo.png',
|
||||
'br' => __DIR__ . '/flags/br.png',
|
||||
'bb' => __DIR__ . '/flags/bb.png',
|
||||
'bn' => __DIR__ . '/flags/bn.png',
|
||||
'bt' => __DIR__ . '/flags/bt.png',
|
||||
'bw' => __DIR__ . '/flags/bw.png',
|
||||
'cf' => __DIR__ . '/flags/cf.png',
|
||||
'ca' => __DIR__ . '/flags/ca.png',
|
||||
'ch' => __DIR__ . '/flags/ch.png',
|
||||
'cl' => __DIR__ . '/flags/cl.png',
|
||||
'cn' => __DIR__ . '/flags/cn.png',
|
||||
'ci' => __DIR__ . '/flags/ci.png',
|
||||
'cm' => __DIR__ . '/flags/cm.png',
|
||||
'cd' => __DIR__ . '/flags/cd.png',
|
||||
'cg' => __DIR__ . '/flags/cg.png',
|
||||
'co' => __DIR__ . '/flags/co.png',
|
||||
'km' => __DIR__ . '/flags/km.png',
|
||||
'cv' => __DIR__ . '/flags/cv.png',
|
||||
'cr' => __DIR__ . '/flags/cr.png',
|
||||
'cu' => __DIR__ . '/flags/cu.png',
|
||||
'cy' => __DIR__ . '/flags/cy.png',
|
||||
'cz' => __DIR__ . '/flags/cz.png',
|
||||
'de' => __DIR__ . '/flags/de.png',
|
||||
'dj' => __DIR__ . '/flags/dj.png',
|
||||
'dm' => __DIR__ . '/flags/dm.png',
|
||||
'dk' => __DIR__ . '/flags/dk.png',
|
||||
'do' => __DIR__ . '/flags/do.png',
|
||||
'dz' => __DIR__ . '/flags/dz.png',
|
||||
'ec' => __DIR__ . '/flags/ec.png',
|
||||
'eg' => __DIR__ . '/flags/eg.png',
|
||||
'er' => __DIR__ . '/flags/er.png',
|
||||
'es' => __DIR__ . '/flags/es.png',
|
||||
'ee' => __DIR__ . '/flags/ee.png',
|
||||
'et' => __DIR__ . '/flags/et.png',
|
||||
'fi' => __DIR__ . '/flags/fi.png',
|
||||
'fj' => __DIR__ . '/flags/fj.png',
|
||||
'fr' => __DIR__ . '/flags/fr.png',
|
||||
'fm' => __DIR__ . '/flags/fm.png',
|
||||
'ga' => __DIR__ . '/flags/ga.png',
|
||||
'gb' => __DIR__ . '/flags/gb.png',
|
||||
'ge' => __DIR__ . '/flags/ge.png',
|
||||
'gh' => __DIR__ . '/flags/gh.png',
|
||||
'gn' => __DIR__ . '/flags/gn.png',
|
||||
'gm' => __DIR__ . '/flags/gm.png',
|
||||
'gw' => __DIR__ . '/flags/gw.png',
|
||||
'gq' => __DIR__ . '/flags/gq.png',
|
||||
'gr' => __DIR__ . '/flags/gr.png',
|
||||
'gd' => __DIR__ . '/flags/gd.png',
|
||||
'gt' => __DIR__ . '/flags/gt.png',
|
||||
'gy' => __DIR__ . '/flags/gy.png',
|
||||
'hn' => __DIR__ . '/flags/hn.png',
|
||||
'hr' => __DIR__ . '/flags/hr.png',
|
||||
'ht' => __DIR__ . '/flags/ht.png',
|
||||
'hu' => __DIR__ . '/flags/hu.png',
|
||||
'id' => __DIR__ . '/flags/id.png',
|
||||
'in' => __DIR__ . '/flags/in.png',
|
||||
'ie' => __DIR__ . '/flags/ie.png',
|
||||
'ir' => __DIR__ . '/flags/ir.png',
|
||||
'iq' => __DIR__ . '/flags/iq.png',
|
||||
'is' => __DIR__ . '/flags/is.png',
|
||||
'il' => __DIR__ . '/flags/il.png',
|
||||
'it' => __DIR__ . '/flags/it.png',
|
||||
'jm' => __DIR__ . '/flags/jm.png',
|
||||
'jo' => __DIR__ . '/flags/jo.png',
|
||||
'jp' => __DIR__ . '/flags/jp.png',
|
||||
'kz' => __DIR__ . '/flags/kz.png',
|
||||
'ke' => __DIR__ . '/flags/ke.png',
|
||||
'kg' => __DIR__ . '/flags/kg.png',
|
||||
'kh' => __DIR__ . '/flags/kh.png',
|
||||
'ki' => __DIR__ . '/flags/ki.png',
|
||||
'kn' => __DIR__ . '/flags/kn.png',
|
||||
'kr' => __DIR__ . '/flags/kr.png',
|
||||
'kw' => __DIR__ . '/flags/kw.png',
|
||||
'la' => __DIR__ . '/flags/la.png',
|
||||
'lb' => __DIR__ . '/flags/lb.png',
|
||||
'lr' => __DIR__ . '/flags/lr.png',
|
||||
'ly' => __DIR__ . '/flags/ly.png',
|
||||
'lc' => __DIR__ . '/flags/lc.png',
|
||||
'li' => __DIR__ . '/flags/li.png',
|
||||
'lk' => __DIR__ . '/flags/lk.png',
|
||||
'ls' => __DIR__ . '/flags/ls.png',
|
||||
'lt' => __DIR__ . '/flags/ls.png',
|
||||
'lu' => __DIR__ . '/flags/lu.png',
|
||||
'lv' => __DIR__ . '/flags/lv.png',
|
||||
'ma' => __DIR__ . '/flags/ma.png',
|
||||
'mc' => __DIR__ . '/flags/mc.png',
|
||||
'md' => __DIR__ . '/flags/md.png',
|
||||
'mg' => __DIR__ . '/flags/mg.png',
|
||||
'mv' => __DIR__ . '/flags/mv.png',
|
||||
'mx' => __DIR__ . '/flags/mx.png',
|
||||
'mh' => __DIR__ . '/flags/mh.png',
|
||||
'mk' => __DIR__ . '/flags/mk.png',
|
||||
'ml' => __DIR__ . '/flags/ml.png',
|
||||
'mt' => __DIR__ . '/flags/mt.png',
|
||||
'mm' => __DIR__ . '/flags/mm.png',
|
||||
'me' => __DIR__ . '/flags/me.png',
|
||||
'mn' => __DIR__ . '/flags/mn.png',
|
||||
'mz' => __DIR__ . '/flags/mz.png',
|
||||
'mr' => __DIR__ . '/flags/mr.png',
|
||||
'mu' => __DIR__ . '/flags/mu.png',
|
||||
'mw' => __DIR__ . '/flags/mw.png',
|
||||
'my' => __DIR__ . '/flags/my.png',
|
||||
'na' => __DIR__ . '/flags/na.png',
|
||||
'ne' => __DIR__ . '/flags/ne.png',
|
||||
'ng' => __DIR__ . '/flags/ng.png',
|
||||
'ni' => __DIR__ . '/flags/ni.png',
|
||||
'nl' => __DIR__ . '/flags/nl.png',
|
||||
'no' => __DIR__ . '/flags/no.png',
|
||||
'np' => __DIR__ . '/flags/np.png',
|
||||
'nr' => __DIR__ . '/flags/nr.png',
|
||||
'nz' => __DIR__ . '/flags/nz.png',
|
||||
'om' => __DIR__ . '/flags/om.png',
|
||||
'pk' => __DIR__ . '/flags/pk.png',
|
||||
'pa' => __DIR__ . '/flags/pa.png',
|
||||
'pe' => __DIR__ . '/flags/pe.png',
|
||||
'ph' => __DIR__ . '/flags/ph.png',
|
||||
'pw' => __DIR__ . '/flags/pw.png',
|
||||
'pg' => __DIR__ . '/flags/pg.png',
|
||||
'pl' => __DIR__ . '/flags/pl.png',
|
||||
'kp' => __DIR__ . '/flags/kp.png',
|
||||
'pt' => __DIR__ . '/flags/pt.png',
|
||||
'py' => __DIR__ . '/flags/py.png',
|
||||
'qa' => __DIR__ . '/flags/qa.png',
|
||||
'ro' => __DIR__ . '/flags/ro.png',
|
||||
'ru' => __DIR__ . '/flags/ru.png',
|
||||
'rw' => __DIR__ . '/flags/rw.png',
|
||||
'sa' => __DIR__ . '/flags/sa.png',
|
||||
'sd' => __DIR__ . '/flags/sd.png',
|
||||
'sn' => __DIR__ . '/flags/sn.png',
|
||||
'sg' => __DIR__ . '/flags/sg.png',
|
||||
'sb' => __DIR__ . '/flags/sb.png',
|
||||
'sl' => __DIR__ . '/flags/sl.png',
|
||||
'sv' => __DIR__ . '/flags/sv.png',
|
||||
'sm' => __DIR__ . '/flags/sm.png',
|
||||
'so' => __DIR__ . '/flags/so.png',
|
||||
'rs' => __DIR__ . '/flags/rs.png',
|
||||
'ss' => __DIR__ . '/flags/ss.png',
|
||||
'st' => __DIR__ . '/flags/st.png',
|
||||
'sr' => __DIR__ . '/flags/sr.png',
|
||||
'sk' => __DIR__ . '/flags/sk.png',
|
||||
'si' => __DIR__ . '/flags/si.png',
|
||||
'se' => __DIR__ . '/flags/se.png',
|
||||
'sz' => __DIR__ . '/flags/sz.png',
|
||||
'sc' => __DIR__ . '/flags/sc.png',
|
||||
'sy' => __DIR__ . '/flags/sy.png',
|
||||
'td' => __DIR__ . '/flags/td.png',
|
||||
'tg' => __DIR__ . '/flags/tg.png',
|
||||
'th' => __DIR__ . '/flags/th.png',
|
||||
'tj' => __DIR__ . '/flags/tj.png',
|
||||
'tm' => __DIR__ . '/flags/tm.png',
|
||||
'tl' => __DIR__ . '/flags/tl.png',
|
||||
'to' => __DIR__ . '/flags/to.png',
|
||||
'tt' => __DIR__ . '/flags/tt.png',
|
||||
'tn' => __DIR__ . '/flags/tn.png',
|
||||
'tr' => __DIR__ . '/flags/tr.png',
|
||||
'tv' => __DIR__ . '/flags/tv.png',
|
||||
'tz' => __DIR__ . '/flags/tz.png',
|
||||
'ug' => __DIR__ . '/flags/ug.png',
|
||||
'ua' => __DIR__ . '/flags/ua.png',
|
||||
'uy' => __DIR__ . '/flags/uy.png',
|
||||
'us' => __DIR__ . '/flags/us.png',
|
||||
'uz' => __DIR__ . '/flags/uz.png',
|
||||
'va' => __DIR__ . '/flags/va.png',
|
||||
'vc' => __DIR__ . '/flags/vc.png',
|
||||
've' => __DIR__ . '/flags/ve.png',
|
||||
'vn' => __DIR__ . '/flags/vn.png',
|
||||
'vu' => __DIR__ . '/flags/vu.png',
|
||||
'ws' => __DIR__ . '/flags/ws.png',
|
||||
'ye' => __DIR__ . '/flags/ye.png',
|
||||
'za' => __DIR__ . '/flags/za.png',
|
||||
'zm' => __DIR__ . '/flags/zm.png',
|
||||
'zw' => __DIR__ . '/flags/zw.png',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@
|
|||
|
||||
return [
|
||||
// Codes based on: https://github.com/matomo-org/device-detector/blob/master/Parser/Client/Browser.php
|
||||
'AND' => __DIR__.'/os/android.png',
|
||||
'ATV' => __DIR__.'/os/apple-tv.png',
|
||||
'COS' => __DIR__.'/os/chrome-os.png',
|
||||
'AND' => __DIR__ . '/os/android.png',
|
||||
'ATV' => __DIR__ . '/os/apple-tv.png',
|
||||
'COS' => __DIR__ . '/os/chrome-os.png',
|
||||
|
||||
/*
|
||||
'AIX' => 'AIX',
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ $collections = [
|
|||
'size' => 16384,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => new stdClass,
|
||||
'default' => new stdClass(),
|
||||
'array' => false,
|
||||
'filters' => ['json', 'range', 'enum'],
|
||||
],
|
||||
|
|
@ -931,6 +931,17 @@ $collections = [
|
|||
'array' => true,
|
||||
'filters' => [],
|
||||
],
|
||||
[
|
||||
'$id' => 'signatureKey',
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => [],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
[
|
||||
|
|
@ -2055,7 +2066,7 @@ $collections = [
|
|||
],
|
||||
[
|
||||
'$id' => 'status',
|
||||
'type' => Database::VAR_STRING,
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
|
|
@ -2099,7 +2110,7 @@ $collections = [
|
|||
],
|
||||
[
|
||||
'$id' => 'sourceType',
|
||||
'type' => Database::VAR_STRING,
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
|
|
@ -2110,7 +2121,7 @@ $collections = [
|
|||
],
|
||||
[
|
||||
'$id' => 'source',
|
||||
'type' => Database::VAR_STRING,
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 2048,
|
||||
'signed' => true,
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* List of server wide error codes and their respective messages.
|
||||
* List of server wide error codes and their respective messages.
|
||||
*/
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
|
|
@ -11,17 +11,17 @@ return [
|
|||
Exception::GENERAL_UNKNOWN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN,
|
||||
'description' => 'An unknown error has occured. Please check the logs for more information.',
|
||||
'code' => 500,
|
||||
'code' => 500,
|
||||
],
|
||||
Exception::GENERAL_MOCK => [
|
||||
'name' => Exception::GENERAL_MOCK,
|
||||
'description' => 'General errors thrown by the mock controller used for testing.',
|
||||
'code' => 400,
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GENERAL_ACCESS_FORBIDDEN => [
|
||||
'name' => Exception::GENERAL_ACCESS_FORBIDDEN,
|
||||
'description' => 'Access to this API is forbidden.',
|
||||
'code' => 401,
|
||||
'code' => 401,
|
||||
],
|
||||
Exception::GENERAL_UNKNOWN_ORIGIN => [
|
||||
'name' => Exception::GENERAL_UNKNOWN_ORIGIN,
|
||||
|
|
@ -81,7 +81,7 @@ return [
|
|||
Exception::GENERAL_PROTOCOL_UNSUPPORTED => [
|
||||
'name' => Exception::GENERAL_PROTOCOL_UNSUPPORTED,
|
||||
'description' => 'The request cannot be fulfilled with the current protocol. Please check the value of the _APP_OPTIONS_FORCE_HTTPS environment variable.',
|
||||
'code' => 500,
|
||||
'code' => 500,
|
||||
],
|
||||
|
||||
/** User Errors */
|
||||
|
|
@ -497,4 +497,4 @@ return [
|
|||
'description' => 'Domain verification for the requested domain has failed.',
|
||||
'code' => 401,
|
||||
]
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ return [
|
|||
'or', // Oriya
|
||||
'tl', // Filipino
|
||||
'pl', // Polish
|
||||
'pt-br', // Portuguese - Brazil
|
||||
'pt-br', // Portuguese - Brazil
|
||||
'pt-pt', // Portuguese - Portugal
|
||||
'pa', // Punjabi
|
||||
'ro', // Romanian
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@
|
|||
"emails.certificate.subject": "Certificate failure for %s",
|
||||
"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 willl 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.",
|
||||
"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.",
|
||||
"emails.certificate.thanks": "Thanks",
|
||||
"emails.certificate.signature": "{{project}} team",
|
||||
"locale.country.unknown": "Unknown",
|
||||
|
|
@ -235,4 +235,4 @@
|
|||
"continents.na": "North America",
|
||||
"continents.oc": "Oceania",
|
||||
"continents.sa": "South America"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,12 +18,12 @@
|
|||
"emails.recovery.subject": "രഹസ്യവാക്ക് പുനക്രമീകരണം",
|
||||
"emails.recovery.hello": "നമസ്കാരം {{name}}",
|
||||
"emails.recovery.body": "നിങ്ങളുടെ {{Project}} രഹസ്യവാക്ക് പുനക്രമീകരിക്കുന്നതിന് ഈ ലിങ്ക് പിന്തുടരുക.",
|
||||
"emails.recovery.footer": "നിങ്ങളുടെ പാസ്വേഡ് പുനക്രമീകരിക്കാന് നിങ്ങൾ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.recovery.footer": "നിങ്ങളുടെ രഹസ്യവാക്ക് പുനക്രമീകരിക്കാന് നിങ്ങൾ ആവശ്യപ്പെട്ടില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.recovery.thanks": "നന്ദി",
|
||||
"emails.recovery.signature": "{{project}} ടീം",
|
||||
"emails.invitation.subject": "%s -ലെ %s ടീമിലേക്കുള്ള ക്ഷണം",
|
||||
"emails.invitation.hello": "നമസ്കാരം",
|
||||
"emails.invitation.body": "നിങ്ങളെ {{project}} -ലെ {{team}} ടീമിലെ അംഗമാകുവാന് ക്ഷണിക്കാൻ {{owner}} ആഗ്രഹിക്കുതിനാലാണ് ഈ മെയിൽ നിങ്ങൾക്ക് അയക്കുന്നത്.",
|
||||
"emails.invitation.body": "നിങ്ങളെ {{project}} -ലെ {{team}} ടീമിലെ അംഗമാകുവാന് ക്ഷണിക്കാൻ {{owner}} ആഗ്രഹിക്കുന്നതിനാലാണ് ഈ മെയിൽ നിങ്ങൾക്ക് അയക്കുന്നത്.",
|
||||
"emails.invitation.footer": "നിങ്ങൾക്ക് താൽപ്പര്യമില്ലെങ്കിൽ, ഈ സന്ദേശം അവഗണിക്കാവുന്നതാണ്.",
|
||||
"emails.invitation.thanks": "നന്ദി",
|
||||
"emails.invitation.signature": "{{project}} ടീം",
|
||||
|
|
@ -229,4 +229,4 @@
|
|||
"continents.na": "വടക്കേ അമേരിക്ക",
|
||||
"continents.oc": "ഓഷ്യാനിയ",
|
||||
"continents.sa": "തെക്കേ അമേരിക്ക"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
/**
|
||||
* List of Appwrite Cloud Functions supported runtimes
|
||||
*/
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
||||
$runtimes = new Runtimes('v1');
|
||||
|
||||
$allowList = empty(App::getEnv('_APP_FUNCTIONS_RUNTIMES')) ? [] : \explode(',', App::getEnv('_APP_FUNCTIONS_RUNTIMES'));
|
||||
|
||||
$runtimes = $runtimes->getAll(true, $allowList);
|
||||
|
||||
return $runtimes;
|
||||
return $runtimes;
|
||||
|
|
|
|||
|
|
@ -70,4 +70,4 @@ return [ // List of publicly visible scopes
|
|||
'health.read' => [
|
||||
'description' => 'Access to read your project\'s health status',
|
||||
],
|
||||
];;
|
||||
];
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ return [
|
|||
'avatars' => [
|
||||
'key' => 'avatars',
|
||||
'name' => 'Avatars',
|
||||
'subtitle'=> 'The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.',
|
||||
'subtitle' => 'The Avatars service aims to help you complete everyday tasks related to your app image, icons, and avatars.',
|
||||
'description' => '/docs/services/avatars.md',
|
||||
'controller' => 'api/avatars.php',
|
||||
'sdk' => true,
|
||||
|
|
|
|||
|
|
@ -5,4 +5,4 @@ return [ // Accepted inputs files
|
|||
'jpeg' => 'image/jpeg',
|
||||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,18 +1,22 @@
|
|||
<?php
|
||||
|
||||
return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
||||
'default' => __DIR__.'/logos/none.png',
|
||||
'default_image' => __DIR__.'/logos/image.png',
|
||||
|
||||
'default' => __DIR__ . '/logos/none.png',
|
||||
'default_image' => __DIR__ . '/logos/image.png',
|
||||
|
||||
// Video Files
|
||||
'video/mp4' => __DIR__.'/logos/video.png',
|
||||
'video/x-flv' => __DIR__.'/logos/video.png',
|
||||
'application/x-mpegURL' => __DIR__.'/logos/video.png',
|
||||
'video/MP2T' => __DIR__.'/logos/video.png',
|
||||
'video/3gpp' => __DIR__.'/logos/video.png',
|
||||
'video/quicktime' => __DIR__.'/logos/video.png',
|
||||
'video/x-msvideo' => __DIR__.'/logos/video.png',
|
||||
'video/x-ms-wmv' => __DIR__.'/logos/video.png',
|
||||
|
||||
'video/mp4' => __DIR__ . '/logos/video.png',
|
||||
'video/x-flv' => __DIR__ . '/logos/video.png',
|
||||
'video/webm' => __DIR__ . '/logos/video.png',
|
||||
'application/x-mpegURL' => __DIR__ . '/logos/video.png',
|
||||
'video/MP2T' => __DIR__ . '/logos/video.png',
|
||||
'video/3gpp' => __DIR__ . '/logos/video.png',
|
||||
'video/quicktime' => __DIR__ . '/logos/video.png',
|
||||
'video/x-msvideo' => __DIR__ . '/logos/video.png',
|
||||
'video/x-ms-wmv' => __DIR__ . '/logos/video.png',
|
||||
|
||||
|
||||
|
||||
// // Microsoft Word
|
||||
// 'application/msword' => __DIR__.'/logos/word.png',
|
||||
|
|
@ -41,4 +45,4 @@ return [ // Based on this list @see http://stackoverflow.com/a/4212908/2299554
|
|||
|
||||
// Adobe PDF
|
||||
// 'application/pdf' => __DIR__.'/logos/pdf.png',
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ return [
|
|||
// Video Files
|
||||
'video/mp4',
|
||||
'video/x-flv',
|
||||
'video/webm',
|
||||
'application/x-mpegURL',
|
||||
'video/MP2T',
|
||||
'video/3gpp',
|
||||
|
|
@ -30,7 +31,7 @@ return [
|
|||
'audio/ogg', // Ogg Vorbis RFC 5334
|
||||
'audio/vorbis', // Vorbis RFC 5215
|
||||
'audio/vnd.wav', // wav RFC 2361
|
||||
|
||||
|
||||
// Microsoft Word
|
||||
'application/msword',
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
||||
|
|
@ -61,4 +62,4 @@ return [
|
|||
|
||||
// Adobe PDF
|
||||
'application/pdf',
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -6,4 +6,4 @@ return [ // Accepted outputs files
|
|||
'gif' => 'image/gif',
|
||||
'png' => 'image/png',
|
||||
'webp' => 'image/webp',
|
||||
];
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Utopia\Config\Config;
|
||||
|
||||
return [
|
||||
[
|
||||
'category' => 'General',
|
||||
|
|
|
|||
|
|
@ -4,22 +4,30 @@ use Ahc\Jwt\JWT;
|
|||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Network\Validator\URL;
|
||||
use Appwrite\OpenSSL\OpenSSL;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Appwrite\Template\Template;
|
||||
use Appwrite\URL\URL as URLParser;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use MaxMind\Db\Reader;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Appwrite\Event\Audit;
|
||||
use Utopia\Audit\Audit as EventAudit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Locale\Locale;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Assoc;
|
||||
|
|
@ -55,14 +63,7 @@ App::post('/v1/account')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $email, $password, $name, $request, $response, $project, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $email, string $password, string $name, Request $request, Response $response, Document $project, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$email = \strtolower($email);
|
||||
if ('console' === $project->getId()) {
|
||||
|
|
@ -117,7 +118,7 @@ App::post('/v1/account')
|
|||
Authorization::setRole('role:' . Auth::USER_ROLE_MEMBER);
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
|
|
@ -153,22 +154,13 @@ App::post('/v1/account/sessions')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($email, $password, $request, $response, $dbForProject, $locale, $geodb, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $email, string $password, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$email = \strtolower($email);
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
$profile = $dbForProject->findOne('users', [
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]
|
||||
);
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]);
|
||||
|
||||
if (!$profile || !Auth::passwordVerify($password, $profile->getAttribute('password'))) {
|
||||
throw new Exception('Invalid credentials', 401, Exception::USER_INVALID_CREDENTIALS); // Wrong password or username
|
||||
|
|
@ -193,20 +185,22 @@ App::post('/v1/account/sessions')
|
|||
'userAgent' => $request->getUserAgent('UNKNOWN'),
|
||||
'ip' => $request->getIP(),
|
||||
'countryCode' => ($record) ? \strtolower($record['country']['iso_code']) : '--',
|
||||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()
|
||||
],
|
||||
$detector->getOS(),
|
||||
$detector->getClient(),
|
||||
$detector->getDevice()
|
||||
));
|
||||
|
||||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $profile->getId()])
|
||||
->setAttribute('$write', ['user:' . $profile->getId()])
|
||||
);
|
||||
->setAttribute('$write', ['user:' . $profile->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$profile->getId())
|
||||
->setResource('user/' . $profile->getId())
|
||||
->setUser($profile)
|
||||
;
|
||||
|
||||
|
|
@ -222,7 +216,7 @@ App::post('/v1/account/sessions')
|
|||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
;
|
||||
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
|
|
@ -257,22 +251,19 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
->label('sdk.methodType', 'webAuth')
|
||||
->label('abuse-limit', 50)
|
||||
->label('abuse-key', 'ip:{ip}')
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), function($node) {return (!$node['mock']);}))).'.')
|
||||
->param('success', '', function ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->param('failure', '', function ($clients) { return new Host($clients); }, 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->param('provider', '', new WhiteList(\array_keys(Config::getParam('providers')), true), 'OAuth2 Provider. Currently, supported providers are: ' . \implode(', ', \array_keys(\array_filter(Config::getParam('providers'), fn($node) => (!$node['mock'])))) . '.')
|
||||
->param('success', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a successful login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->param('failure', '', fn($clients) => new Host($clients), 'URL to redirect back to your app after a failed login attempt. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->param('scopes', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'A list of custom OAuth2 scopes. Check each provider internal docs for a list of supported scopes. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed, each 128 characters long.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
->action(function ($provider, $success, $failure, $scopes, $request, $response, $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
->action(function (string $provider, string $success, string $failure, array $scopes, Request $request, Response $response, Document $project) use ($oauthDefaultSuccess, $oauthDefaultFailure) {
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$callback = $protocol.'://'.$request->getHostname().'/v1/account/sessions/oauth2/callback/'.$provider.'/'.$project->getId();
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
|
||||
if (!empty($appSecret) && isset($appSecret['version'])) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
|
||||
|
|
@ -283,17 +274,17 @@ App::get('/v1/account/sessions/oauth2/:provider')
|
|||
throw new Exception('This provider is disabled. Please configure the provider app ID and app secret key from your ' . APP_NAME . ' console to continue.', 412, Exception::PROJECT_PROVIDER_DISABLED);
|
||||
}
|
||||
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
}
|
||||
|
||||
if(empty($success)) {
|
||||
if (empty($success)) {
|
||||
$success = $protocol . '://' . $request->getHostname() . $oauthDefaultSuccess;
|
||||
}
|
||||
|
||||
if(empty($failure)) {
|
||||
if (empty($failure)) {
|
||||
$failure = $protocol . '://' . $request->getHostname() . $oauthDefaultFailure;
|
||||
}
|
||||
|
||||
|
|
@ -317,9 +308,7 @@ App::get('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($projectId, $provider, $code, $state, $request, $response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $projectId, string $provider, string $code, string $state, Request $request, Response $response) {
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
|
|
@ -344,9 +333,7 @@ App::post('/v1/account/sessions/oauth2/callback/:provider/:projectId')
|
|||
->param('state', '', new Text(2048), 'Login state params.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($projectId, $provider, $code, $state, $request, $response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $projectId, string $provider, string $code, string $state, Request $request, Response $response) {
|
||||
|
||||
$domain = $request->getHostname();
|
||||
$protocol = $request->getProtocol();
|
||||
|
|
@ -379,23 +366,14 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($provider, $code, $state, $request, $response, $project, $user, $dbForProject, $geodb, $audits, $events, $usage) use ($oauthDefaultSuccess) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $provider, string $code, string $state, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Reader $geodb, Audit $audits, Event $events, Stats $usage) use ($oauthDefaultSuccess) {
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$callback = $protocol . '://' . $request->getHostname() . '/v1/account/sessions/oauth2/callback/' . $provider . '/' . $project->getId();
|
||||
$defaultState = ['success' => $project->getAttribute('url', ''), 'failure' => ''];
|
||||
$validateURL = new URL();
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
|
||||
if (!empty($appSecret) && isset($appSecret['version'])) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $appSecret['version']);
|
||||
|
|
@ -431,7 +409,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$state['failure'] = null;
|
||||
|
||||
$accessToken = $oauth2->getAccessToken($code);
|
||||
$refreshToken =$oauth2->getRefreshToken($code);
|
||||
$refreshToken = $oauth2->getRefreshToken($code);
|
||||
$accessTokenExpiry = $oauth2->getAccessTokenExpiry($code);
|
||||
|
||||
if (empty($accessToken)) {
|
||||
|
|
@ -457,7 +435,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
|
||||
if ($current) { // Delete current session of new one.
|
||||
$currentDocument = $dbForProject->getDocument('sessions', $current);
|
||||
if(!$currentDocument->isEmpty()) {
|
||||
if (!$currentDocument->isEmpty()) {
|
||||
$dbForProject->deleteDocument('sessions', $currentDocument->getId());
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
}
|
||||
|
|
@ -478,8 +456,7 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
$isVerified = $oauth2->isEmailVerified($accessToken);
|
||||
|
||||
$user = $dbForProject->findOne('users', [
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]
|
||||
);
|
||||
new Query('email', Query::TYPE_EQUAL, [$email])]);
|
||||
|
||||
if ($user === false || $user->isEmpty()) { // Last option -> create the user, generate random password
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
|
@ -561,20 +538,19 @@ App::get('/v1/account/sessions/oauth2/:provider/redirect')
|
|||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()])
|
||||
);
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
$usage
|
||||
->setParam('users.sessions.create', 1)
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('provider', 'oauth2-'.$provider)
|
||||
->setParam('provider', 'oauth2-' . $provider)
|
||||
;
|
||||
|
||||
$events
|
||||
|
|
@ -625,7 +601,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
->label('abuse-key', 'url:{url},email:{param-email}')
|
||||
->param('userId', '', new CustomId(), 'Unique Id. Choose your own unique ID or pass the string "unique()" to auto generate it. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.')
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the magic URL login. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', true, ['clients'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
@ -634,17 +610,9 @@ App::post('/v1/account/sessions/magic-url')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('mails')
|
||||
->action(function ($userId, $email, $url, $request, $response, $project, $dbForProject, $locale, $audits, $events, $mails) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Mail $mails */
|
||||
->action(function (string $userId, string $email, string $url, Request $request, Response $response, Document $project, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails) {
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
|
|
@ -700,17 +668,16 @@ App::post('/v1/account/sessions/magic-url')
|
|||
'ip' => $request->getIP(),
|
||||
]);
|
||||
|
||||
Authorization::setRole('user:'.$user->getId());
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
|
||||
$token = $dbForProject->createDocument('tokens', $token
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
if(empty($url)) {
|
||||
$url = $request->getProtocol().'://'.$request->getHostname().'/auth/magic-url';
|
||||
if (empty($url)) {
|
||||
$url = $request->getProtocol() . '://' . $request->getHostname() . '/auth/magic-url';
|
||||
}
|
||||
|
||||
$url = Template::parseURL($url);
|
||||
|
|
@ -736,7 +703,7 @@ App::post('/v1/account/sessions/magic-url')
|
|||
$token->setAttribute('secret', ($isPrivilegedUser || $isAppUser) ? $loginSecret : '');
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
|
|
@ -769,16 +736,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
->inject('geodb')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function ($userId, $secret, $request, $response, $dbForProject, $locale, $geodb, $audits, $events) {
|
||||
/** @var string $userId */
|
||||
/** @var string $secret */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $secret, Request $request, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Audit $audits, Event $events) {
|
||||
|
||||
$user = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
|
||||
|
||||
|
|
@ -816,8 +774,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()])
|
||||
);
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
|
@ -838,7 +795,7 @@ App::put('/v1/account/sessions/magic-url')
|
|||
throw new Exception('Failed saving user to DB', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$audits->setResource('user/'.$user->getId());
|
||||
$audits->setResource('user/' . $user->getId());
|
||||
|
||||
$events
|
||||
->setParam('userId', $user->getId())
|
||||
|
|
@ -852,12 +809,12 @@ App::put('/v1/account/sessions/magic-url')
|
|||
$protocol = $request->getProtocol();
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
;
|
||||
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
|
|
@ -892,17 +849,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($request, $response, $locale, $user, $project, $dbForProject, $geodb, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Stats\Stats $events */
|
||||
->action(function (Request $request, Response $response, Locale $locale, Document $user, Document $project, Database $dbForProject, Reader $geodb, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
|
||||
|
|
@ -970,12 +917,11 @@ App::post('/v1/account/sessions/anonymous')
|
|||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()])
|
||||
);
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$audits->setResource('user/'.$user->getId());
|
||||
$audits->setResource('user/' . $user->getId());
|
||||
|
||||
$usage
|
||||
->setParam('users.sessions.create', 1)
|
||||
|
|
@ -997,7 +943,7 @@ App::post('/v1/account/sessions/anonymous')
|
|||
->setStatusCode(Response::STATUS_CODE_CREATED)
|
||||
;
|
||||
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
|
|
@ -1024,10 +970,7 @@ App::post('/v1/account/jwt')
|
|||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('dbForProject')
|
||||
->action(function ($response, $user, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (Response $response, Document $user, Database $dbForProject) {
|
||||
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -1070,10 +1013,7 @@ App::get('/v1/account')
|
|||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('usage')
|
||||
->action(function ($response, $user, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (Response $response, Document $user, Stats $usage) {
|
||||
|
||||
$usage->setParam('users.read', 1);
|
||||
|
||||
|
|
@ -1094,10 +1034,7 @@ App::get('/v1/account/prefs')
|
|||
->inject('response')
|
||||
->inject('user')
|
||||
->inject('usage')
|
||||
->action(function ($response, $user, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (Response $response, Document $user, Stats $usage) {
|
||||
|
||||
$prefs = $user->getAttribute('prefs', new \stdClass());
|
||||
|
||||
|
|
@ -1121,17 +1058,13 @@ App::get('/v1/account/sessions')
|
|||
->inject('user')
|
||||
->inject('locale')
|
||||
->inject('usage')
|
||||
->action(function ($response, $user, $locale, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (Response $response, Document $user, Locale $locale, Stats $usage) {
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$current = Auth::sessionVerify($sessions, Auth::$secret);
|
||||
|
||||
foreach ($sessions as $key => $session) {/** @var Document $session */
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
|
||||
$session->setAttribute('countryName', $countryName);
|
||||
$session->setAttribute('current', ($current == $session->getId()) ? true : false);
|
||||
|
|
@ -1166,16 +1099,9 @@ App::get('/v1/account/logs')
|
|||
->inject('geodb')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($limit, $offset, $response, $user, $locale, $geodb, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (int $limit, int $offset, Response $response, Document $user, Locale $locale, Reader $geodb, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$audit = new EventAudit($dbForProject);
|
||||
|
||||
$logs = $audit->getLogsByUser($user->getId(), $limit, $offset);
|
||||
|
||||
|
|
@ -1197,13 +1123,12 @@ App::get('/v1/account/logs')
|
|||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
$usage->setParam('users.read', 1);
|
||||
|
|
@ -1231,12 +1156,7 @@ App::get('/v1/account/sessions/:sessionId')
|
|||
->inject('locale')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($sessionId, $response, $user, $locale, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (?string $sessionId, Response $response, Document $user, Locale $locale, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$sessionId = ($sessionId === 'current')
|
||||
|
|
@ -1245,7 +1165,7 @@ App::get('/v1/account/sessions/:sessionId')
|
|||
|
||||
foreach ($sessions as $session) {/** @var Document $session */
|
||||
if ($sessionId == $session->getId()) {
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
|
||||
$session
|
||||
->setAttribute('current', ($session->getAttribute('secret') == Auth::hash(Auth::$secret)))
|
||||
|
|
@ -1280,21 +1200,14 @@ App::patch('/v1/account/name')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($name, $response, $user, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Stats\Stats $events */
|
||||
->action(function (string $name, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')]))
|
||||
);
|
||||
->setAttribute('search', implode(' ', [$user->getId(), $name, $user->getAttribute('email')])));
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
|
|
@ -1324,13 +1237,7 @@ App::patch('/v1/account/password')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($password, $oldPassword, $response, $user, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Stats\Stats $events */
|
||||
->action(function (string $password, string $oldPassword, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
// Check old password only if its an existing user.
|
||||
if ($user->getAttribute('passwordUpdate') !== 0 && !Auth::passwordVerify($oldPassword, $user->getAttribute('password'))) { // Double check user password
|
||||
|
|
@ -1346,7 +1253,7 @@ App::patch('/v1/account/password')
|
|||
);
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
|
|
@ -1376,13 +1283,7 @@ App::patch('/v1/account/email')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($email, $password, $response, $user, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Stats\Stats $events */
|
||||
->action(function (string $email, string $password, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$isAnonymousUser = is_null($user->getAttribute('email')) && is_null($user->getAttribute('password')); // Check if request is from an anonymous account for converting
|
||||
|
||||
|
|
@ -1405,14 +1306,13 @@ App::patch('/v1/account/email')
|
|||
->setAttribute('password', $isAnonymousUser ? Auth::passwordHash($password) : $user->getAttribute('password', ''))
|
||||
->setAttribute('email', $email)
|
||||
->setAttribute('emailVerification', false) // After this user needs to confirm mail again
|
||||
->setAttribute('search', implode(' ', [$user->getId(), $user->getAttribute('name'), $user->getAttribute('email')]))
|
||||
);
|
||||
} catch(Duplicate $th) {
|
||||
->setAttribute('search', implode(' ', [$user->getId(), $user->getAttribute('name'), $user->getAttribute('email')])));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
->setUser($user)
|
||||
;
|
||||
|
||||
|
|
@ -1441,17 +1341,11 @@ App::patch('/v1/account/prefs')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($prefs, $response, $user, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (array $prefs, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('prefs', $prefs));
|
||||
|
||||
$audits->setResource('user/'.$user->getId());
|
||||
$audits->setResource('user/' . $user->getId());
|
||||
$usage->setParam('users.update', 1);
|
||||
$events->setParam('userId', $user->getId());
|
||||
|
||||
|
|
@ -1477,14 +1371,7 @@ App::patch('/v1/account/status')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($request, $response, $user, $dbForProject, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Audit $audits, Event $events, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user->setAttribute('status', false));
|
||||
|
||||
|
|
@ -1526,15 +1413,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($sessionId, $request, $response, $user, $dbForProject, $locale, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Stats $usage) {
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$sessionId = ($sessionId === 'current')
|
||||
|
|
@ -1556,7 +1435,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) { // If current session delete the cookies too
|
||||
$session
|
||||
->setAttribute('current', true)
|
||||
->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
||||
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
||||
;
|
||||
|
||||
if (!Config::getParam('domainVerification')) {
|
||||
|
|
@ -1570,7 +1449,7 @@ App::delete('/v1/account/sessions/:sessionId')
|
|||
->addCookie(Auth::$cookieName, '', \time() - 3600, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
;
|
||||
}
|
||||
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
$events
|
||||
|
|
@ -1613,17 +1492,7 @@ App::patch('/v1/account/sessions/:sessionId')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($sessionId, $request, $response, $user, $dbForProject, $project, $locale, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var boolean $force */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (?string $sessionId, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Audit $audits, Event $events, Stats $usage) {
|
||||
|
||||
$sessionId = ($sessionId === 'current')
|
||||
? Auth::sessionVerify($user->getAttribute('sessions'), Auth::$secret)
|
||||
|
|
@ -1633,7 +1502,6 @@ App::patch('/v1/account/sessions/:sessionId')
|
|||
|
||||
foreach ($sessions as $key => $session) {/** @var Document $session */
|
||||
if ($sessionId == $session->getId()) {
|
||||
|
||||
// Comment below would skip re-generation if token is still valid
|
||||
// We decided to not include this because developer can get expiration date from the session
|
||||
// I kept code in comment because it might become relevant in the future
|
||||
|
|
@ -1646,10 +1514,10 @@ App::patch('/v1/account/sessions/:sessionId')
|
|||
$provider = $session->getAttribute('provider');
|
||||
$refreshToken = $session->getAttribute('providerRefreshToken');
|
||||
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider.'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider.'Secret'] ?? '{}';
|
||||
$appId = $project->getAttribute('authProviders', [])[$provider . 'Appid'] ?? '';
|
||||
$appSecret = $project->getAttribute('authProviders', [])[$provider . 'Secret'] ?? '{}';
|
||||
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\'.\ucfirst($provider);
|
||||
$className = 'Appwrite\\Auth\\OAuth2\\' . \ucfirst($provider);
|
||||
|
||||
if (!\class_exists($className)) {
|
||||
throw new Exception('Provider is not supported', 501, Exception::PROJECT_PROVIDER_UNSUPPORTED);
|
||||
|
|
@ -1709,15 +1577,7 @@ App::delete('/v1/account/sessions')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($request, $response, $user, $dbForProject, $locale, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (Request $request, Response $response, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Stats $usage) {
|
||||
|
||||
$protocol = $request->getProtocol();
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
|
@ -1733,7 +1593,7 @@ App::delete('/v1/account/sessions')
|
|||
|
||||
$session
|
||||
->setAttribute('current', false)
|
||||
->setAttribute('countryName', $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
||||
->setAttribute('countryName', $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown')))
|
||||
;
|
||||
|
||||
if ($session->getAttribute('secret') == Auth::hash(Auth::$secret)) {
|
||||
|
|
@ -1780,7 +1640,7 @@ App::post('/v1/account/recovery')
|
|||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', ['url:{url},email:{param-email}', 'ip:{ip}'])
|
||||
->param('email', '', new Email(), 'User email.')
|
||||
->param('url', '', function ($clients) {return new Host($clients);}, 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
|
||||
->param('url', '', fn ($clients) => new Host($clients), 'URL to redirect the user back to your app from the recovery email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -1790,18 +1650,9 @@ App::post('/v1/account/recovery')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($email, $url, $request, $response, $dbForProject, $project, $locale, $mails, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Mail $mails */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $email, string $url, Request $request, Response $response, Database $dbForProject, Document $project, Locale $locale, Mail $mails, Audit $audits, Event $events, Stats $usage) {
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
|
|
@ -1839,9 +1690,8 @@ App::post('/v1/account/recovery')
|
|||
Authorization::setRole('user:' . $profile->getId());
|
||||
|
||||
$recovery = $dbForProject->createDocument('tokens', $recovery
|
||||
->setAttribute('$read', ['user:'.$profile->getId()])
|
||||
->setAttribute('$write', ['user:'.$profile->getId()])
|
||||
);
|
||||
->setAttribute('$read', ['user:' . $profile->getId()])
|
||||
->setAttribute('$write', ['user:' . $profile->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $profile->getId());
|
||||
|
||||
|
|
@ -1901,12 +1751,7 @@ App::put('/v1/account/recovery')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $secret, $password, $passwordAgain, $response, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $secret, string $password, string $passwordAgain, Response $response, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
if ($password !== $passwordAgain) {
|
||||
throw new Exception('Passwords must match', 400, Exception::USER_PASSWORD_MISMATCH);
|
||||
|
|
@ -1930,8 +1775,7 @@ App::put('/v1/account/recovery')
|
|||
$profile = $dbForProject->updateDocument('users', $profile->getId(), $profile
|
||||
->setAttribute('password', Auth::passwordHash($password))
|
||||
->setAttribute('passwordUpdate', \time())
|
||||
->setAttribute('emailVerification', true)
|
||||
);
|
||||
->setAttribute('emailVerification', true));
|
||||
|
||||
$recoveryDocument = $dbForProject->getDocument('tokens', $recovery);
|
||||
|
||||
|
|
@ -1968,7 +1812,7 @@ App::post('/v1/account/verification')
|
|||
->label('sdk.response.model', Response::MODEL_TOKEN)
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'url:{url},userId:{userId}')
|
||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page
|
||||
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the verification email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add built-in confirm page
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
@ -1979,19 +1823,9 @@ App::post('/v1/account/verification')
|
|||
->inject('events')
|
||||
->inject('mails')
|
||||
->inject('usage')
|
||||
->action(function ($url, $request, $response, $project, $user, $dbForProject, $locale, $audits, $events, $mails, $usage) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Mail $mails */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $url, Request $request, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, Audit $audits, Event $events, Mail $mails, Stats $usage) {
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
if (empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
|
|
@ -2016,9 +1850,8 @@ App::post('/v1/account/verification')
|
|||
Authorization::setRole('user:' . $user->getId());
|
||||
|
||||
$verification = $dbForProject->createDocument('tokens', $verification
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
|
|
@ -2076,13 +1909,7 @@ App::put('/v1/account/verification')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $secret, $response, $user, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $secret, Response $response, Document $user, Database $dbForProject, Audit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$profile = Authorization::skip(fn() => $dbForProject->getDocument('users', $userId));
|
||||
|
||||
|
|
|
|||
|
|
@ -193,7 +193,7 @@ App::get('/v1/avatars/image')
|
|||
->setContentType('image/png')
|
||||
->addHeader('Expires', $date)
|
||||
->addHeader('X-Appwrite-Cache', 'miss')
|
||||
->send($data);;
|
||||
->send($data);
|
||||
|
||||
unset($image);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Utopia\App;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Validator\Boolean;
|
||||
|
|
@ -25,6 +26,7 @@ use Utopia\Database\Exception\Authorization as AuthorizationException;
|
|||
use Utopia\Database\Exception\Duplicate as DuplicateException;
|
||||
use Utopia\Database\Exception\Limit as LimitException;
|
||||
use Utopia\Database\Exception\Structure as StructureException;
|
||||
use Utopia\Locale\Locale;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Network\Validator\IP;
|
||||
|
|
@ -38,17 +40,11 @@ use Appwrite\Event\Audit as EventAudit;
|
|||
use Appwrite\Event\Database as EventDatabase;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Stats\Stats;
|
||||
use MaxMind\Db\Reader;
|
||||
|
||||
/**
|
||||
* Create attribute of varying type
|
||||
*
|
||||
* @param string $collectionId
|
||||
* @param Utopia\Database\Document $attribute
|
||||
* @param Appwrite\Utopia\Response $response
|
||||
* @param Utopia\Database\Database $dbForProject
|
||||
* @param Appwrite\Event\Database $database
|
||||
* @param Appwrite\Event\Audit $audits
|
||||
* @param Appwrite\Stats\Stats $usage
|
||||
*
|
||||
* @return Document Newly created attribute document
|
||||
*/
|
||||
|
|
@ -58,11 +54,11 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
$type = $attribute->getAttribute('type', '');
|
||||
$size = $attribute->getAttribute('size', 0);
|
||||
$required = $attribute->getAttribute('required', true);
|
||||
$signed = $attribute->getAttribute('signed', true); // integers are signed by default
|
||||
$signed = $attribute->getAttribute('signed', true); // integers are signed by default
|
||||
$array = $attribute->getAttribute('array', false);
|
||||
$format = $attribute->getAttribute('format', '');
|
||||
$formatOptions = $attribute->getAttribute('formatOptions', []);
|
||||
$filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint
|
||||
$filters = $attribute->getAttribute('filters', []); // filters are hidden from the endpoint
|
||||
$default = $attribute->getAttribute('default');
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
|
@ -88,7 +84,7 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
|
||||
try {
|
||||
$attribute = new Document([
|
||||
'$id' => $collectionId.'_'.$key,
|
||||
'$id' => $collectionId . '_' . $key,
|
||||
'key' => $key,
|
||||
'collectionId' => $collectionId,
|
||||
'type' => $type,
|
||||
|
|
@ -105,11 +101,9 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
|
||||
$dbForProject->checkAttribute($collection, $attribute);
|
||||
$attribute = $dbForProject->createDocument('attributes', $attribute);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException $exception) {
|
||||
throw new Exception('Attribute already exists', 409, Exception::ATTRIBUTE_ALREADY_EXISTS);
|
||||
}
|
||||
catch (LimitException $exception) {
|
||||
} catch (LimitException $exception) {
|
||||
throw new Exception('Attribute limit exceeded', 400, Exception::ATTRIBUTE_LIMIT_EXCEEDED);
|
||||
}
|
||||
|
||||
|
|
@ -131,14 +125,14 @@ function createAttribute(string $collectionId, Document $attribute, Response $re
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collectionId)
|
||||
->setResource('collection/' . $collectionId)
|
||||
->setPayload($attribute->getArrayCopy())
|
||||
;
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
|
||||
return $attribute;
|
||||
};
|
||||
}
|
||||
|
||||
App::post('/v1/database/collections')
|
||||
->desc('Create Collection')
|
||||
|
|
@ -162,12 +156,7 @@ App::post('/v1/database/collections')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $name, $permission, $read, $write, $response, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $name, ?string $permission, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$collectionId = $collectionId == 'unique()' ? $dbForProject->getId() : $collectionId;
|
||||
|
||||
|
|
@ -193,7 +182,7 @@ App::post('/v1/database/collections')
|
|||
}
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collectionId)
|
||||
->setResource('collection/' . $collectionId)
|
||||
->setPayload($collection->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -224,9 +213,7 @@ App::get('/v1/database/collections')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorCollection = $dbForProject->getDocument('collections', $cursor);
|
||||
|
|
@ -265,9 +252,7 @@ App::get('/v1/database/collections/:collectionId')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $collectionId, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -293,11 +278,7 @@ App::get('/v1/database/usage')
|
|||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($range, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
->action(function (string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
|
|
@ -335,7 +316,7 @@ App::get('/v1/database/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
|
@ -357,7 +338,7 @@ App::get('/v1/database/usage')
|
|||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
@ -404,10 +385,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
->param('collectionId', '', new UID(), 'Collection ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($range, $collectionId, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
->action(function (string $range, string $collectionId, Response $response, Database $dbForProject) {
|
||||
|
||||
$collectionDocument = $dbForProject->getDocument('collections', $collectionId);
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
|
@ -417,7 +395,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
}
|
||||
|
||||
$usage = [];
|
||||
if(App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
$periods = [
|
||||
'24h' => [
|
||||
'period' => '30m',
|
||||
|
|
@ -447,7 +425,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
|
@ -469,7 +447,7 @@ App::get('/v1/database/:collectionId/usage')
|
|||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
@ -514,12 +492,7 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($collectionId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
->action(function (string $collectionId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
|
||||
$collectionDocument = $dbForProject->getDocument('collections', $collectionId);
|
||||
$collection = $dbForProject->getCollection('collection_' . $collectionDocument->getInternalId());
|
||||
|
|
@ -529,7 +502,7 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$resource = 'collection/'.$collectionId;
|
||||
$resource = 'collection/' . $collectionId;
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
|
@ -569,8 +542,8 @@ App::get('/v1/database/collections/:collectionId/logs')
|
|||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
|
@ -606,12 +579,7 @@ App::put('/v1/database/collections/:collectionId')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $name, $permission, $read, $write, $enabled, $response, $dbForProject, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $name, string $permission, ?array $read, ?array $write, bool $enabled, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -631,18 +599,15 @@ App::put('/v1/database/collections/:collectionId')
|
|||
->setAttribute('permission', $permission)
|
||||
->setAttribute('dateUpdated', time())
|
||||
->setAttribute('enabled', $enabled)
|
||||
->setAttribute('search', implode(' ', [$collectionId, $name]))
|
||||
);
|
||||
}
|
||||
catch (AuthorizationException $exception) {
|
||||
->setAttribute('search', implode(' ', [$collectionId, $name])));
|
||||
} catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
throw new Exception('Bad structure. '.$exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception('Bad structure. ' . $exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collectionId)
|
||||
->setResource('collection/' . $collectionId)
|
||||
->setPayload($collection->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -670,13 +635,7 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
->inject('audits')
|
||||
->inject('deletes')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $response, $dbForProject, $events, $audits, $deletes, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $collectionId, Response $response, Database $dbForProject, Event $events, EventAudit $audits, Delete $deletes, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -701,7 +660,7 @@ App::delete('/v1/database/collections/:collectionId')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collectionId)
|
||||
->setResource('collection/' . $collectionId)
|
||||
->setPayload($collection->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -734,13 +693,7 @@ App::post('/v1/database/collections/:collectionId/attributes/string')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $size, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, ?int $size, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
// Ensure attribute default is within required size
|
||||
$validator = new Text($size);
|
||||
|
|
@ -783,12 +736,7 @@ App::post('/v1/database/collections/:collectionId/attributes/email')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, Event $audits, Stats $usage, Event $events) {
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
'key' => $key,
|
||||
|
|
@ -827,13 +775,7 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $elements, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, array $elements, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
// use length of longest string as attribute size
|
||||
$size = 0;
|
||||
|
|
@ -841,7 +783,6 @@ App::post('/v1/database/collections/:collectionId/attributes/enum')
|
|||
$length = \strlen($element);
|
||||
if ($length === 0) {
|
||||
throw new Exception('Each enum element must not be empty', 400, Exception::ATTRIBUTE_VALUE_INVALID);
|
||||
|
||||
}
|
||||
$size = ($length > $size) ? $length : $size;
|
||||
}
|
||||
|
|
@ -887,12 +828,7 @@ App::post('/v1/database/collections/:collectionId/attributes/ip')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
'key' => $key,
|
||||
|
|
@ -930,12 +866,7 @@ App::post('/v1/database/collections/:collectionId/attributes/url')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?string $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
'key' => $key,
|
||||
|
|
@ -975,13 +906,7 @@ App::post('/v1/database/collections/:collectionId/attributes/integer')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?int $min, ?int $max, ?int $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
// Ensure attribute default is within range
|
||||
$min = (is_null($min)) ? PHP_INT_MIN : \intval($min);
|
||||
|
|
@ -1048,13 +973,7 @@ App::post('/v1/database/collections/:collectionId/attributes/float')
|
|||
->inject('audits')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $key, $required, $min, $max, $default, $array, $response, $dbForProject, $database, $audits, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?float $min, ?float $max, ?float $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Event $events, Stats $usage) {
|
||||
|
||||
// Ensure attribute default is within range
|
||||
$min = (is_null($min)) ? -PHP_FLOAT_MAX : \floatval($min);
|
||||
|
|
@ -1122,13 +1041,7 @@ App::post('/v1/database/collections/:collectionId/attributes/boolean')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $required, $default, $array, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject*/
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, ?bool $required, ?bool $default, bool $array, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$attribute = createAttribute($collectionId, new Document([
|
||||
'key' => $key,
|
||||
|
|
@ -1157,9 +1070,7 @@ App::get('/v1/database/collections/:collectionId/attributes')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $collectionId, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1201,9 +1112,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:key')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $key, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $collectionId, string $key, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1211,7 +1120,7 @@ App::get('/v1/database/collections/:collectionId/attributes/:key')
|
|||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId.'_'.$key);
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId . '_' . $key);
|
||||
|
||||
if ($attribute->isEmpty()) {
|
||||
throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND);
|
||||
|
|
@ -1221,11 +1130,11 @@ App::get('/v1/database/collections/:collectionId/attributes/:key')
|
|||
$type = $attribute->getAttribute('type');
|
||||
$format = $attribute->getAttribute('format');
|
||||
|
||||
$model = match($type) {
|
||||
$model = match ($type) {
|
||||
Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN,
|
||||
Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER,
|
||||
Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT,
|
||||
Database::VAR_STRING => match($format) {
|
||||
Database::VAR_STRING => match ($format) {
|
||||
APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL,
|
||||
APP_DATABASE_ATTRIBUTE_ENUM => Response::MODEL_ATTRIBUTE_ENUM,
|
||||
APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP,
|
||||
|
|
@ -1259,13 +1168,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $key, $response, $dbForProject, $database, $events, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, EventAudit $audits, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1273,7 +1176,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId.'_'.$key);
|
||||
$attribute = $dbForProject->getDocument('attributes', $collectionId . '_' . $key);
|
||||
|
||||
if ($attribute->isEmpty()) {
|
||||
throw new Exception('Attribute not found', 404, Exception::ATTRIBUTE_NOT_FOUND);
|
||||
|
|
@ -1299,11 +1202,11 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
$type = $attribute->getAttribute('type');
|
||||
$format = $attribute->getAttribute('format');
|
||||
|
||||
$model = match($type) {
|
||||
$model = match ($type) {
|
||||
Database::VAR_BOOLEAN => Response::MODEL_ATTRIBUTE_BOOLEAN,
|
||||
Database::VAR_INTEGER => Response::MODEL_ATTRIBUTE_INTEGER,
|
||||
Database::VAR_FLOAT => Response::MODEL_ATTRIBUTE_FLOAT,
|
||||
Database::VAR_STRING => match($format) {
|
||||
Database::VAR_STRING => match ($format) {
|
||||
APP_DATABASE_ATTRIBUTE_EMAIL => Response::MODEL_ATTRIBUTE_EMAIL,
|
||||
APP_DATABASE_ATTRIBUTE_ENUM => Response::MODEL_ATTRIBUTE_ENUM,
|
||||
APP_DATABASE_ATTRIBUTE_IP => Response::MODEL_ATTRIBUTE_IP,
|
||||
|
|
@ -1321,7 +1224,7 @@ App::delete('/v1/database/collections/:collectionId/attributes/:key')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collectionId)
|
||||
->setResource('collection/' . $collectionId)
|
||||
->setPayload($attribute->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -1351,13 +1254,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
->inject('audits')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($collectionId, $key, $type, $attributes, $orders, $response, $dbForProject, $database, $audits, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $collectionId, string $key, string $type, array $attributes, array $orders, Response $response, Database $dbForProject, EventDatabase $database, EventAudit $audits, Stats $usage, Event $events) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1395,7 +1292,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
|
||||
// ensure attribute is available
|
||||
if ($attributeStatus !== 'available') {
|
||||
throw new Exception ('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400, Exception::ATTRIBUTE_NOT_AVAILABLE);
|
||||
throw new Exception('Attribute not available: ' . $oldAttributes[$attributeIndex]['key'], 400, Exception::ATTRIBUTE_NOT_AVAILABLE);
|
||||
}
|
||||
|
||||
// set attribute size as index length only for strings
|
||||
|
|
@ -1404,7 +1301,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
|
||||
try {
|
||||
$index = $dbForProject->createDocument('indexes', new Document([
|
||||
'$id' => $collectionId.'_'.$key,
|
||||
'$id' => $collectionId . '_' . $key,
|
||||
'key' => $key,
|
||||
'status' => 'processing', // processing, available, failed, deleting, stuck
|
||||
'collectionId' => $collectionId,
|
||||
|
|
@ -1434,7 +1331,7 @@ App::post('/v1/database/collections/:collectionId/indexes')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collection->getId())
|
||||
->setResource('collection/' . $collection->getId())
|
||||
->setPayload($index->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -1457,9 +1354,7 @@ App::get('/v1/database/collections/:collectionId/indexes')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $collectionId, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1493,9 +1388,7 @@ App::get('/v1/database/collections/:collectionId/indexes/:key')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $key, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $collectionId, string $key, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1540,13 +1433,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
|
|||
->inject('events')
|
||||
->inject('audits')
|
||||
->inject('usage')
|
||||
->action(function ($collectionId, $key, $response, $dbForProject, $database, $events, $audits, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $collectionId, string $key, Response $response, Database $dbForProject, EventDatabase $database, Event $events, EventAudit $audits, Stats $usage) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1554,7 +1441,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
|
|||
throw new Exception('Collection not found', 404, Exception::COLLECTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$index = $dbForProject->getDocument('indexes', $collectionId.'_'.$key);
|
||||
$index = $dbForProject->getDocument('indexes', $collectionId . '_' . $key);
|
||||
|
||||
if (empty($index->getId())) {
|
||||
throw new Exception('Index not found', 404, Exception::INDEX_NOT_FOUND);
|
||||
|
|
@ -1583,7 +1470,7 @@ App::delete('/v1/database/collections/:collectionId/indexes/:key')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('collection/'.$collection->getId())
|
||||
->setResource('collection/' . $collection->getId())
|
||||
->setPayload($index->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -1614,14 +1501,7 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->inject('mode')
|
||||
->action(function ($documentId, $collectionId, $data, $read, $write, $response, $dbForProject, $user, $audits, $usage, $events, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var string $mode */
|
||||
->action(function (string $documentId, string $collectionId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, Document $user, EventAudit $audits, Stats $usage, Event $events, string $mode) {
|
||||
|
||||
$data = (\is_string($data)) ? \json_decode($data, true) : $data; // Cast to JSON array
|
||||
|
||||
|
|
@ -1635,7 +1515,7 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
*
|
||||
*
|
||||
* @var Document $collection
|
||||
*/
|
||||
$collection = Authorization::skip(fn() => $dbForProject->getDocument('collections', $collectionId));
|
||||
|
|
@ -1656,8 +1536,8 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
|
||||
$data['$collection'] = $collection->getId(); // Adding this param to make API easier for developers
|
||||
$data['$id'] = $documentId == 'unique()' ? $dbForProject->getId() : $documentId;
|
||||
$data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:'.$user->getId()] : $write ?? []; // By default set write permissions for user
|
||||
$data['$read'] = (is_null($read) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $read ?? []; // By default set read permissions for user
|
||||
$data['$write'] = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? []; // By default set write permissions for user
|
||||
|
||||
// Users can only add their roles to documents, API keys and Admin users can add any
|
||||
$roles = Authorization::getRoles();
|
||||
|
|
@ -1665,13 +1545,13 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
// TODO: Isn't this a 401: Unauthorized Error ?
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
// TODO: Isn't this a 401: Unauthorized Error ?
|
||||
throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1684,11 +1564,9 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
$document = $dbForProject->createDocument('collection_' . $collection->getInternalId(), new Document($data));
|
||||
}
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException $exception) {
|
||||
throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
|
@ -1704,7 +1582,7 @@ App::post('/v1/database/collections/:collectionId/documents')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('document/'.$document->getId())
|
||||
->setResource('document/' . $document->getId())
|
||||
->setPayload($document->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -1724,7 +1602,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_DOCUMENT_LIST)
|
||||
->param('collectionId', '', new UID(), 'Collection ID. You can create a new collection using the Database service [server integration](https://appwrite.io/docs/server/database#createCollection).')
|
||||
->param('queries', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/database#querying-documents). Maximum of 100 queries are allowed, each 128 characters long.', true)
|
||||
->param('queries', [], new ArrayList(new Text(128), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/database#querying-documents). Maximum of 100 queries are allowed, each 128 characters long.', true)
|
||||
->param('limit', 25, new Range(0, 100), 'Maximum number of documents to return in response. By default will return maximum 25 results. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' results allowed per request.', true)
|
||||
->param('offset', 0, new Range(0, APP_LIMIT_COUNT), 'Offset value. The default value is 0. Use this value to manage pagination. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
->param('cursor', '', new UID(), 'ID of the document used as the starting point for the query, excluding the document itself. Should be used for efficient pagination when working with large sets of data. [learn more about pagination](https://appwrite.io/docs/pagination)', true)
|
||||
|
|
@ -1735,11 +1613,7 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->action(function ($collectionId, $queries, $limit, $offset, $cursor, $cursorDirection, $orderAttributes, $orderTypes, $response, $dbForProject, $usage, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var string $mode */
|
||||
->action(function (string $collectionId, array $queries, int $limit, int $offset, string $cursor, string $cursorDirection, array $orderAttributes, array $orderTypes, Response $response, Database $dbForProject, Stats $usage, string $mode) {
|
||||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
|
|
@ -1772,13 +1646,13 @@ App::get('/v1/database/collections/:collectionId/documents')
|
|||
return $query;
|
||||
}, $queries);
|
||||
|
||||
if(!empty($orderAttributes)) {
|
||||
if (!empty($orderAttributes)) {
|
||||
$validator = new OrderAttributes($collection->getAttribute('attributes', []), $collection->getAttribute('indexes', []), true);
|
||||
if (!$validator->isValid($orderAttributes)) {
|
||||
throw new Exception($validator->getDescription(), 400, Exception::GENERAL_QUERY_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (!empty($queries)) {
|
||||
$validator = new QueriesValidator(new QueryValidator($collection->getAttribute('attributes', [])), $collection->getAttribute('indexes', []), true);
|
||||
if (!$validator->isValid($queries)) {
|
||||
|
|
@ -1839,10 +1713,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->action(function ($collectionId, $documentId, $response, $dbForProject, $usage, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var string $mode */
|
||||
->action(function (string $collectionId, string $documentId, Response $response, Database $dbForProject, Stats $usage, string $mode) {
|
||||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
|
|
@ -1906,12 +1777,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
|||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->action(function ($collectionId, $documentId, $limit, $offset, $response, $dbForProject, $locale, $geodb) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
->action(function (string $collectionId, string $documentId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb) {
|
||||
|
||||
$collection = $dbForProject->getDocument('collections', $collectionId);
|
||||
|
||||
|
|
@ -1926,7 +1792,7 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
|||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$resource = 'document/'.$document->getId();
|
||||
$resource = 'document/' . $document->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
|
@ -1966,8 +1832,8 @@ App::get('/v1/database/collections/:collectionId/documents/:documentId/logs')
|
|||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
|
@ -2002,13 +1868,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
->inject('usage')
|
||||
->inject('events')
|
||||
->inject('mode')
|
||||
->action(function ($collectionId, $documentId, $data, $read, $write, $response, $dbForProject, $audits, $usage, $events, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var string $mode */
|
||||
->action(function (string $collectionId, string $documentId, string|array $data, ?array $read, ?array $write, Response $response, Database $dbForProject, EventAudit $audits, Stats $usage, Event $events, string $mode) {
|
||||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
|
|
@ -2059,14 +1919,14 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
$roles = Authorization::getRoles();
|
||||
|
||||
if (!Auth::isAppUser($roles) && !Auth::isPrivilegedUser($roles)) {
|
||||
if(!is_null($read)) {
|
||||
if (!is_null($read)) {
|
||||
foreach ($data['$read'] as $read) {
|
||||
if (!Authorization::isRole($read)) {
|
||||
throw new Exception('Read permissions must be one of: ('.\implode(', ', $roles).')', 400, Exception::USER_UNAUTHORIZED);
|
||||
throw new Exception('Read permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!is_null($write)) {
|
||||
if (!is_null($write)) {
|
||||
foreach ($data['$write'] as $write) {
|
||||
if (!Authorization::isRole($write)) {
|
||||
throw new Exception('Write permissions must be one of: (' . \implode(', ', $roles) . ')', 400, Exception::USER_UNAUTHORIZED);
|
||||
|
|
@ -2086,14 +1946,11 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
* Reset $collection attribute to remove prefix.
|
||||
*/
|
||||
$document->setAttribute('$collection', $collectionId);
|
||||
}
|
||||
catch (AuthorizationException $exception) {
|
||||
} catch (AuthorizationException $exception) {
|
||||
throw new Exception('Unauthorized permissions', 401, Exception::USER_UNAUTHORIZED);
|
||||
}
|
||||
catch (DuplicateException $exception) {
|
||||
} catch (DuplicateException $exception) {
|
||||
throw new Exception('Document already exists', 409, Exception::DOCUMENT_ALREADY_EXISTS);
|
||||
}
|
||||
catch (StructureException $exception) {
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
}
|
||||
|
||||
|
|
@ -2109,7 +1966,7 @@ App::patch('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('document/'.$document->getId())
|
||||
->setResource('document/' . $document->getId())
|
||||
->setPayload($document->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -2136,14 +1993,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
->inject('deletes')
|
||||
->inject('usage')
|
||||
->inject('mode')
|
||||
->action(function ($collectionId, $documentId, $response, $dbForProject, $events, $audits, $deletes, $usage, $mode) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var string $mode */
|
||||
->action(function (string $collectionId, string $documentId, Response $response, Database $dbForProject, Event $events, EventAudit $audits, Delete $deletes, Stats $usage, string $mode) {
|
||||
|
||||
/**
|
||||
* Skip Authorization to get the collection. Needed in case of empty permissions for document level permissions.
|
||||
|
|
@ -2206,7 +2056,7 @@ App::delete('/v1/database/collections/:collectionId/documents/:documentId')
|
|||
;
|
||||
|
||||
$audits
|
||||
->setResource('document/'.$document->getId())
|
||||
->setResource('document/' . $document->getId())
|
||||
->setPayload($document->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -3,16 +3,21 @@
|
|||
use Ahc\Jwt\JWT;
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Func;
|
||||
use Appwrite\Event\Validator\Event as ValidatorEvent;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Validator\File;
|
||||
use Utopia\Storage\Validator\FileExt;
|
||||
use Utopia\Storage\Validator\FileSize;
|
||||
use Utopia\Storage\Validator\Upload;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Swoole\Request;
|
||||
use Appwrite\Task\Validator\Cron;
|
||||
use Utopia\App;
|
||||
use Utopia\Database\Database;
|
||||
|
|
@ -55,10 +60,7 @@ App::post('/v1/functions')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $name, $execute, $runtime, $vars, $events, $schedule, $timeout, $response, $dbForProject, $eventsInstance) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $eventsInstance */
|
||||
->action(function (string $functionId, string $name, array $execute, string $runtime, array $vars, array $events, string $schedule, int $timeout, Response $response, Database $dbForProject, Event $eventsInstance) {
|
||||
|
||||
$functionId = ($functionId == 'unique()') ? $dbForProject->getId() : $functionId;
|
||||
$function = $dbForProject->createDocument('functions', new Document([
|
||||
|
|
@ -104,9 +106,7 @@ App::get('/v1/functions')
|
|||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorFunction = $dbForProject->getDocument('functions', $cursor);
|
||||
|
|
@ -140,8 +140,7 @@ App::get('/v1/functions/runtimes')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_RUNTIME_LIST)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$runtimes = Config::getParam('runtimes');
|
||||
|
||||
|
|
@ -170,10 +169,7 @@ App::get('/v1/functions/:functionId')
|
|||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
|
||||
->action(function (string $functionId, Response $response, Database $dbForProject) {
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
|
|
@ -197,11 +193,7 @@ App::get('/v1/functions/:functionId/usage')
|
|||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d']), 'Date range.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $range, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
->action(function (string $functionId, string $range, Response $response, Database $dbForProject) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -309,12 +301,7 @@ App::put('/v1/functions/:functionId')
|
|||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $name, $execute, $vars, $events, $schedule, $timeout, $response, $dbForProject, $project, $user, $eventsInstance) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Event\Event $eventsInstance */
|
||||
->action(function (string $functionId, string $name, array $execute, array $vars, array $events, string $schedule, int $timeout, Response $response, Database $dbForProject, Document $project, Document $user, Event $eventsInstance) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -373,11 +360,7 @@ App::patch('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $deploymentId, $response, $dbForProject, $project, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $events) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
|
@ -440,11 +423,7 @@ App::delete('/v1/functions/:functionId')
|
|||
->inject('dbForProject')
|
||||
->inject('deletes')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $response, $dbForProject, $deletes, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $functionId, Response $response, Database $dbForProject, Delete $deletes, Event $events) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -491,15 +470,7 @@ App::post('/v1/functions/:functionId/deployments')
|
|||
->inject('project')
|
||||
->inject('deviceFunctions')
|
||||
->inject('deviceLocal')
|
||||
->action(function ($functionId, $entrypoint, $file, $activate, $request, $response, $dbForProject, $usage, $events, $project, $deviceFunctions, $deviceLocal) {
|
||||
/** @var Utopia\Swoole\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
/** @var Utopia\Storage\Device $deviceLocal */
|
||||
->action(function (string $functionId, string $entrypoint, array $file, bool $activate, Request $request, Response $response, Database $dbForProject, Stats $usage, Event $events, Document $project, Device $deviceFunctions, Device $deviceLocal) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -679,9 +650,7 @@ App::get('/v1/functions/:functionId/deployments')
|
|||
->param('orderType', 'ASC', new WhiteList(['ASC', 'DESC'], true), 'Order result by ASC or DESC order.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $functionId, string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -737,9 +706,7 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
->param('deploymentId', '', new UID(), 'Deployment ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $deploymentId, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -779,13 +746,7 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
|
|||
->inject('deletes')
|
||||
->inject('events')
|
||||
->inject('deviceFunctions')
|
||||
->action(function ($functionId, $deploymentId, $response, $dbForProject, $usage, $deletes, $events, $deviceFunctions) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $usage */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Utopia\Storage\Device $deviceFunctions */
|
||||
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Stats $usage, Delete $deletes, Event $events, Device $deviceFunctions) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
if ($function->isEmpty()) {
|
||||
|
|
@ -849,12 +810,7 @@ App::post('/v1/functions/:functionId/executions')
|
|||
->inject('dbForProject')
|
||||
->inject('user')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $data, $async, $response, $project, $dbForProject, $user, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $functionId, string $data, bool $async, Response $response, Document $project, Database $dbForProject, Document $user, Event $events) {
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
|
|
@ -916,7 +872,6 @@ App::post('/v1/functions/:functionId/executions')
|
|||
|
||||
$jwt = ''; // initialize
|
||||
if (!$user->isEmpty()) { // If userId exists, generate a JWT for function
|
||||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
$current = new Document();
|
||||
|
||||
|
|
@ -1031,9 +986,7 @@ App::get('/v1/functions/:functionId/executions')
|
|||
->param('cursorDirection', Database::CURSOR_AFTER, new WhiteList([Database::CURSOR_AFTER, Database::CURSOR_BEFORE]), 'Direction of the cursor.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $limit, $offset, $search, $cursor, $cursorDirection, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $functionId, int $limit, int $offset, string $search, string $cursor, string $cursorDirection, Response $response, Database $dbForProject) {
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
|
|
@ -1081,9 +1034,7 @@ App::get('/v1/functions/:functionId/executions/:executionId')
|
|||
->param('executionId', '', new UID(), 'Execution ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($functionId, $executionId, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject) {
|
||||
|
||||
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
|
||||
|
||||
|
|
@ -1122,11 +1073,7 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
|
|||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('events')
|
||||
->action(function ($functionId, $deploymentId, $buildId, $response, $dbForProject, $project, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $functionId, string $deploymentId, string $buildId, Response $response, Database $dbForProject, Document $project, Event $events) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
|
||||
|
|
|
|||
|
|
@ -1,8 +1,5 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* 1. Map all objects, object-params, object-fields
|
||||
|
|
@ -13,6 +10,9 @@ use Utopia\App;
|
|||
* 6. Write tests!
|
||||
*/
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\App;
|
||||
|
||||
App::post('/v1/graphql')
|
||||
->desc('GraphQL Endpoint')
|
||||
->groups(['api', 'graphql'])
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ use Utopia\Database\Document;
|
|||
use Utopia\Registry\Registry;
|
||||
use Utopia\Storage\Device;
|
||||
use Utopia\Storage\Device\Local;
|
||||
use Utopia\Storage\Storage;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::get('/v1/health')
|
||||
->desc('Get HTTP')
|
||||
|
|
@ -68,9 +68,9 @@ App::get('/v1/health/db')
|
|||
|
||||
// Run a small test to check the connection
|
||||
$statement = $db->prepare("SELECT 1;");
|
||||
|
||||
|
||||
$statement->closeCursor();
|
||||
|
||||
|
||||
$statement->execute();
|
||||
} catch (Exception $_e) {
|
||||
throw new Exception('Database is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
|
|
@ -141,7 +141,7 @@ App::get('/v1/health/time')
|
|||
\socket_connect($sock, $host, 123);
|
||||
|
||||
/* Send request */
|
||||
$msg = "\010".\str_repeat("\0", 47);
|
||||
$msg = "\010" . \str_repeat("\0", 47);
|
||||
|
||||
\socket_send($sock, $msg, \strlen($msg), 0);
|
||||
|
||||
|
|
@ -256,20 +256,22 @@ App::get('/v1/health/storage/local')
|
|||
|
||||
$checkStart = \microtime(true);
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume) {
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
if (!\is_readable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception('Device ' . $key . ' dir is not readable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (!\is_writable($device->getRoot())) {
|
||||
throw new Exception('Device '.$key.' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new Exception('Device ' . $key . ' dir is not writable', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -304,13 +306,15 @@ App::get('/v1/health/anti-virus')
|
|||
$output['status'] = 'disabled';
|
||||
$output['version'] = '';
|
||||
} else {
|
||||
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
|
||||
$antivirus = new Network(
|
||||
App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
|
||||
);
|
||||
|
||||
try {
|
||||
$output['version'] = @$antivirus->version();
|
||||
$output['status'] = (@$antivirus->ping()) ? 'pass' : 'fail';
|
||||
} catch( \Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
throw new Exception('Antivirus is not available', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
}
|
||||
|
|
@ -338,7 +342,7 @@ App::get('/v1/health/stats') // Currently only used internally
|
|||
$response
|
||||
->json([
|
||||
'storage' => [
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot().'/')),
|
||||
'used' => Storage::human($deviceFiles->getDirectorySize($deviceFiles->getRoot() . '/')),
|
||||
'partitionTotal' => Storage::human($deviceFiles->getPartitionTotalSpace()),
|
||||
'partitionFree' => Storage::human($deviceFiles->getPartitionFreeSpace()),
|
||||
],
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ App::post('/v1/projects')
|
|||
}
|
||||
|
||||
$projectId = ($projectId == 'unique()') ? $dbForConsole->getId() : $projectId;
|
||||
if($projectId === 'console') {
|
||||
if ($projectId === 'console') {
|
||||
throw new Exception("'console' is a reserved project.", 400, Exception::PROJECT_RESERVED_PROJECT);
|
||||
}
|
||||
$project = $dbForConsole->createDocument('projects', new Document([
|
||||
|
|
@ -106,7 +106,7 @@ App::post('/v1/projects')
|
|||
'search' => implode(' ', [$projectId, $name]),
|
||||
]));
|
||||
/** @var array $collections */
|
||||
$collections = Config::getParam('collections', []);
|
||||
$collections = Config::getParam('collections', []);
|
||||
|
||||
$dbForProject->setNamespace("_{$project->getId()}");
|
||||
$dbForProject->create('appwrite');
|
||||
|
|
@ -118,7 +118,7 @@ App::post('/v1/projects')
|
|||
$adapter->setup();
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
$attributes = [];
|
||||
|
|
@ -281,7 +281,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
|
@ -303,7 +303,7 @@ App::get('/v1/projects/:projectId/usage')
|
|||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
@ -374,8 +374,7 @@ App::patch('/v1/projects/:projectId')
|
|||
->setAttribute('legalCity', $legalCity)
|
||||
->setAttribute('legalAddress', $legalAddress)
|
||||
->setAttribute('legalTaxId', $legalTaxId)
|
||||
->setAttribute('search', implode(' ', [$projectId, $name]))
|
||||
);
|
||||
->setAttribute('search', implode(' ', [$projectId, $name])));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
|
@ -391,7 +390,7 @@ App::patch('/v1/projects/:projectId/service')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_PROJECT)
|
||||
->param('projectId', '', new UID(), 'Project unique ID.')
|
||||
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), function ($element) {return $element['optional'];})), true), 'Service name.')
|
||||
->param('service', '', new WhiteList(array_keys(array_filter(Config::getParam('services'), fn($element) => $element['optional'])), true), 'Service name.')
|
||||
->param('status', null, new Boolean(), 'Service status.')
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
|
|
@ -470,8 +469,7 @@ App::patch('/v1/projects/:projectId/auth/limit')
|
|||
$auths['limit'] = $limit;
|
||||
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project
|
||||
->setAttribute('auths', $auths)
|
||||
);
|
||||
->setAttribute('auths', $auths));
|
||||
|
||||
$response->dynamic($project, Response::MODEL_PROJECT);
|
||||
});
|
||||
|
|
@ -584,6 +582,8 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
|
||||
$security = (bool) filter_var($security, FILTER_VALIDATE_BOOLEAN);
|
||||
|
||||
|
||||
|
||||
$webhook = new Document([
|
||||
'$id' => $dbForConsole->getId(),
|
||||
'$read' => ['role:all'],
|
||||
|
|
@ -595,6 +595,7 @@ App::post('/v1/projects/:projectId/webhooks')
|
|||
'security' => $security,
|
||||
'httpUser' => $httpUser,
|
||||
'httpPass' => $httpPass,
|
||||
'signatureKey' => \bin2hex(\random_bytes(64)),
|
||||
]);
|
||||
|
||||
$webhook = $dbForConsole->createDocument('webhooks', $webhook);
|
||||
|
|
@ -688,9 +689,10 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
->param('security', false, new Boolean(true), 'Certificate verification, false for disabled or true for enabled.')
|
||||
->param('httpUser', '', new Text(256), 'Webhook HTTP user. Max length: 256 chars.', true)
|
||||
->param('httpPass', '', new Text(256), 'Webhook HTTP password. Max length: 256 chars.', true)
|
||||
->param('signatureKey', null, new Text(256), 'Webhook signature key. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, Response $response, Database $dbForConsole) {
|
||||
->action(function (string $projectId, string $webhookId, string $name, array $events, string $url, bool $security, string $httpUser, string $httpPass, string $signatureKey, Response $response, Database $dbForConsole) {
|
||||
|
||||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
|
|
@ -718,8 +720,11 @@ App::put('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
->setAttribute('httpPass', $httpPass)
|
||||
;
|
||||
|
||||
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
if (!empty($signatureKey)) {
|
||||
$webhook->setAttribute('signatureKey', $signatureKey);
|
||||
}
|
||||
|
||||
$dbForConsole->updateDocument('webhooks', $webhook->getId(), $webhook);
|
||||
$dbForConsole->deleteCachedDocument('projects', $project->getId());
|
||||
|
||||
$response->dynamic($webhook, Response::MODEL_WEBHOOK);
|
||||
|
|
@ -751,7 +756,7 @@ App::delete('/v1/projects/:projectId/webhooks/:webhookId')
|
|||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
]);
|
||||
|
||||
if($webhook === false || $webhook->isEmpty()) {
|
||||
if ($webhook === false || $webhook->isEmpty()) {
|
||||
throw new Exception('Webhook not found', 404, Exception::WEBHOOK_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -941,7 +946,7 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
|
|||
new Query('projectId', Query::TYPE_EQUAL, [$project->getId()])
|
||||
]);
|
||||
|
||||
if($key === false || $key->isEmpty()) {
|
||||
if ($key === false || $key->isEmpty()) {
|
||||
throw new Exception('Key not found', 404, Exception::KEY_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ use Utopia\Validator\Integer;
|
|||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\Swoole\Request;
|
||||
|
||||
App::post('/v1/storage/buckets')
|
||||
->desc('Create bucket')
|
||||
|
|
@ -262,8 +262,7 @@ App::put('/v1/storage/buckets/:bucketId')
|
|||
->setAttribute('enabled', (bool) filter_var($enabled, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('encryption', (bool) filter_var($encryption, FILTER_VALIDATE_BOOLEAN))
|
||||
->setAttribute('permission', $permission)
|
||||
->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN))
|
||||
);
|
||||
->setAttribute('antivirus', (bool) filter_var($antivirus, FILTER_VALIDATE_BOOLEAN)));
|
||||
|
||||
$audits
|
||||
->setResource('storage/buckets/' . $bucket->getId())
|
||||
|
|
@ -360,8 +359,10 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->action(function (string $bucketId, string $fileId, array $file, ?array $read, ?array $write, Request $request, Response $response, Database $dbForProject, Document $user, Audit $audits, Stats $usage, Event $events, string $mode, Device $deviceFiles, Device $deviceLocal) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -462,7 +463,7 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
}
|
||||
|
||||
// Save to storage
|
||||
$fileSize??=$deviceLocal->getFileSize($fileTmpName);
|
||||
$fileSize ??= $deviceLocal->getFileSize($fileTmpName);
|
||||
$path = $deviceFiles->getPath($fileId . '.' . \pathinfo($fileName, PATHINFO_EXTENSION));
|
||||
$path = str_ireplace($deviceFiles->getRoot(), $deviceFiles->getRoot() . DIRECTORY_SEPARATOR . $bucket->getId(), $path); // Add bucket id to path after root
|
||||
|
||||
|
|
@ -492,8 +493,10 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
$write = (is_null($write) && !$user->isEmpty()) ? ['user:' . $user->getId()] : $write ?? [];
|
||||
if ($chunksUploaded === $chunks) {
|
||||
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled' && $bucket->getAttribute('antivirus', true) && $fileSize <= APP_LIMIT_ANTIVIRUS && App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL) === Storage::DEVICE_LOCAL) {
|
||||
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
|
||||
$antivirus = new Network(
|
||||
App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
|
||||
);
|
||||
|
||||
if (!$antivirus->fileScan($path)) {
|
||||
$deviceFiles->delete($path);
|
||||
|
|
@ -587,7 +590,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
} else {
|
||||
$file = $dbForProject->updateDocument('bucket_' . $bucket->getInternalId(), $fileId, $file);
|
||||
}
|
||||
|
||||
}
|
||||
} catch (StructureException $exception) {
|
||||
throw new Exception($exception->getMessage(), 400, Exception::DOCUMENT_INVALID_STRUCTURE);
|
||||
|
|
@ -604,7 +606,6 @@ App::post('/v1/storage/buckets/:bucketId/files')
|
|||
->setParam('storage.files.create', 1)
|
||||
->setParam('bucketId', $bucketId)
|
||||
;
|
||||
|
||||
} else {
|
||||
try {
|
||||
if ($file->isEmpty()) {
|
||||
|
|
@ -689,8 +690,10 @@ App::get('/v1/storage/buckets/:bucketId/files')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -765,8 +768,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -837,8 +842,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -879,7 +886,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
$mime = $file->getAttribute('mimeType');
|
||||
|
||||
if (!\in_array($mime, $inputs) || $file->getAttribute('sizeActual') > (int) App::getEnv('_APP_STORAGE_PREVIEW_LIMIT', 20000000)) {
|
||||
if(!\in_array($mime, $inputs)) {
|
||||
if (!\in_array($mime, $inputs)) {
|
||||
$path = (\array_key_exists($mime, $fileLogos)) ? $fileLogos[$mime] : $fileLogos['default'];
|
||||
} else {
|
||||
// it was an image but the file size exceeded the limit
|
||||
|
|
@ -904,7 +911,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/preview')
|
|||
$cache = new Cache(new Filesystem(APP_STORAGE_CACHE . DIRECTORY_SEPARATOR . 'app-' . $project->getId() . DIRECTORY_SEPARATOR . $bucketId . DIRECTORY_SEPARATOR . $fileId)); // Limit file number or size
|
||||
$data = $cache->load($key, 60 * 60 * 24 * 30 * 3/* 3 months */);
|
||||
|
||||
if(empty($output)) {
|
||||
if (empty($output)) {
|
||||
// when file extension is not provided and the mime type is not one of our supported outputs
|
||||
// we fallback to `jpg` output format
|
||||
$output = empty($type) ? (array_search($mime, $outputs) ?? 'jpg') : $type;
|
||||
|
|
@ -1003,8 +1010,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/download')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -1140,8 +1149,10 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/view')
|
|||
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -1311,8 +1322,10 @@ App::put('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
}
|
||||
}
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -1386,8 +1399,10 @@ App::delete('/v1/storage/buckets/:bucketId/files/:fileId')
|
|||
->action(function (string $bucketId, string $fileId, Response $response, Database $dbForProject, Event $events, Audit $audits, Stats $usage, string $mode, Device $deviceFiles, Document $project) {
|
||||
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
|
||||
|
||||
if ($bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)) {
|
||||
if (
|
||||
$bucket->isEmpty()
|
||||
|| (!$bucket->getAttribute('enabled') && $mode !== APP_MODE_ADMIN)
|
||||
) {
|
||||
throw new Exception('Bucket not found', 404, Exception::STORAGE_BUCKET_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -1527,10 +1542,10 @@ App::get('/v1/storage/usage')
|
|||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit-\count($requestDocs);
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
@ -1636,10 +1651,10 @@ App::get('/v1/storage/:bucketId/usage')
|
|||
}
|
||||
|
||||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit-\count($requestDocs);
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -59,8 +59,8 @@ App::post('/v1/teams')
|
|||
$teamId = $teamId == 'unique()' ? $dbForProject->getId() : $teamId;
|
||||
$team = Authorization::skip(fn() => $dbForProject->createDocument('teams', new Document([
|
||||
'$id' => $teamId ,
|
||||
'$read' => ['team:'.$teamId],
|
||||
'$write' => ['team:'.$teamId .'/owner'],
|
||||
'$read' => ['team:' . $teamId],
|
||||
'$write' => ['team:' . $teamId . '/owner'],
|
||||
'name' => $name,
|
||||
'total' => ($isPrivilegedUser || $isAppUser) ? 0 : 1,
|
||||
'dateCreated' => \time(),
|
||||
|
|
@ -71,8 +71,8 @@ App::post('/v1/teams')
|
|||
$membershipId = $dbForProject->getId();
|
||||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['user:'.$user->getId(), 'team:'.$team->getId()],
|
||||
'$write' => ['user:'.$user->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'$read' => ['user:' . $user->getId(), 'team:' . $team->getId()],
|
||||
'$write' => ['user:' . $user->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'userId' => $user->getId(),
|
||||
'teamId' => $team->getId(),
|
||||
'roles' => $roles,
|
||||
|
|
@ -95,7 +95,7 @@ App::post('/v1/teams')
|
|||
|
||||
$audits
|
||||
->setParam('event', 'teams.create')
|
||||
->setParam('resource', 'team/'.$teamId)
|
||||
->setParam('resource', 'team/' . $teamId)
|
||||
->setParam('data', $team->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -198,10 +198,9 @@ App::put('/v1/teams/:teamId')
|
|||
throw new Exception('Team not found', 404, Exception::TEAM_NOT_FOUND);
|
||||
}
|
||||
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(),$team
|
||||
$team = $dbForProject->updateDocument('teams', $team->getId(), $team
|
||||
->setAttribute('name', $name)
|
||||
->setAttribute('search', implode(' ', [$teamId, $name]))
|
||||
);
|
||||
->setAttribute('search', implode(' ', [$teamId, $name])));
|
||||
|
||||
$events->setParam('teamId', $team->getId());
|
||||
$audits->setResource('team/' . $team->getId());
|
||||
|
|
@ -260,7 +259,7 @@ App::delete('/v1/teams/:teamId')
|
|||
|
||||
$audits
|
||||
->setParam('event', 'teams.delete')
|
||||
->setParam('resource', 'team/'.$teamId)
|
||||
->setParam('resource', 'team/' . $teamId)
|
||||
->setParam('data', $team->getArrayCopy())
|
||||
;
|
||||
|
||||
|
|
@ -284,7 +283,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
->param('teamId', '', new UID(), 'Team ID.')
|
||||
->param('email', '', new Email(), 'Email of the new team member.')
|
||||
->param('roles', [], new ArrayList(new Key(), APP_LIMIT_ARRAY_PARAMS_SIZE), 'Array of strings. Use this param to set the user roles in the team. A role can be any string. Learn more about [roles and permissions](/docs/permissions). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' roles are allowed, each 32 characters long.')
|
||||
->param('url', '', function ($clients) { return new Host($clients); }, 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('url', '', fn($clients) => new Host($clients), 'URL to redirect the user back to your app from the invitation email. Only URLs from hostnames in your project platform list are allowed. This requirement helps to prevent an [open redirect](https://cheatsheetseries.owasp.org/cheatsheets/Unvalidated_Redirects_and_Forwards_Cheat_Sheet.html) attack against your project API.', false, ['clients']) // TODO add our own built-in confirm page
|
||||
->param('name', '', new Text(128), 'Name of the new team member. Max length: 128 chars.', true)
|
||||
->inject('response')
|
||||
->inject('project')
|
||||
|
|
@ -296,13 +295,13 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
->inject('events')
|
||||
->action(function (string $teamId, string $email, array $roles, string $url, string $name, Response $response, Document $project, Document $user, Database $dbForProject, Locale $locale, EventAudit $audits, Mail $mails, Event $events) {
|
||||
|
||||
if(empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if (!$isPrivilegedUser && !$isAppUser && empty(App::getEnv('_APP_SMTP_HOST'))) {
|
||||
throw new Exception('SMTP Disabled', 503, Exception::GENERAL_SMTP_DISABLED);
|
||||
}
|
||||
|
||||
$email = \strtolower($email);
|
||||
$name = (empty($name)) ? $email : $name;
|
||||
$team = $dbForProject->getDocument('teams', $teamId);
|
||||
|
|
@ -314,13 +313,12 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$invitee = $dbForProject->findOne('users', [new Query('email', Query::TYPE_EQUAL, [$email])]); // Get user by email address
|
||||
|
||||
if (empty($invitee)) { // Create new user if no user with same email found
|
||||
|
||||
$limit = $project->getAttribute('auths', [])['limit'] ?? 0;
|
||||
|
||||
if ($limit !== 0 && $project->getId() !== 'console') { // check users limit, console invites are allways allowed.
|
||||
$total = $dbForProject->count('users', [], APP_LIMIT_USERS);
|
||||
|
||||
if($total >= $limit) {
|
||||
if ($total >= $limit) {
|
||||
throw new Exception('Project registration is restricted. Contact your administrator for more information.', 501, Exception::USER_COUNT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
|
@ -329,16 +327,16 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$userId = $dbForProject->getId();
|
||||
$invitee = Authorization::skip(fn() => $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['user:'.$userId, 'role:all'],
|
||||
'$write' => ['user:'.$userId],
|
||||
'$read' => ['user:' . $userId, 'role:all'],
|
||||
'$write' => ['user:' . $userId],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
'password' => Auth::passwordHash(Auth::passwordGenerator()),
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
/**
|
||||
* Set the password update time to 0 for users created using
|
||||
* team invite and OAuth to allow password updates without an
|
||||
* old password
|
||||
*/
|
||||
'passwordUpdate' => 0,
|
||||
'registration' => \time(),
|
||||
|
|
@ -355,7 +353,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
}
|
||||
|
||||
$isOwner = Authorization::isRole('team:'.$team->getId().'/owner');;
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to send invitations for this team', 401, Exception::USER_UNAUTHORIZED);
|
||||
|
|
@ -367,7 +365,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
$membership = new Document([
|
||||
'$id' => $membershipId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:'.$invitee->getId(), 'team:'.$team->getId().'/owner'],
|
||||
'$write' => ['user:' . $invitee->getId(), 'team:' . $team->getId() . '/owner'],
|
||||
'userId' => $invitee->getId(),
|
||||
'teamId' => $team->getId(),
|
||||
'roles' => $roles,
|
||||
|
|
@ -382,7 +380,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
try {
|
||||
$membership = Authorization::skip(fn() => $dbForProject->createDocument('memberships', $membership));
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('User has already been invited or is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
throw new Exception('User is already a member of this team', 409, Exception::TEAM_INVITE_ALREADY_EXISTS);
|
||||
}
|
||||
$team->setAttribute('total', $team->getAttribute('total', 0) + 1);
|
||||
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
|
|
@ -414,7 +412,7 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
}
|
||||
|
||||
$audits
|
||||
->setResource('team/'.$teamId)
|
||||
->setResource('team/' . $teamId)
|
||||
;
|
||||
|
||||
$events
|
||||
|
|
@ -423,11 +421,13 @@ App::post('/v1/teams/:teamId/memberships')
|
|||
;
|
||||
|
||||
$response->setStatusCode(Response::STATUS_CODE_CREATED);
|
||||
$response->dynamic($membership
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
, Response::MODEL_MEMBERSHIP);
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
|
||||
App::get('/v1/teams/:teamId/memberships')
|
||||
|
|
@ -490,7 +490,7 @@ App::get('/v1/teams/:teamId/memberships')
|
|||
|
||||
$memberships = array_filter($memberships, fn(Document $membership) => !empty($membership->getAttribute('userId')));
|
||||
|
||||
$memberships = array_map(function($membership) use ($dbForProject, $team) {
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $team) {
|
||||
$user = $dbForProject->getDocument('users', $membership->getAttribute('userId'));
|
||||
|
||||
$membership
|
||||
|
|
@ -533,7 +533,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$membership = $dbForProject->getDocument('memberships', $membershipId);
|
||||
|
||||
if($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
if ($membership->isEmpty() || empty($membership->getAttribute('userId'))) {
|
||||
throw new Exception('Membership not found', 404, Exception::MEMBERSHIP_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -545,7 +545,7 @@ App::get('/v1/teams/:teamId/memberships/:membershipId')
|
|||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
;
|
||||
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP );
|
||||
$response->dynamic($membership, Response::MODEL_MEMBERSHIP);
|
||||
});
|
||||
|
||||
App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
||||
|
|
@ -588,7 +588,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId')
|
|||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');;
|
||||
$isOwner = Authorization::isRole('team:' . $team->getId() . '/owner');
|
||||
|
||||
if (!$isOwner && !$isPrivilegedUser && !$isAppUser) { // Not owner, not admin, not app (server)
|
||||
throw new Exception('User is not allowed to modify roles', 401, Exception::USER_UNAUTHORIZED);
|
||||
|
|
@ -667,7 +667,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
}
|
||||
|
||||
if ($userId !== $membership->getAttribute('userId')) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
}
|
||||
|
||||
if ($user->isEmpty()) {
|
||||
|
|
@ -675,7 +675,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
}
|
||||
|
||||
if ($membership->getAttribute('userId') !== $user->getId()) {
|
||||
throw new Exception('Invite does not belong to current user ('.$user->getAttribute('email').')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
throw new Exception('Invite does not belong to current user (' . $user->getAttribute('email') . ')', 401, Exception::TEAM_INVITE_MISMATCH);
|
||||
}
|
||||
|
||||
if ($membership->getAttribute('confirm') === true) {
|
||||
|
|
@ -693,7 +693,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
|
||||
// Log user in
|
||||
|
||||
Authorization::setRole('user:'.$user->getId());
|
||||
Authorization::setRole('user:' . $user->getId());
|
||||
|
||||
$detector = new Detector($request->getUserAgent('UNKNOWN'));
|
||||
$record = $geodb->get($request->getIP());
|
||||
|
|
@ -712,13 +712,12 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
], $detector->getOS(), $detector->getClient(), $detector->getDevice()));
|
||||
|
||||
$session = $dbForProject->createDocument('sessions', $session
|
||||
->setAttribute('$read', ['user:'.$user->getId()])
|
||||
->setAttribute('$write', ['user:'.$user->getId()])
|
||||
);
|
||||
->setAttribute('$read', ['user:' . $user->getId()])
|
||||
->setAttribute('$write', ['user:' . $user->getId()]));
|
||||
|
||||
$dbForProject->deleteCachedDocument('users', $user->getId());
|
||||
|
||||
Authorization::setRole('user:'.$userId);
|
||||
Authorization::setRole('user:' . $userId);
|
||||
|
||||
$membership = $dbForProject->updateDocument('memberships', $membership->getId(), $membership);
|
||||
|
||||
|
|
@ -726,7 +725,7 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
|
||||
$team = Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team->setAttribute('total', $team->getAttribute('total', 0) + 1)));
|
||||
|
||||
$audits->setResource('team/'.$teamId);
|
||||
$audits->setResource('team/' . $teamId);
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
|
|
@ -740,15 +739,17 @@ App::patch('/v1/teams/:teamId/memberships/:membershipId/status')
|
|||
}
|
||||
|
||||
$response
|
||||
->addCookie(Auth::$cookieName.'_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName . '_legacy', Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, null)
|
||||
->addCookie(Auth::$cookieName, Auth::encodeSession($user->getId(), $secret), $expiry, '/', Config::getParam('cookieDomain'), ('https' == $protocol), true, Config::getParam('cookieSamesite'))
|
||||
;
|
||||
|
||||
$response->dynamic($membership
|
||||
$response->dynamic(
|
||||
$membership
|
||||
->setAttribute('teamName', $team->getAttribute('name'))
|
||||
->setAttribute('userName', $user->getAttribute('name'))
|
||||
->setAttribute('userEmail', $user->getAttribute('email'))
|
||||
, Response::MODEL_MEMBERSHIP);
|
||||
->setAttribute('userEmail', $user->getAttribute('email')),
|
||||
Response::MODEL_MEMBERSHIP
|
||||
);
|
||||
});
|
||||
|
||||
App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
||||
|
|
@ -807,7 +808,7 @@ App::delete('/v1/teams/:teamId/memberships/:membershipId')
|
|||
Authorization::skip(fn() => $dbForProject->updateDocument('teams', $team->getId(), $team));
|
||||
}
|
||||
|
||||
$audits->setResource('team/'.$teamId);
|
||||
$audits->setResource('team/' . $teamId);
|
||||
|
||||
$events
|
||||
->setParam('teamId', $team->getId())
|
||||
|
|
@ -850,7 +851,7 @@ App::get('/v1/teams/:teamId/logs')
|
|||
}
|
||||
|
||||
$audit = new Audit($dbForProject);
|
||||
$resource = 'team/'.$team->getId();
|
||||
$resource = 'team/' . $team->getId();
|
||||
$logs = $audit->getLogsByResource($resource, $limit, $offset);
|
||||
|
||||
$output = [];
|
||||
|
|
@ -890,8 +891,8 @@ App::get('/v1/teams/:teamId/logs')
|
|||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
|
|
|||
|
|
@ -3,12 +3,17 @@
|
|||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Auth\Validator\Password;
|
||||
use Appwrite\Detector\Detector;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Audit as EventAudit;
|
||||
use Appwrite\Network\Validator\Email;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Audit\Audit;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Locale\Locale;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Exception\Duplicate;
|
||||
|
|
@ -21,6 +26,7 @@ use Utopia\Validator\WhiteList;
|
|||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Boolean;
|
||||
use MaxMind\Db\Reader;
|
||||
|
||||
App::post('/v1/users')
|
||||
->desc('Create User')
|
||||
|
|
@ -42,11 +48,7 @@ App::post('/v1/users')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $email, $password, $name, $response, $dbForProject, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $email, string $password, string $name, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$email = \strtolower($email);
|
||||
|
||||
|
|
@ -55,7 +57,7 @@ App::post('/v1/users')
|
|||
$user = $dbForProject->createDocument('users', new Document([
|
||||
'$id' => $userId,
|
||||
'$read' => ['role:all'],
|
||||
'$write' => ['user:'.$userId],
|
||||
'$write' => ['user:' . $userId],
|
||||
'email' => $email,
|
||||
'emailVerification' => false,
|
||||
'status' => true,
|
||||
|
|
@ -106,10 +108,7 @@ App::get('/v1/users')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($search, $limit, $offset, $cursor, $cursorDirection, $orderType, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $search, int $limit, int $offset, string $cursor, string $cursorDirection, string $orderType, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
if (!empty($cursor)) {
|
||||
$cursorUser = $dbForProject->getDocument('users', $cursor);
|
||||
|
|
@ -150,10 +149,7 @@ App::get('/v1/users/:userId')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -182,10 +178,7 @@ App::get('/v1/users/:userId/prefs')
|
|||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForProject, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -217,11 +210,7 @@ App::get('/v1/users/:userId/sessions')
|
|||
->inject('dbForProject')
|
||||
->inject('locale')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForProject, $locale, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject, Locale $locale, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -231,10 +220,10 @@ App::get('/v1/users/:userId/sessions')
|
|||
|
||||
$sessions = $user->getAttribute('sessions', []);
|
||||
|
||||
foreach ($sessions as $key => $session) {
|
||||
foreach ($sessions as $key => $session) {
|
||||
/** @var Document $session */
|
||||
|
||||
$countryName = $locale->getText('countries.'.strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$countryName = $locale->getText('countries.' . strtolower($session->getAttribute('countryCode')), $locale->getText('locale.country.unknown'));
|
||||
$session->setAttribute('countryName', $countryName);
|
||||
$session->setAttribute('current', false);
|
||||
|
||||
|
|
@ -264,10 +253,7 @@ App::get('/v1/users/:userId/memberships')
|
|||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function ($userId, $response, $dbForProject) {
|
||||
/** @var string $userId */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -275,7 +261,7 @@ App::get('/v1/users/:userId/memberships')
|
|||
throw new Exception('User not found', 404, Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
$memberships = array_map(function($membership) use ($dbForProject, $user) {
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $user) {
|
||||
$team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId'));
|
||||
|
||||
$membership
|
||||
|
|
@ -311,13 +297,7 @@ App::get('/v1/users/:userId/logs')
|
|||
->inject('locale')
|
||||
->inject('geodb')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $limit, $offset, $response, $dbForProject, $locale, $geodb, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var MaxMind\Db\Reader $geodb */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, int $limit, int $offset, Response $response, Database $dbForProject, Locale $locale, Reader $geodb, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -362,8 +342,8 @@ App::get('/v1/users/:userId/logs')
|
|||
$record = $geodb->get($log['ip']);
|
||||
|
||||
if ($record) {
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.'.strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
$output[$i]['countryCode'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), false) ? \strtolower($record['country']['iso_code']) : '--';
|
||||
$output[$i]['countryName'] = $locale->getText('countries.' . strtolower($record['country']['iso_code']), $locale->getText('locale.country.unknown'));
|
||||
} else {
|
||||
$output[$i]['countryCode'] = '--';
|
||||
$output[$i]['countryName'] = $locale->getText('locale.country.unknown');
|
||||
|
|
@ -398,11 +378,7 @@ App::patch('/v1/users/:userId/status')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $status, $response, $dbForProject, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, bool $status, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -441,11 +417,7 @@ App::patch('/v1/users/:userId/verification')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $emailVerification, $response, $dbForProject, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, bool $emailVerification, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -484,11 +456,7 @@ App::patch('/v1/users/:userId/name')
|
|||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function ($userId, $name, $response, $dbForProject, $audits, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $name, Response $response, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -504,7 +472,7 @@ App::patch('/v1/users/:userId/name')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
;
|
||||
|
||||
$events
|
||||
|
|
@ -532,11 +500,7 @@ App::patch('/v1/users/:userId/password')
|
|||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function ($userId, $password, $response, $dbForProject, $audits, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $password, Response $response, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -551,7 +515,7 @@ App::patch('/v1/users/:userId/password')
|
|||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
;
|
||||
|
||||
$events
|
||||
|
|
@ -579,11 +543,7 @@ App::patch('/v1/users/:userId/email')
|
|||
->inject('dbForProject')
|
||||
->inject('audits')
|
||||
->inject('events')
|
||||
->action(function ($userId, $email, $response, $dbForProject, $audits, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, string $email, Response $response, Database $dbForProject, EventAudit $audits, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -605,13 +565,13 @@ App::patch('/v1/users/:userId/email')
|
|||
|
||||
try {
|
||||
$user = $dbForProject->updateDocument('users', $user->getId(), $user);
|
||||
} catch(Duplicate $th) {
|
||||
} catch (Duplicate $th) {
|
||||
throw new Exception('Email already exists', 409, Exception::USER_EMAIL_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
|
||||
$audits
|
||||
->setResource('user/'.$user->getId())
|
||||
->setResource('user/' . $user->getId())
|
||||
;
|
||||
|
||||
$events
|
||||
|
|
@ -639,11 +599,7 @@ App::patch('/v1/users/:userId/prefs')
|
|||
->inject('dbForProject')
|
||||
->inject('usage')
|
||||
->inject('events')
|
||||
->action(function ($userId, $prefs, $response, $dbForProject, $usage, $events) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
->action(function (string $userId, array $prefs, Response $response, Database $dbForProject, Stats $usage, Event $events) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -681,11 +637,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $sessionId, $response, $dbForProject, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, string $sessionId, Response $response, Database $dbForProject, Event $events, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -695,7 +647,7 @@ App::delete('/v1/users/:userId/sessions/:sessionId')
|
|||
|
||||
$session = $dbForProject->getDocument('sessions', $sessionId);
|
||||
|
||||
if($session->isEmpty()) {
|
||||
if ($session->isEmpty()) {
|
||||
throw new Exception('Session not found', 404, Exception::USER_SESSION_NOT_FOUND);
|
||||
}
|
||||
|
||||
|
|
@ -732,11 +684,7 @@ App::delete('/v1/users/:userId/sessions')
|
|||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForProject, $events, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject, Event $events, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -777,18 +725,13 @@ App::delete('/v1/users/:userId')
|
|||
->label('sdk.description', '/docs/references/users/delete.md')
|
||||
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
|
||||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->param('userId', '', function () {return new UID();}, 'User ID.')
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('events')
|
||||
->inject('deletes')
|
||||
->inject('usage')
|
||||
->action(function ($userId, $response, $dbForProject, $events, $deletes, $usage) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
->action(function (string $userId, Response $response, Database $dbForProject, Event $events, Delete $deletes, Stats $usage) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
|
|
@ -829,13 +772,11 @@ App::get('/v1/users/usage')
|
|||
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
|
||||
->label('sdk.response.model', Response::MODEL_USAGE_USERS)
|
||||
->param('range', '30d', new WhiteList(['24h', '7d', '30d', '90d'], true), 'Date range.', true)
|
||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn($value) => "oauth-".$value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||
->param('provider', '', new WhiteList(\array_merge(['email', 'anonymous'], \array_map(fn($value) => "oauth-" . $value, \array_keys(Config::getParam('providers', [])))), true), 'Provider Name.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('register')
|
||||
->action(function ($range, $provider, $response, $dbForProject) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
->action(function (string $range, string $provider, Response $response, Database $dbForProject) {
|
||||
|
||||
$usage = [];
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled') {
|
||||
|
|
@ -871,7 +812,7 @@ App::get('/v1/users/usage')
|
|||
|
||||
$stats = [];
|
||||
|
||||
Authorization::skip(function() use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
Authorization::skip(function () use ($dbForProject, $periods, $range, $metrics, &$stats) {
|
||||
foreach ($metrics as $metric) {
|
||||
$limit = $periods[$range]['limit'];
|
||||
$period = $periods[$range]['period'];
|
||||
|
|
@ -880,7 +821,7 @@ App::get('/v1/users/usage')
|
|||
new Query('period', Query::TYPE_EQUAL, [$period]),
|
||||
new Query('metric', Query::TYPE_EQUAL, [$metric]),
|
||||
], $limit, 0, ['time'], [Database::ORDER_DESC]);
|
||||
|
||||
|
||||
$stats[$metric] = [];
|
||||
foreach ($requestDocs as $requestDoc) {
|
||||
$stats[$metric][] = [
|
||||
|
|
@ -892,9 +833,8 @@ App::get('/v1/users/usage')
|
|||
// backfill metrics with empty values for graphs
|
||||
$backfill = $limit - \count($requestDocs);
|
||||
while ($backfill > 0) {
|
||||
|
||||
$last = $limit - $backfill - 1; // array index of last added metric
|
||||
$diff = match($period) { // convert period to seconds for unix timestamp math
|
||||
$diff = match ($period) { // convert period to seconds for unix timestamp math
|
||||
'30m' => 1800,
|
||||
'1d' => 86400,
|
||||
};
|
||||
|
|
@ -905,7 +845,7 @@ App::get('/v1/users/usage')
|
|||
$backfill--;
|
||||
}
|
||||
$stats[$metric] = array_reverse($stats[$metric]);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$usage = new Document([
|
||||
|
|
@ -919,8 +859,7 @@ App::get('/v1/users/usage')
|
|||
'sessionsProviderCreate' => $stats["users.sessions.$provider.create"],
|
||||
'sessionsDelete' => $stats["users.sessions.delete"]
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
$response->dynamic($usage, Response::MODEL_USAGE_USERS);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
use Utopia\App;
|
||||
use Utopia\Locale\Locale;
|
||||
use Utopia\Logger\Logger;
|
||||
use Utopia\Logger\Log;
|
||||
use Utopia\Logger\Log\User;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\View;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Extend\Exception as AppwriteException;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Domains\Domain;
|
||||
use Appwrite\Auth\Auth;
|
||||
|
|
@ -18,6 +20,7 @@ use Appwrite\Utopia\Response\Filters\V11 as ResponseV11;
|
|||
use Appwrite\Utopia\Response\Filters\V12 as ResponseV12;
|
||||
use Appwrite\Utopia\Response\Filters\V13 as ResponseV13;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
|
@ -31,16 +34,7 @@ Config::setParam('domainVerification', false);
|
|||
Config::setParam('cookieDomain', 'localhost');
|
||||
Config::setParam('cookieSamesite', Response::COOKIE_SAMESITE_NONE);
|
||||
|
||||
App::init(function ($utopia, $request, $response, $console, $project, $dbForConsole, $user, $locale, $clients) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $console */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Locale\Locale $locale */
|
||||
/** @var array $clients */
|
||||
App::init(function (App $utopia, Request $request, Response $response, Document $console, Document $project, Database $dbForConsole, Document $user, Locale $locale, array $clients) {
|
||||
|
||||
/*
|
||||
* Request format
|
||||
|
|
@ -50,14 +44,14 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
|
||||
$requestFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
|
||||
if ($requestFormat) {
|
||||
switch($requestFormat) {
|
||||
case version_compare ($requestFormat , '0.12.0', '<') :
|
||||
switch ($requestFormat) {
|
||||
case version_compare($requestFormat, '0.12.0', '<'):
|
||||
Request::setFilter(new RequestV12());
|
||||
break;
|
||||
case version_compare ($requestFormat , '0.13.0', '<') :
|
||||
case version_compare($requestFormat, '0.13.0', '<'):
|
||||
Request::setFilter(new RequestV13());
|
||||
break;
|
||||
case version_compare ($requestFormat , '0.14.0', '<') :
|
||||
case version_compare($requestFormat, '0.14.0', '<'):
|
||||
Request::setFilter(new RequestV14());
|
||||
break;
|
||||
default:
|
||||
|
|
@ -75,21 +69,21 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
if (empty($domain->get()) || !$domain->isKnown() || $domain->isTest()) {
|
||||
$domains[$domain->get()] = false;
|
||||
Console::warning($domain->get() . ' is not a publicly accessible domain. Skipping SSL certificate generation.');
|
||||
} elseif(str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
|
||||
} elseif (str_starts_with($request->getURI(), '/.well-known/acme-challenge')) {
|
||||
Console::warning('Skipping SSL certificates generation on ACME challenge.');
|
||||
} else {
|
||||
Authorization::disable();
|
||||
|
||||
$envDomain = App::getEnv('_APP_DOMAIN', '');
|
||||
$mainDomain = null;
|
||||
if(!empty($envDomain) && $envDomain !== 'localhost') {
|
||||
if (!empty($envDomain) && $envDomain !== 'localhost') {
|
||||
$mainDomain = $envDomain;
|
||||
} else {
|
||||
$domainDocument = $dbForConsole->findOne('domains', [], 0, ['_id'], ['ASC']);
|
||||
$mainDomain = $domainDocument ? $domainDocument->getAttribute('domain') : $domain->get();
|
||||
}
|
||||
|
||||
if($mainDomain !== $domain->get()) {
|
||||
if ($mainDomain !== $domain->get()) {
|
||||
Console::warning($domain->get() . ' is not a main domain. Skipping SSL certificate generation.');
|
||||
} else {
|
||||
$domainDocument = $dbForConsole->findOne('domains', [
|
||||
|
|
@ -127,11 +121,11 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
}
|
||||
|
||||
if ($project->isEmpty()) {
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
if (!empty($route->getLabel('sdk.auth', [])) && $project->isEmpty() && ($route->getLabel('scope', '') !== 'public')) {
|
||||
throw new Exception('Missing or unknown project ID', 400, Exception::PROJECT_UNKNOWN);
|
||||
throw new AppwriteException('Missing or unknown project ID', 400, AppwriteException::PROJECT_UNKNOWN);
|
||||
}
|
||||
|
||||
$referrer = $request->getReferer();
|
||||
|
|
@ -149,37 +143,38 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
|
||||
$refDomain = (!$route->getLabel('origin', false)) // This route is publicly accessible
|
||||
? $refDomain
|
||||
: (!empty($protocol) ? $protocol : $request->getProtocol()).'://'.$origin.(!empty($port) ? ':'.$port : '');
|
||||
: (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $origin . (!empty($port) ? ':' . $port : '');
|
||||
|
||||
$selfDomain = new Domain($request->getHostname());
|
||||
$endDomain = new Domain((string)$origin);
|
||||
|
||||
Config::setParam('domainVerification',
|
||||
Config::setParam(
|
||||
'domainVerification',
|
||||
($selfDomain->getRegisterable() === $endDomain->getRegisterable()) &&
|
||||
$endDomain->getRegisterable() !== '');
|
||||
$endDomain->getRegisterable() !== ''
|
||||
);
|
||||
|
||||
Config::setParam('cookieDomain', (
|
||||
$request->getHostname() === 'localhost' ||
|
||||
$request->getHostname() === 'localhost:'.$request->getPort() ||
|
||||
$request->getHostname() === 'localhost:' . $request->getPort() ||
|
||||
(\filter_var($request->getHostname(), FILTER_VALIDATE_IP) !== false)
|
||||
)
|
||||
? null
|
||||
: '.'.$request->getHostname()
|
||||
);
|
||||
: '.' . $request->getHostname());
|
||||
|
||||
/*
|
||||
/*
|
||||
* Response format
|
||||
*/
|
||||
$responseFormat = $request->getHeader('x-appwrite-response-format', App::getEnv('_APP_SYSTEM_RESPONSE_FORMAT', ''));
|
||||
if ($responseFormat) {
|
||||
switch($responseFormat) {
|
||||
case version_compare ($responseFormat , '0.11.2', '<=') :
|
||||
switch ($responseFormat) {
|
||||
case version_compare($responseFormat, '0.11.2', '<='):
|
||||
Response::setFilter(new ResponseV11());
|
||||
break;
|
||||
case version_compare ($responseFormat , '0.12.4', '<='):
|
||||
case version_compare($responseFormat, '0.12.4', '<='):
|
||||
Response::setFilter(new ResponseV12());
|
||||
break;
|
||||
case version_compare ($responseFormat , '0.13.4', '<='):
|
||||
case version_compare($responseFormat, '0.13.4', '<='):
|
||||
Response::setFilter(new ResponseV13());
|
||||
break;
|
||||
default:
|
||||
|
|
@ -197,14 +192,14 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
*/
|
||||
if (App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled') === 'enabled') { // Force HTTPS
|
||||
if ($request->getProtocol() !== 'https') {
|
||||
if($request->getMethod() !== Request::METHOD_GET) {
|
||||
throw new Exception('Method unsupported over HTTP.', 500, Exception::GENERAL_PROTOCOL_UNSUPPORTED);
|
||||
if ($request->getMethod() !== Request::METHOD_GET) {
|
||||
throw new AppwriteException('Method unsupported over HTTP.', 500, AppwriteException::GENERAL_PROTOCOL_UNSUPPORTED);
|
||||
}
|
||||
|
||||
return $response->redirect('https://'.$request->getHostname().$request->getURI());
|
||||
return $response->redirect('https://' . $request->getHostname() . $request->getURI());
|
||||
}
|
||||
|
||||
$response->addHeader('Strict-Transport-Security', 'max-age='.(60 * 60 * 24 * 126)); // 126 days
|
||||
$response->addHeader('Strict-Transport-Security', 'max-age=' . (60 * 60 * 24 * 126)); // 126 days
|
||||
}
|
||||
|
||||
$response
|
||||
|
|
@ -225,11 +220,13 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
$origin = $request->getOrigin($request->getReferer(''));
|
||||
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
|
||||
if (!$originValidator->isValid($origin)
|
||||
if (
|
||||
!$originValidator->isValid($origin)
|
||||
&& \in_array($request->getMethod(), [Request::METHOD_POST, Request::METHOD_PUT, Request::METHOD_PATCH, Request::METHOD_DELETE])
|
||||
&& $route->getLabel('origin', false) !== '*'
|
||||
&& empty($request->getHeader('x-appwrite-key', ''))) {
|
||||
throw new Exception($originValidator->getDescription(), 403, Exception::GENERAL_UNKNOWN_ORIGIN);
|
||||
&& empty($request->getHeader('x-appwrite-key', ''))
|
||||
) {
|
||||
throw new AppwriteException($originValidator->getDescription(), 403, AppwriteException::GENERAL_UNKNOWN_ORIGIN);
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -274,7 +271,7 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
$user = new Document([
|
||||
'$id' => '',
|
||||
'status' => true,
|
||||
'email' => 'app.'.$project->getId().'@service.'.$request->getHostname(),
|
||||
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
|
||||
'password' => '',
|
||||
'name' => $project->getAttribute('name', 'Untitled'),
|
||||
]);
|
||||
|
|
@ -282,47 +279,47 @@ App::init(function ($utopia, $request, $response, $console, $project, $dbForCons
|
|||
$role = Auth::USER_ROLE_APP;
|
||||
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
|
||||
|
||||
Authorization::setRole('role:'.Auth::USER_ROLE_APP);
|
||||
Authorization::setRole('role:' . Auth::USER_ROLE_APP);
|
||||
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
|
||||
}
|
||||
}
|
||||
|
||||
Authorization::setRole('role:'.$role);
|
||||
Authorization::setRole('role:' . $role);
|
||||
|
||||
foreach (Auth::getRoles($user) as $authRole) {
|
||||
Authorization::setRole($authRole);
|
||||
}
|
||||
|
||||
$service = $route->getLabel('sdk.namespace','');
|
||||
if(!empty($service)) {
|
||||
if(array_key_exists($service, $project->getAttribute('services',[]))
|
||||
&& !$project->getAttribute('services',[])[$service]
|
||||
&& !Auth::isPrivilegedUser(Authorization::getRoles())) {
|
||||
throw new Exception('Service is disabled', 503, Exception::GENERAL_SERVICE_DISABLED);
|
||||
$service = $route->getLabel('sdk.namespace', '');
|
||||
if (!empty($service)) {
|
||||
$roles = Authorization::getRoles();
|
||||
if (
|
||||
array_key_exists($service, $project->getAttribute('services', []))
|
||||
&& !$project->getAttribute('services', [])[$service]
|
||||
&& !(Auth::isPrivilegedUser($roles) || Auth::isAppUser($roles))
|
||||
) {
|
||||
throw new AppwriteException('Service is disabled', 503, AppwriteException::GENERAL_SERVICE_DISABLED);
|
||||
}
|
||||
}
|
||||
|
||||
if (!\in_array($scope, $scopes)) {
|
||||
if ($project->isEmpty()) { // Check if permission is denied because project is missing
|
||||
throw new Exception('Project not found', 404, Exception::PROJECT_NOT_FOUND);
|
||||
throw new AppwriteException('Project not found', 404, AppwriteException::PROJECT_NOT_FOUND);
|
||||
}
|
||||
|
||||
throw new Exception($user->getAttribute('email', 'User').' (role: '.\strtolower($roles[$role]['label']).') missing scope ('.$scope.')', 401, Exception::GENERAL_UNAUTHORIZED_SCOPE);
|
||||
throw new AppwriteException($user->getAttribute('email', 'User') . ' (role: ' . \strtolower($roles[$role]['label']) . ') missing scope (' . $scope . ')', 401, AppwriteException::GENERAL_UNAUTHORIZED_SCOPE);
|
||||
}
|
||||
|
||||
if (false === $user->getAttribute('status')) { // Account is blocked
|
||||
throw new Exception('Invalid credentials. User is blocked', 401, Exception::USER_BLOCKED);
|
||||
throw new AppwriteException('Invalid credentials. User is blocked', 401, AppwriteException::USER_BLOCKED);
|
||||
}
|
||||
|
||||
if ($user->getAttribute('reset')) {
|
||||
throw new Exception('Password reset is required', 412, Exception::USER_PASSWORD_RESET_REQUIRED);
|
||||
throw new AppwriteException('Password reset is required', 412, AppwriteException::USER_PASSWORD_RESET_REQUIRED);
|
||||
}
|
||||
|
||||
}, ['utopia', 'request', 'response', 'console', 'project', 'dbForConsole', 'user', 'locale', 'clients']);
|
||||
|
||||
App::options(function ($request, $response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
App::options(function (Request $request, Response $response) {
|
||||
|
||||
$origin = $request->getOrigin();
|
||||
|
||||
|
|
@ -336,15 +333,7 @@ App::options(function ($request, $response) {
|
|||
->noContent();
|
||||
}, ['request', 'response']);
|
||||
|
||||
App::error(function ($error, $utopia, $request, $response, $layout, $project, $logger, $loggerBreadcrumbs) {
|
||||
/** @var Exception $error */
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Logger\Logger $logger */
|
||||
/** @var Utopia\Logger\Log\Breadcrumb[] $loggerBreadcrumbs */
|
||||
App::error(function (Throwable $error, App $utopia, Request $request, Response $response, View $layout, Document $project, ?Logger $logger, array $loggerBreadcrumbs) {
|
||||
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
$route = $utopia->match($request);
|
||||
|
|
@ -354,18 +343,18 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
throw $error;
|
||||
}
|
||||
|
||||
if($logger) {
|
||||
if($error->getCode() >= 500 || $error->getCode() === 0) {
|
||||
if ($logger) {
|
||||
if ($error->getCode() >= 500 || $error->getCode() === 0) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $utopia->getResource('user');
|
||||
} catch(\Throwable $th) {
|
||||
} catch (\Throwable $th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
|
||||
$log = new Utopia\Logger\Log();
|
||||
|
||||
if(isset($user) && !$user->isEmpty()) {
|
||||
if (isset($user) && !$user->isEmpty()) {
|
||||
$log->setUser(new User($user->getId()));
|
||||
}
|
||||
|
||||
|
|
@ -376,7 +365,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
$log->setMessage($error->getMessage());
|
||||
|
||||
$log->addTag('method', $route->getMethod());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('verboseType', get_class($error));
|
||||
$log->addTag('code', $error->getCode());
|
||||
$log->addTag('projectId', $project->getId());
|
||||
|
|
@ -395,12 +384,12 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
foreach($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
foreach ($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
$log->addBreadcrumb($loggerBreadcrumb);
|
||||
}
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Log pushed with status code: '.$responseCode);
|
||||
Console::info('Log pushed with status code: ' . $responseCode);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -411,35 +400,35 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
$trace = $error->getTrace();
|
||||
|
||||
if (php_sapi_name() === 'cli') {
|
||||
Console::error('[Error] Timestamp: '.date('c', time()));
|
||||
Console::error('[Error] Timestamp: ' . date('c', time()));
|
||||
|
||||
if($route) {
|
||||
Console::error('[Error] Method: '.$route->getMethod());
|
||||
Console::error('[Error] URL: '.$route->getPath());
|
||||
if ($route) {
|
||||
Console::error('[Error] Method: ' . $route->getMethod());
|
||||
Console::error('[Error] URL: ' . $route->getPath());
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: '.get_class($error));
|
||||
Console::error('[Error] Message: '.$message);
|
||||
Console::error('[Error] File: '.$file);
|
||||
Console::error('[Error] Line: '.$line);
|
||||
Console::error('[Error] Type: ' . get_class($error));
|
||||
Console::error('[Error] Message: ' . $message);
|
||||
Console::error('[Error] File: ' . $file);
|
||||
Console::error('[Error] Line: ' . $line);
|
||||
}
|
||||
|
||||
/** Handle Utopia Errors */
|
||||
if ($error instanceof Utopia\Exception) {
|
||||
$error = new Exception($message, $code, Exception::GENERAL_UNKNOWN, $error);
|
||||
switch($code) {
|
||||
$error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error);
|
||||
switch ($code) {
|
||||
case 400:
|
||||
$error->setType(Exception::GENERAL_ARGUMENT_INVALID);
|
||||
$error->setType(AppwriteException::GENERAL_ARGUMENT_INVALID);
|
||||
break;
|
||||
case 404:
|
||||
$error->setType(Exception::GENERAL_ROUTE_NOT_FOUND);
|
||||
$error->setType(AppwriteException::GENERAL_ROUTE_NOT_FOUND);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Wrap all exceptions inside Appwrite\Extend\Exception */
|
||||
if (!($error instanceof Exception)) {
|
||||
$error = new Exception($message, $code, Exception::GENERAL_UNKNOWN, $error);
|
||||
if (!($error instanceof AppwriteException)) {
|
||||
$error = new AppwriteException($message, $code, AppwriteException::GENERAL_UNKNOWN, $error);
|
||||
}
|
||||
|
||||
switch ($code) { // Don't show 500 errors!
|
||||
|
|
@ -501,7 +490,7 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', $project->getAttribute('name').' - Error')
|
||||
->setParam('title', $project->getAttribute('name') . ' - Error')
|
||||
->setParam('description', 'No Description')
|
||||
->setParam('body', $comp)
|
||||
->setParam('version', $version)
|
||||
|
|
@ -511,8 +500,10 @@ App::error(function ($error, $utopia, $request, $response, $layout, $project, $l
|
|||
$response->html($layout->render());
|
||||
}
|
||||
|
||||
$response->dynamic(new Document($output),
|
||||
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR);
|
||||
$response->dynamic(
|
||||
new Document($output),
|
||||
$utopia->isDevelopment() ? Response::MODEL_ERROR_DEV : Response::MODEL_ERROR
|
||||
);
|
||||
}, ['error', 'utopia', 'request', 'response', 'layout', 'project', 'logger', 'loggerBreadcrumbs']);
|
||||
|
||||
App::get('/manifest.json')
|
||||
|
|
@ -520,8 +511,7 @@ App::get('/manifest.json')
|
|||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->json([
|
||||
'name' => APP_NAME,
|
||||
|
|
@ -547,8 +537,8 @@ App::get('/robots.txt')
|
|||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
$template = new View(__DIR__.'/../views/general/robots.phtml');
|
||||
->action(function (Response $response) {
|
||||
$template = new View(__DIR__ . '/../views/general/robots.phtml');
|
||||
$response->text($template->render(false));
|
||||
});
|
||||
|
||||
|
|
@ -557,8 +547,8 @@ App::get('/humans.txt')
|
|||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
$template = new View(__DIR__.'/../views/general/humans.phtml');
|
||||
->action(function (Response $response) {
|
||||
$template = new View(__DIR__ . '/../views/general/humans.phtml');
|
||||
$response->text($template->render(false));
|
||||
});
|
||||
|
||||
|
|
@ -568,7 +558,7 @@ App::get('/.well-known/acme-challenge')
|
|||
->label('docs', false)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($request, $response) {
|
||||
->action(function (Request $request, Response $response) {
|
||||
$uriChunks = \explode('/', $request->getURI());
|
||||
$token = $uriChunks[\count($uriChunks) - 1];
|
||||
|
||||
|
|
@ -581,32 +571,32 @@ App::get('/.well-known/acme-challenge')
|
|||
]);
|
||||
|
||||
if (!$validator->isValid($token) || \count($uriChunks) !== 4) {
|
||||
throw new Exception('Invalid challenge token.', 400);
|
||||
throw new AppwriteException('Invalid challenge token.', 400);
|
||||
}
|
||||
|
||||
$base = \realpath(APP_STORAGE_CERTIFICATES);
|
||||
$absolute = \realpath($base.'/.well-known/acme-challenge/'.$token);
|
||||
$absolute = \realpath($base . '/.well-known/acme-challenge/' . $token);
|
||||
|
||||
if (!$base) {
|
||||
throw new Exception('Storage error', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new AppwriteException('Storage error', 500, AppwriteException::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
if (!$absolute) {
|
||||
throw new Exception('Unknown path', 404);
|
||||
throw new AppwriteException('Unknown path', 404);
|
||||
}
|
||||
|
||||
if (!\substr($absolute, 0, \strlen($base)) === $base) {
|
||||
throw new Exception('Invalid path', 401);
|
||||
throw new AppwriteException('Invalid path', 401);
|
||||
}
|
||||
|
||||
if (!\file_exists($absolute)) {
|
||||
throw new Exception('Unknown path', 404);
|
||||
throw new AppwriteException('Unknown path', 404);
|
||||
}
|
||||
|
||||
$content = @\file_get_contents($absolute);
|
||||
|
||||
if (!$content) {
|
||||
throw new Exception('Failed to get contents', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
throw new AppwriteException('Failed to get contents', 500, AppwriteException::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$response->text($content);
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ global $utopia, $request, $response;
|
|||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Database\Document;
|
||||
use Appwrite\Network\Validator\Host;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Validator\ArrayList;
|
||||
|
|
@ -206,13 +207,12 @@ App::get('/v1/mock/tests/general/download')
|
|||
->label('sdk.response.code', Response::STATUS_CODE_OK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setContentType('text/plain')
|
||||
->addHeader('Content-Disposition', 'attachment; filename="test.txt"')
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)).' GMT') // 45 days cache
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + (60 * 60 * 24 * 45)) . ' GMT') // 45 days cache
|
||||
->addHeader('X-Peak', \memory_get_peak_usage())
|
||||
->send("Download test passed.")
|
||||
;
|
||||
|
|
@ -237,54 +237,52 @@ App::post('/v1/mock/tests/general/upload')
|
|||
->param('file', [], new File(), 'Sample file param', false)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->action(function ($x, $y, $z, $file, $request, $response) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Swoole\Response $response */
|
||||
|
||||
->action(function (string $x, int $y, array $z, array $file, Request $request, Response $response) {
|
||||
|
||||
$file = $request->getFiles('file');
|
||||
|
||||
|
||||
$contentRange = $request->getHeader('content-range');
|
||||
|
||||
$chunkSize = 5*1024*1024; // 5MB
|
||||
$chunkSize = 5 * 1024 * 1024; // 5MB
|
||||
|
||||
if(!empty($contentRange)) {
|
||||
if (!empty($contentRange)) {
|
||||
$start = $request->getContentRangeStart();
|
||||
$end = $request->getContentRangeEnd();
|
||||
$size = $request->getContentRangeSize();
|
||||
$id = $request->getHeader('x-appwrite-id', '');
|
||||
$file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
if(is_null($start) || is_null($end) || is_null($size)) {
|
||||
if (is_null($start) || is_null($end) || is_null($size)) {
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start > $end || $end > $size) {
|
||||
if ($start > $end || $end > $size) {
|
||||
throw new Exception('Invalid content-range header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start === 0 && !empty($id)) {
|
||||
if ($start === 0 && !empty($id)) {
|
||||
throw new Exception('First chunked request cannot have id header', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($start !== 0 && $id !== 'newfileid') {
|
||||
if ($start !== 0 && $id !== 'newfileid') {
|
||||
throw new Exception('All chunked request must have id header (except first)', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($end !== $size && $end-$start+1 !== $chunkSize) {
|
||||
if ($end !== $size && $end - $start + 1 !== $chunkSize) {
|
||||
throw new Exception('Chunk size must be 5MB (except last chunk)', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if ($end !== $size && $file['size'] !== $chunkSize) {
|
||||
throw new Exception('Wrong chunk size', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($file['size'] > $chunkSize) {
|
||||
|
||||
if ($file['size'] > $chunkSize) {
|
||||
throw new Exception('Chunk size must be 5MB or less', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
if($end !== $size) {
|
||||
if ($end !== $size) {
|
||||
$response->json([
|
||||
'$id'=> 'newfileid',
|
||||
'$id' => 'newfileid',
|
||||
'chunksTotal' => $file['size'] / $chunkSize,
|
||||
'chunksUploaded' => $start / $chunkSize
|
||||
]);
|
||||
|
|
@ -293,11 +291,11 @@ App::post('/v1/mock/tests/general/upload')
|
|||
$file['tmp_name'] = (\is_array($file['tmp_name'])) ? $file['tmp_name'][0] : $file['tmp_name'];
|
||||
$file['name'] = (\is_array($file['name'])) ? $file['name'][0] : $file['name'];
|
||||
$file['size'] = (\is_array($file['size'])) ? $file['size'][0] : $file['size'];
|
||||
|
||||
|
||||
if ($file['name'] !== 'file.png') {
|
||||
throw new Exception('Wrong file name', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
|
||||
if ($file['size'] !== 38756) {
|
||||
throw new Exception('Wrong file size', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
|
@ -321,8 +319,7 @@ App::get('/v1/mock/tests/general/redirect')
|
|||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->redirect('/v1/mock/tests/general/redirect/done');
|
||||
});
|
||||
|
|
@ -356,9 +353,7 @@ App::get('/v1/mock/tests/general/set-cookie')
|
|||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->inject('request')
|
||||
->action(function ($response, $request) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
->action(function (Response $response, Request $request) {
|
||||
|
||||
$response->addCookie('cookieName', 'cookieValue', \time() + 31536000, '/', $request->getHostname(), true, true);
|
||||
});
|
||||
|
|
@ -376,8 +371,7 @@ App::get('/v1/mock/tests/general/get-cookie')
|
|||
->label('sdk.response.model', Response::MODEL_MOCK)
|
||||
->label('sdk.mock', true)
|
||||
->inject('request')
|
||||
->action(function ($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
->action(function (Request $request) {
|
||||
|
||||
if ($request->getCookie('cookieName', '') !== 'cookieValue') {
|
||||
throw new Exception('Missing cookie value', 400, Exception::GENERAL_MOCK);
|
||||
|
|
@ -396,8 +390,7 @@ App::get('/v1/mock/tests/general/empty')
|
|||
->label('sdk.response.model', Response::MODEL_NONE)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->noContent();
|
||||
});
|
||||
|
|
@ -447,8 +440,7 @@ App::get('/v1/mock/tests/general/502-error')
|
|||
->label('sdk.response.model', Response::MODEL_ANY)
|
||||
->label('sdk.mock', true)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setStatusCode(502)
|
||||
|
|
@ -467,10 +459,9 @@ App::get('/v1/mock/tests/general/oauth2')
|
|||
->param('scope', '', new Text(100), 'OAuth2 scope list.')
|
||||
->param('state', '', new Text(1024), 'OAuth2 state.')
|
||||
->inject('response')
|
||||
->action(function ($client_id, $redirectURI, $scope, $state, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $client_id, string $redirectURI, string $scope, string $state, Response $response) {
|
||||
|
||||
$response->redirect($redirectURI.'?'.\http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
$response->redirect($redirectURI . '?' . \http_build_query(['code' => 'abcdef', 'state' => $state]));
|
||||
});
|
||||
|
||||
App::get('/v1/mock/tests/general/oauth2/token')
|
||||
|
|
@ -486,8 +477,7 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
|||
->param('code', '', new Text(100), 'OAuth2 state.', true)
|
||||
->param('refresh_token', '', new Text(100), 'OAuth2 refresh token.', true)
|
||||
->inject('response')
|
||||
->action(function ($client_id, $client_secret, $grantType, $redirectURI, $code, $refreshToken, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $client_id, string $client_secret, string $grantType, string $redirectURI, string $code, string $refreshToken, Response $response) {
|
||||
|
||||
if ($client_id != '1') {
|
||||
throw new Exception('Invalid client ID', 400, Exception::GENERAL_MOCK);
|
||||
|
|
@ -503,13 +493,13 @@ App::get('/v1/mock/tests/general/oauth2/token')
|
|||
'expires_in' => 14400
|
||||
];
|
||||
|
||||
if($grantType === 'authorization_code') {
|
||||
if ($grantType === 'authorization_code') {
|
||||
if ($code !== 'abcdef') {
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
||||
$response->json($responseJson);
|
||||
} else if($grantType === 'refresh_token') {
|
||||
} elseif ($grantType === 'refresh_token') {
|
||||
if ($refreshToken !== 'tuvwxyz') {
|
||||
throw new Exception('Invalid refresh token', 400, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
|
@ -527,8 +517,7 @@ App::get('/v1/mock/tests/general/oauth2/user')
|
|||
->label('docs', false)
|
||||
->param('token', '', new Text(100), 'OAuth2 Access Token.')
|
||||
->inject('response')
|
||||
->action(function ($token, $response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (string $token, Response $response) {
|
||||
|
||||
if ($token != '123456') {
|
||||
throw new Exception('Invalid token', 400, Exception::GENERAL_MOCK);
|
||||
|
|
@ -547,8 +536,7 @@ App::get('/v1/mock/tests/general/oauth2/success')
|
|||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response->json([
|
||||
'result' => 'success',
|
||||
|
|
@ -561,8 +549,7 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
|||
->label('scope', 'public')
|
||||
->label('docs', false)
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_BAD_REQUEST)
|
||||
|
|
@ -571,16 +558,13 @@ App::get('/v1/mock/tests/general/oauth2/failure')
|
|||
]);
|
||||
});
|
||||
|
||||
App::shutdown(function($utopia, $response, $request) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
App::shutdown(function (App $utopia, Response $response, Request $request) {
|
||||
|
||||
$result = [];
|
||||
$route = $utopia->match($request);
|
||||
$path = APP_STORAGE_CACHE.'/tests.json';
|
||||
$path = APP_STORAGE_CACHE . '/tests.json';
|
||||
$tests = (\file_exists($path)) ? \json_decode(\file_get_contents($path), true) : [];
|
||||
|
||||
|
||||
if (!\is_array($tests)) {
|
||||
throw new Exception('Failed to read results', 500, Exception::GENERAL_MOCK);
|
||||
}
|
||||
|
|
@ -594,4 +578,4 @@ App::shutdown(function($utopia, $response, $request) {
|
|||
}
|
||||
|
||||
$response->dynamic(new Document(['result' => $route->getMethod() . ':' . $route->getPath() . ':passed']), Response::MODEL_MOCK);
|
||||
}, ['utopia', 'response', 'request'], 'mock');
|
||||
}, ['utopia', 'response', 'request'], 'mock');
|
||||
|
|
|
|||
|
|
@ -1,30 +1,24 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Event\Audit;
|
||||
use Appwrite\Event\Delete;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Mail;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Stats\Stats;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Utopia\App;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Utopia\Abuse\Abuse;
|
||||
use Utopia\Abuse\Adapters\TimeLimit;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Registry\Registry;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $project, $user, $events, $audits, $mails, $usage, $deletes, $database, $dbForProject, $mode) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Utopia\Database\Document $user */
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Event\Mail $mails */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Event $deletes */
|
||||
/** @var Appwrite\Event\Event $database */
|
||||
/** @var Appwrite\Event\Event $functions */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
App::init(function (App $utopia, Request $request, Response $response, Document $project, Document $user, Event $events, Audit $audits, Mail $mails, Stats $usage, Delete $deletes, Event $database, Database $dbForProject, string $mode) {
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
|
|
@ -46,7 +40,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
->setParam('{userId}', $user->getId())
|
||||
->setParam('{userAgent}', $request->getUserAgent(''))
|
||||
->setParam('{ip}', $request->getIP())
|
||||
->setParam('{url}', $request->getHostname().$route->getPath());
|
||||
->setParam('{url}', $request->getHostname() . $route->getPath());
|
||||
$timeLimitArray[] = $timeLimit;
|
||||
}
|
||||
|
||||
|
|
@ -58,8 +52,8 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
|
||||
foreach ($timeLimitArray as $timeLimit) {
|
||||
foreach ($request->getParams() as $key => $value) { // Set request params as potential abuse keys
|
||||
if(!empty($value)) {
|
||||
$timeLimit->setParam('{param-'.$key.'}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
if (!empty($value)) {
|
||||
$timeLimit->setParam('{param-' . $key . '}', (\is_array($value)) ? \json_encode($value) : $value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -74,10 +68,11 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
;
|
||||
}
|
||||
|
||||
if ((App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled' // Route is rate-limited
|
||||
&& $abuse->check()) // Abuse is not disabled
|
||||
&& (!$isAppUser && !$isPrivilegedUser)) // User is not an admin or API key
|
||||
{
|
||||
if (
|
||||
(App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') !== 'disabled' // Route is rate-limited
|
||||
&& $abuse->check()) // Abuse is not disabled
|
||||
&& (!$isAppUser && !$isPrivilegedUser)
|
||||
) { // User is not an admin or API key
|
||||
throw new Exception('Too many requests', 429, Exception::GENERAL_RATE_LIMIT_EXCEEDED);
|
||||
}
|
||||
}
|
||||
|
|
@ -108,7 +103,7 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
$usage
|
||||
->setParam('projectId', $project->getId())
|
||||
->setParam('httpRequest', 1)
|
||||
->setParam('httpUrl', $request->getHostname().$request->getURI())
|
||||
->setParam('httpUrl', $request->getHostname() . $request->getURI())
|
||||
->setParam('httpMethod', $request->getMethod())
|
||||
->setParam('httpPath', $route->getPath())
|
||||
->setParam('networkRequestSize', 0)
|
||||
|
|
@ -120,48 +115,45 @@ App::init(function ($utopia, $request, $response, $project, $user, $events, $aud
|
|||
$database->setProject($project);
|
||||
}, ['utopia', 'request', 'response', 'project', 'user', 'events', 'audits', 'mails', 'usage', 'deletes', 'database', 'dbForProject', 'mode'], 'api');
|
||||
|
||||
App::init(function ($utopia, $request, $project) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
App::init(function (App $utopia, Request $request, Document $project) {
|
||||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$isPrivilegedUser = Auth::isPrivilegedUser(Authorization::getRoles());
|
||||
$isAppUser = Auth::isAppUser(Authorization::getRoles());
|
||||
|
||||
if($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
if ($isAppUser || $isPrivilegedUser) { // Skip limits for app and console devs
|
||||
return;
|
||||
}
|
||||
|
||||
$auths = $project->getAttribute('auths', []);
|
||||
switch ($route->getLabel('auth.type', '')) {
|
||||
case 'emailPassword':
|
||||
if(($auths['emailPassword'] ?? true) === false) {
|
||||
if (($auths['emailPassword'] ?? true) === false) {
|
||||
throw new Exception('Email / Password authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'magic-url':
|
||||
if($project->getAttribute('usersAuthMagicURL', true) === false) {
|
||||
if ($project->getAttribute('usersAuthMagicURL', true) === false) {
|
||||
throw new Exception('Magic URL authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'anonymous':
|
||||
if(($auths['anonymous'] ?? true) === false) {
|
||||
if (($auths['anonymous'] ?? true) === false) {
|
||||
throw new Exception('Anonymous authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'invites':
|
||||
if(($auths['invites'] ?? true) === false) {
|
||||
if (($auths['invites'] ?? true) === false) {
|
||||
throw new Exception('Invites authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'jwt':
|
||||
if(($auths['JWT'] ?? true) === false) {
|
||||
if (($auths['JWT'] ?? true) === false) {
|
||||
throw new Exception('JWT authentication is disabled for this project', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
}
|
||||
break;
|
||||
|
|
@ -170,21 +162,9 @@ App::init(function ($utopia, $request, $project) {
|
|||
throw new Exception('Unsupported authentication route', 501, Exception::USER_AUTH_METHOD_UNSUPPORTED);
|
||||
break;
|
||||
}
|
||||
|
||||
}, ['utopia', 'request', 'project'], 'auth');
|
||||
|
||||
App::shutdown(function ($utopia, $request, $response, $project, $events, $audits, $usage, $deletes, $database, $mode, $dbForProject) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
/** @var Appwrite\Event\Event $events */
|
||||
/** @var Appwrite\Event\Audit $audits */
|
||||
/** @var Appwrite\Stats\Stats $usage */
|
||||
/** @var Appwrite\Event\Delete $deletes */
|
||||
/** @var Appwrite\Event\Database $database */
|
||||
/** @var bool $mode */
|
||||
/** @var Utopia\Database\Database $dbForProject */
|
||||
App::shutdown(function (App $utopia, Request $request, Response $response, Document $project, Event $events, Audit $audits, Stats $usage, Delete $deletes, Event $database, string $mode, Database $dbForProject) {
|
||||
|
||||
if (!empty($events->getEvent())) {
|
||||
if (empty($events->getPayload())) {
|
||||
|
|
@ -233,7 +213,7 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
|||
channels: $target['channels'],
|
||||
roles: $target['roles'],
|
||||
options: [
|
||||
'permissionsChanged' => $target['permissionsChanged'],
|
||||
'permissionsChanged' => $target['permissionsChanged'],
|
||||
'userId' => $events->getParam('userId')
|
||||
]
|
||||
);
|
||||
|
|
@ -256,15 +236,15 @@ App::shutdown(function ($utopia, $request, $response, $project, $events, $audits
|
|||
}
|
||||
|
||||
$route = $utopia->match($request);
|
||||
if (App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
|
||||
if (
|
||||
App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled'
|
||||
&& $project->getId()
|
||||
&& $mode !== APP_MODE_ADMIN // TODO: add check to make sure user is admin
|
||||
&& !empty($route->getLabel('sdk.namespace', null))) { // Don't calculate console usage on admin mode
|
||||
|
||||
&& !empty($route->getLabel('sdk.namespace', null))
|
||||
) { // Don't calculate console usage on admin mode
|
||||
$usage
|
||||
->setParam('networkRequestSize', $request->getSize() + $usage->getParam('storage'))
|
||||
->setParam('networkResponseSize', $response->getSize())
|
||||
->submit();
|
||||
}
|
||||
|
||||
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject'], 'api');
|
||||
}, ['utopia', 'request', 'response', 'project', 'events', 'audits', 'usage', 'deletes', 'database', 'mode', 'dbForProject'], 'api');
|
||||
|
|
|
|||
|
|
@ -2,12 +2,11 @@
|
|||
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\View;
|
||||
|
||||
App::init(function ($utopia, $request, $response, $layout) {
|
||||
/** @var Utopia\App $utopia */
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
App::init(function (App $utopia, Request $request, Response $response, View $layout) {
|
||||
|
||||
/* AJAX check */
|
||||
if (!empty($request->getQuery('version', ''))) {
|
||||
|
|
@ -48,10 +47,10 @@ App::init(function ($utopia, $request, $response, $layout) {
|
|||
|
||||
$route = $utopia->match($request);
|
||||
|
||||
$route->label('error', __DIR__.'/../../views/general/error.phtml');
|
||||
$route->label('error', __DIR__ . '/../../views/general/error.phtml');
|
||||
|
||||
$scope = $route->getLabel('scope', '');
|
||||
|
||||
|
||||
$layout
|
||||
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
|
||||
->setParam('isDev', App::isDevelopment())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Appwrite\Utopia\View;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
|
|
@ -8,8 +9,7 @@ use Utopia\Domains\Domain;
|
|||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Storage\Storage;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
App::init(function (View $layout) {
|
||||
|
||||
$layout
|
||||
->setParam('description', 'Appwrite Console allows you to easily manage, monitor, and control your entire backend API and tools.')
|
||||
|
|
@ -17,12 +17,10 @@ App::init(function ($layout) {
|
|||
;
|
||||
}, ['layout'], 'console');
|
||||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
App::shutdown(function (Response $response, View $layout) {
|
||||
|
||||
$header = new View(__DIR__.'/../../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/console/comps/footer.phtml');
|
||||
$header = new View(__DIR__ . '/../../views/console/comps/header.phtml');
|
||||
$footer = new View(__DIR__ . '/../../views/console/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('home', App::getEnv('_APP_HOME', ''))
|
||||
|
|
@ -43,17 +41,16 @@ App::get('/error/:code')
|
|||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (int $code, View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Error')
|
||||
->setParam('title', APP_NAME . ' - Error')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -62,17 +59,16 @@ App::get('/console')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', ''))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Console')
|
||||
->setParam('title', APP_NAME . ' - Console')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -81,19 +77,18 @@ App::get('/console/account')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/account/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/account/index.phtml');
|
||||
|
||||
$cc = new View(__DIR__.'/../../views/console/forms/credit-card.phtml');
|
||||
$cc = new View(__DIR__ . '/../../views/console/forms/credit-card.phtml');
|
||||
|
||||
$page
|
||||
->setParam('cc', $cc)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Account - '.APP_NAME)
|
||||
->setParam('title', 'Account - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -102,13 +97,12 @@ App::get('/console/notifications')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/v1/console/notifications/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/v1/console/notifications/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Notifications')
|
||||
->setParam('title', APP_NAME . ' - Notifications')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -117,14 +111,13 @@ App::get('/console/home')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/home/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/home/index.phtml');
|
||||
$page
|
||||
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
|
||||
->setParam('usageStatsEnabled', App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled');
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Console')
|
||||
->setParam('title', APP_NAME . ' - Console')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -133,22 +126,20 @@ App::get('/console/settings')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$target = new Domain(App::getEnv('_APP_DOMAIN_TARGET', ''));
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/settings/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/settings/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('services', array_filter(Config::getParam('services'), function($element) {return $element['optional'];}))
|
||||
$page->setParam('services', array_filter(Config::getParam('services'), fn($element) => $element['optional']))
|
||||
->setParam('customDomainsEnabled', ($target->isKnown() && !$target->isTest()))
|
||||
->setParam('customDomainsTarget', $target->get())
|
||||
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Settings')
|
||||
->setParam('title', APP_NAME . ' - Settings')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -157,15 +148,14 @@ App::get('/console/webhooks')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/webhooks/index.phtml');
|
||||
|
||||
$page->setParam('events', Config::getParam('events', []));
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('title', APP_NAME . ' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -175,10 +165,9 @@ App::get('/console/webhooks/webhook')
|
|||
->label('scope', 'console')
|
||||
->param('id', '', new UID(), 'Webhook unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($id, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (string $id, View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/webhook.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/webhooks/webhook.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
|
|
@ -186,7 +175,7 @@ App::get('/console/webhooks/webhook')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('title', APP_NAME . ' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -195,10 +184,9 @@ App::get('/console/webhooks/webhook/new')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/webhooks/webhook.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/webhooks/webhook.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
|
|
@ -206,7 +194,7 @@ App::get('/console/webhooks/webhook/new')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Webhooks')
|
||||
->setParam('title', APP_NAME . ' - Webhooks')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -215,16 +203,15 @@ App::get('/console/keys')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$scopes = array_keys(Config::getParam('scopes'));
|
||||
$page = new View(__DIR__.'/../../views/console/keys/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/keys/index.phtml');
|
||||
|
||||
$page->setParam('scopes', $scopes);
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - API Keys')
|
||||
->setParam('title', APP_NAME . ' - API Keys')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -233,13 +220,12 @@ App::get('/console/database')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/database/index.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database')
|
||||
->setParam('title', APP_NAME . ' - Database')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -250,11 +236,9 @@ App::get('/console/database/collection')
|
|||
->param('id', '', new UID(), 'Collection unique ID.')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (string $id, Response $response, View $layout) {
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
$logs = new View(__DIR__ . '/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
|
|
@ -264,12 +248,12 @@ App::get('/console/database/collection')
|
|||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/collection.phtml');
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/database/collection.phtml');
|
||||
|
||||
$page->setParam('logs', $logs);
|
||||
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Collection')
|
||||
->setParam('title', APP_NAME . ' - Database Collection')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
|
|
@ -286,10 +270,9 @@ App::get('/console/database/document')
|
|||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (string $collection, View $layout) {
|
||||
|
||||
$logs = new View(__DIR__.'/../../views/console/comps/logs.phtml');
|
||||
$logs = new View(__DIR__ . '/../../views/console/comps/logs.phtml');
|
||||
|
||||
$logs
|
||||
->setParam('interval', App::getEnv('_APP_MAINTENANCE_RETENTION_AUDIT', 0))
|
||||
|
|
@ -300,7 +283,7 @@ App::get('/console/database/document')
|
|||
])
|
||||
;
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', false)
|
||||
|
|
@ -309,7 +292,7 @@ App::get('/console/database/document')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('title', APP_NAME . ' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -319,10 +302,9 @@ App::get('/console/database/document/new')
|
|||
->label('scope', 'console')
|
||||
->param('collection', '', new UID(), 'Collection unique ID.')
|
||||
->inject('layout')
|
||||
->action(function ($collection, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (string $collection, View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/database/document.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/database/document.phtml');
|
||||
|
||||
$page
|
||||
->setParam('new', true)
|
||||
|
|
@ -331,7 +313,7 @@ App::get('/console/database/document/new')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Database Document')
|
||||
->setParam('title', APP_NAME . ' - Database Document')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -340,10 +322,10 @@ App::get('/console/storage')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/console/storage/index.phtml');
|
||||
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/console/storage/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
|
|
@ -351,7 +333,7 @@ App::get('/console/storage')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage')
|
||||
->setParam('title', APP_NAME . ' - Storage')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -362,19 +344,17 @@ App::get('/console/storage/bucket')
|
|||
->param('id', '', new UID(), 'Bucket unique ID.')
|
||||
->inject('response')
|
||||
->inject('layout')
|
||||
->action(function ($id, $response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\View $layout */
|
||||
->action(function (string $id, Response $response, View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/storage/bucket.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/storage/bucket.phtml');
|
||||
$page
|
||||
->setParam('home', App::getEnv('_APP_HOME', 0))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
;
|
||||
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Storage Buckets')
|
||||
->setParam('title', APP_NAME . ' - Storage Buckets')
|
||||
->setParam('body', $page)
|
||||
;
|
||||
|
||||
|
|
@ -390,10 +370,9 @@ App::get('/console/users')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/index.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/users/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('auth', Config::getParam('auth'))
|
||||
|
|
@ -402,7 +381,7 @@ App::get('/console/users')
|
|||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Users')
|
||||
->setParam('title', APP_NAME . ' - Users')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -411,13 +390,12 @@ App::get('/console/users/user')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/user.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/users/user.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - User')
|
||||
->setParam('title', APP_NAME . ' - User')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -426,13 +404,12 @@ App::get('/console/users/teams/team')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/console/users/team.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/console/users/team.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Team')
|
||||
->setParam('title', APP_NAME . ' - Team')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -442,15 +419,15 @@ App::get('/console/functions')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
$page = new View(__DIR__.'/../../views/console/functions/index.phtml');
|
||||
->action(function (View $layout) {
|
||||
$page = new View(__DIR__ . '/../../views/console/functions/index.phtml');
|
||||
|
||||
$page
|
||||
->setParam('runtimes', Config::getParam('runtimes'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Functions')
|
||||
->setParam('title', APP_NAME . ' - Functions')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -460,19 +437,19 @@ App::get('/console/functions/function')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'console')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
$page = new View(__DIR__.'/../../views/console/functions/function.phtml');
|
||||
->action(function (View $layout) {
|
||||
$page = new View(__DIR__ . '/../../views/console/functions/function.phtml');
|
||||
|
||||
$page
|
||||
->setParam('events', Config::getParam('events', []))
|
||||
->setParam('fileLimit', App::getEnv('_APP_STORAGE_LIMIT', 0))
|
||||
->setParam('fileLimitHuman', Storage::human(App::getEnv('_APP_STORAGE_LIMIT', 0)))
|
||||
->setParam('timeout', (int) App::getEnv('_APP_FUNCTIONS_TIMEOUT', 900))
|
||||
->setParam('usageStatsEnabled',App::getEnv('_APP_USAGE_STATS','enabled') == 'enabled');
|
||||
->setParam('usageStatsEnabled', App::getEnv('_APP_USAGE_STATS', 'enabled') == 'enabled');
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME.' - Function')
|
||||
->setParam('title', APP_NAME . ' - Function')
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -484,8 +461,8 @@ App::get('/console/version')
|
|||
->inject('response')
|
||||
->action(function ($response) {
|
||||
try {
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
|
||||
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/v1/health/version'), true);
|
||||
|
||||
if ($version && isset($version['version'])) {
|
||||
return $response->json(['version' => $version['version']]);
|
||||
} else {
|
||||
|
|
@ -494,4 +471,4 @@ App::get('/console/version')
|
|||
} catch (\Throwable $th) {
|
||||
throw new Exception('Failed to check for a newer version', 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,14 +1,16 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Utopia\View;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\App;
|
||||
use Utopia\Config\Config;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
|
||||
App::init(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
App::init(function (View $layout) {
|
||||
|
||||
$header = new View(__DIR__.'/../../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__.'/../../views/home/comps/footer.phtml');
|
||||
$header = new View(__DIR__ . '/../../views/home/comps/header.phtml');
|
||||
$footer = new View(__DIR__ . '/../../views/home/comps/footer.phtml');
|
||||
|
||||
$footer
|
||||
->setParam('version', App::getEnv('_APP_VERSION', 'UNKNOWN'))
|
||||
|
|
@ -24,9 +26,7 @@ App::init(function ($layout) {
|
|||
;
|
||||
}, ['layout'], 'home');
|
||||
|
||||
App::shutdown(function ($response, $layout) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
App::shutdown(function (Response $response, View $layout) {
|
||||
|
||||
$response->html($layout->render());
|
||||
}, ['response', 'layout'], 'home');
|
||||
|
|
@ -38,10 +38,7 @@ App::get('/')
|
|||
->inject('response')
|
||||
->inject('dbForConsole')
|
||||
->inject('project')
|
||||
->action(function ($response, $dbForConsole, $project) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
->action(function (Response $response, Database $dbForConsole, Document $project) {
|
||||
|
||||
$response
|
||||
->addHeader('Cache-Control', 'no-store, no-cache, must-revalidate, max-age=0')
|
||||
|
|
@ -52,10 +49,10 @@ App::get('/')
|
|||
if ('console' === $project->getId() || $project->isEmpty()) {
|
||||
$whitelistRoot = App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled');
|
||||
|
||||
if($whitelistRoot !== 'disabled') {
|
||||
if ($whitelistRoot !== 'disabled') {
|
||||
$count = $dbForConsole->count('users', [], 1);
|
||||
|
||||
if($count !== 0) {
|
||||
if ($count !== 0) {
|
||||
return $response->redirect('/auth/signin');
|
||||
}
|
||||
}
|
||||
|
|
@ -69,17 +66,16 @@ App::get('/auth/signin')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signin.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/signin.phtml');
|
||||
|
||||
$page
|
||||
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign In - '.APP_NAME)
|
||||
->setParam('title', 'Sign In - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -88,16 +84,16 @@ App::get('/auth/signup')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
$page = new View(__DIR__.'/../../views/home/auth/signup.phtml');
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/signup.phtml');
|
||||
|
||||
$page
|
||||
->setParam('root', App::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled'))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Sign Up - '.APP_NAME)
|
||||
->setParam('title', 'Sign Up - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -106,17 +102,16 @@ App::get('/auth/recovery')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/recovery.phtml');
|
||||
|
||||
$page
|
||||
->setParam('smtpEnabled', (!empty(App::getEnv('_APP_SMTP_HOST'))))
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Recovery - '.APP_NAME)
|
||||
->setParam('title', 'Password Recovery - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -125,13 +120,12 @@ App::get('/auth/confirm')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/confirm.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/confirm.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Account Confirmation - '.APP_NAME)
|
||||
->setParam('title', 'Account Confirmation - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -140,13 +134,12 @@ App::get('/auth/join')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/join.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/join.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Invitation - '.APP_NAME)
|
||||
->setParam('title', 'Invitation - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -155,13 +148,12 @@ App::get('/auth/recovery/reset')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/recovery/reset.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/recovery/reset.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Password Reset - '.APP_NAME)
|
||||
->setParam('title', 'Password Reset - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -170,10 +162,9 @@ App::get('/auth/oauth2/success')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
|
|
@ -188,10 +179,9 @@ App::get('/auth/magic-url')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/magicURL.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/magicURL.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
|
|
@ -206,10 +196,9 @@ App::get('/auth/oauth2/failure')
|
|||
->label('permission', 'public')
|
||||
->label('scope', 'home')
|
||||
->inject('layout')
|
||||
->action(function ($layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/home/auth/oauth2.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/home/auth/oauth2.phtml');
|
||||
|
||||
$layout
|
||||
->setParam('title', APP_NAME)
|
||||
|
|
@ -225,17 +214,16 @@ App::get('/error/:code')
|
|||
->label('scope', 'home')
|
||||
->param('code', null, new \Utopia\Validator\Numeric(), 'Valid status code number', false)
|
||||
->inject('layout')
|
||||
->action(function ($code, $layout) {
|
||||
/** @var Appwrite\Utopia\View $layout */
|
||||
->action(function (int $code, View $layout) {
|
||||
|
||||
$page = new View(__DIR__.'/../../views/error.phtml');
|
||||
$page = new View(__DIR__ . '/../../views/error.phtml');
|
||||
|
||||
$page
|
||||
->setParam('code', $code)
|
||||
;
|
||||
|
||||
$layout
|
||||
->setParam('title', 'Error'.' - '.APP_NAME)
|
||||
->setParam('title', 'Error' . ' - ' . APP_NAME)
|
||||
->setParam('body', $page);
|
||||
});
|
||||
|
||||
|
|
@ -244,8 +232,7 @@ App::get('/versions')
|
|||
->groups(['web', 'home'])
|
||||
->label('scope', 'public')
|
||||
->inject('response')
|
||||
->action(function ($response) {
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
->action(function (Response $response) {
|
||||
|
||||
$platforms = Config::getParam('platforms');
|
||||
|
||||
|
|
@ -253,15 +240,15 @@ App::get('/versions')
|
|||
'server' => APP_VERSION_STABLE,
|
||||
];
|
||||
|
||||
foreach($platforms as $platform) {
|
||||
foreach ($platforms as $platform) {
|
||||
$languages = $platform['languages'] ?? [];
|
||||
|
||||
foreach ($languages as $key => $language) {
|
||||
if(isset($language['dev']) && $language['dev']) {
|
||||
if (isset($language['dev']) && $language['dev']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if(isset($language['enabled']) && !$language['enabled']) {
|
||||
if (isset($language['enabled']) && !$language['enabled']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Appwrite\Runtimes\Runtimes;
|
||||
|
|
@ -38,7 +39,7 @@ Runtime::enableCoroutine(true, SWOOLE_HOOK_ALL);
|
|||
const MAINTENANCE_INTERVAL = 3600; // 3600 seconds = 1 hour
|
||||
|
||||
/**
|
||||
* Create a Swoole table to store runtime information
|
||||
* Create a Swoole table to store runtime information
|
||||
*/
|
||||
$activeRuntimes = new Swoole\Table(1024);
|
||||
$activeRuntimes->column('id', Swoole\Table::TYPE_STRING, 256);
|
||||
|
|
@ -67,8 +68,8 @@ $providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
|||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
$logger = null;
|
||||
|
||||
if(!empty($providerName) && !empty($providerConfig) && Logger::hasProvider($providerName)) {
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
|
||||
if (!empty($providerName) && !empty($providerConfig) && Logger::hasProvider($providerName)) {
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
|
||||
$adapter = new $classname($providerConfig);
|
||||
$logger = new Logger($adapter);
|
||||
}
|
||||
|
|
@ -89,7 +90,7 @@ function logError(Throwable $error, string $action, Utopia\Route $route = null)
|
|||
|
||||
if ($route) {
|
||||
$log->addTag('method', $route->getMethod());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('url', $route->getPath());
|
||||
}
|
||||
|
||||
$log->addTag('code', $error->getCode());
|
||||
|
|
@ -113,11 +114,13 @@ function logError(Throwable $error, string $action, Utopia\Route $route = null)
|
|||
Console::error('[Error] Message: ' . $error->getMessage());
|
||||
Console::error('[Error] File: ' . $error->getFile());
|
||||
Console::error('[Error] Line: ' . $error->getLine());
|
||||
};
|
||||
}
|
||||
|
||||
function getStorageDevice($root): Device {
|
||||
function getStorageDevice($root): Device
|
||||
{
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
|
|
@ -187,9 +190,9 @@ App::post('/v1/runtimes')
|
|||
|
||||
try {
|
||||
Console::info('Building container : ' . $runtimeId);
|
||||
|
||||
/**
|
||||
* Temporary file paths in the executor
|
||||
|
||||
/**
|
||||
* Temporary file paths in the executor
|
||||
*/
|
||||
$tmpSource = "/tmp/$runtimeId/src/code.tar.gz";
|
||||
$tmpBuild = "/tmp/$runtimeId/builds/code.tar.gz";
|
||||
|
|
@ -200,7 +203,7 @@ App::post('/v1/runtimes')
|
|||
$sourceDevice = getStorageDevice("/");
|
||||
$localDevice = new Local();
|
||||
$buffer = $sourceDevice->read($source);
|
||||
if(!$localDevice->write($tmpSource, $buffer)) {
|
||||
if (!$localDevice->write($tmpSource, $buffer)) {
|
||||
throw new Exception('Failed to copy source code to temporary directory', 500);
|
||||
};
|
||||
|
||||
|
|
@ -226,7 +229,7 @@ App::post('/v1/runtimes')
|
|||
->setCpus((int) App::getEnv('_APP_FUNCTIONS_CPUS', 0))
|
||||
->setMemory((int) App::getEnv('_APP_FUNCTIONS_MEMORY', 0))
|
||||
->setSwap((int) App::getEnv('_APP_FUNCTIONS_MEMORY_SWAP', 0));
|
||||
|
||||
|
||||
/** Keep the container alive if we have commands to be executed */
|
||||
$entrypoint = !empty($commands) ? [
|
||||
'tail',
|
||||
|
|
@ -248,8 +251,8 @@ App::post('/v1/runtimes')
|
|||
],
|
||||
workdir: $workdir,
|
||||
volumes: [
|
||||
\dirname($tmpSource). ':/tmp:rw',
|
||||
\dirname($tmpBuild). ':/usr/code:rw'
|
||||
\dirname($tmpSource) . ':/tmp:rw',
|
||||
\dirname($tmpBuild) . ':/usr/code:rw'
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -259,7 +262,7 @@ App::post('/v1/runtimes')
|
|||
|
||||
$orchestration->networkConnect($runtimeId, App::getEnv('OPEN_RUNTIMES_NETWORK', 'appwrite_runtimes'));
|
||||
|
||||
/**
|
||||
/**
|
||||
* Execute any commands if they were provided
|
||||
*/
|
||||
if (!empty($commands)) {
|
||||
|
|
@ -289,7 +292,7 @@ App::post('/v1/runtimes')
|
|||
$outputPath = $destinationDevice->getPath(\uniqid() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
|
||||
|
||||
$buffer = $localDevice->read($tmpBuild);
|
||||
if(!$destinationDevice->write($outputPath, $buffer, $localDevice->getFileMimeType($tmpBuild))) {
|
||||
if (!$destinationDevice->write($outputPath, $buffer, $localDevice->getFileMimeType($tmpBuild))) {
|
||||
throw new Exception('Failed to move built code to storage', 500);
|
||||
};
|
||||
|
||||
|
|
@ -322,13 +325,12 @@ App::post('/v1/runtimes')
|
|||
}
|
||||
|
||||
Console::success('Build Stage completed in ' . ($endTime - $startTime) . ' seconds');
|
||||
|
||||
} catch (Throwable $th) {
|
||||
Console::error('Build failed: ' . $th->getMessage() . $stdout);
|
||||
throw new Exception($th->getMessage() . $stdout, 500);
|
||||
} finally {
|
||||
// Container cleanup
|
||||
if($remove) {
|
||||
if ($remove) {
|
||||
if (!empty($containerId)) {
|
||||
// If container properly created
|
||||
$orchestration->remove($containerId, true);
|
||||
|
|
@ -361,7 +363,7 @@ App::get('/v1/runtimes')
|
|||
->action(function ($activeRuntimes, Response $response) {
|
||||
$runtimes = [];
|
||||
|
||||
foreach($activeRuntimes as $runtime) {
|
||||
foreach ($activeRuntimes as $runtime) {
|
||||
$runtimes[] = $runtime;
|
||||
}
|
||||
|
||||
|
|
@ -377,7 +379,7 @@ App::get('/v1/runtimes/:runtimeId')
|
|||
->inject('response')
|
||||
->action(function ($runtimeId, $activeRuntimes, Response $response) {
|
||||
|
||||
if(!$activeRuntimes->exists($runtimeId)) {
|
||||
if (!$activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -396,7 +398,7 @@ App::delete('/v1/runtimes/:runtimeId')
|
|||
->inject('response')
|
||||
->action(function (string $runtimeId, $orchestrationPool, $activeRuntimes, Response $response) {
|
||||
|
||||
if(!$activeRuntimes->exists($runtimeId)) {
|
||||
if (!$activeRuntimes->exists($runtimeId)) {
|
||||
throw new Exception('Runtime not found', 404);
|
||||
}
|
||||
|
||||
|
|
@ -473,33 +475,33 @@ App::post('/v1/execution')
|
|||
\curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
|
||||
\curl_setopt($ch, CURLOPT_TIMEOUT, $timeout);
|
||||
\curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10);
|
||||
|
||||
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, [
|
||||
'Content-Type: application/json',
|
||||
'Content-Length: ' . \strlen($body),
|
||||
'x-internal-challenge: ' . $secret,
|
||||
'host: null'
|
||||
]);
|
||||
|
||||
|
||||
$executorResponse = \curl_exec($ch);
|
||||
|
||||
|
||||
$statusCode = \curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||||
|
||||
|
||||
$error = \curl_error($ch);
|
||||
|
||||
|
||||
$errNo = \curl_errno($ch);
|
||||
|
||||
|
||||
\curl_close($ch);
|
||||
|
||||
switch (true) {
|
||||
/** No Error. */
|
||||
case $errNo === 0:
|
||||
case $errNo === 0:
|
||||
break;
|
||||
/** Runtime not ready for requests yet. 111 is the swoole error code for Connection Refused - see https://openswoole.com/docs/swoole-error-code */
|
||||
case $errNo === 111:
|
||||
throw new Exception('An internal curl error has occurred within the executor! Error Msg: ' . $error, 406);
|
||||
/** Any other CURL error */
|
||||
default:
|
||||
default:
|
||||
throw new Exception('An internal curl error has occurred within the executor! Error Msg: ' . $error, 500);
|
||||
}
|
||||
|
||||
|
|
@ -551,7 +553,7 @@ App::setResource('activeRuntimes', fn() => $activeRuntimes);
|
|||
App::error(function ($utopia, $error, $request, $response) {
|
||||
$route = $utopia->match($request);
|
||||
logError($error, "httpError", $route);
|
||||
|
||||
|
||||
switch ($error->getCode()) {
|
||||
case 400: // Error allowed publicly
|
||||
case 401: // Error allowed publicly
|
||||
|
|
@ -570,7 +572,7 @@ App::error(function ($utopia, $error, $request, $response) {
|
|||
default:
|
||||
$code = 500; // All other errors get the generic 500 server error status code
|
||||
}
|
||||
|
||||
|
||||
$output = [
|
||||
'message' => $error->getMessage(),
|
||||
'code' => $error->getCode(),
|
||||
|
|
@ -591,13 +593,13 @@ App::error(function ($utopia, $error, $request, $response) {
|
|||
|
||||
App::init(function ($request, $response) {
|
||||
$secretKey = $request->getHeader('x-appwrite-executor-key', '');
|
||||
if (empty($secretKey)) {
|
||||
throw new Exception('Missing executor key', 401);
|
||||
}
|
||||
|
||||
if ($secretKey !== App::getEnv('_APP_EXECUTOR_SECRET', '')) {
|
||||
if (empty($secretKey)) {
|
||||
throw new Exception('Missing executor key', 401);
|
||||
}
|
||||
}
|
||||
|
||||
if ($secretKey !== App::getEnv('_APP_EXECUTOR_SECRET', '')) {
|
||||
throw new Exception('Missing executor key', 401);
|
||||
}
|
||||
}, ['request', 'response']);
|
||||
|
||||
|
||||
|
|
@ -605,7 +607,7 @@ $http->on('start', function ($http) {
|
|||
global $orchestrationPool;
|
||||
global $activeRuntimes;
|
||||
|
||||
/**
|
||||
/**
|
||||
* Warmup: make sure images are ready to run fast 🚀
|
||||
*/
|
||||
$runtimes = new Runtimes('v1');
|
||||
|
|
@ -696,11 +698,10 @@ $http->on('start', function ($http) {
|
|||
}
|
||||
}
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
|
||||
$http->on('beforeShutdown', function() {
|
||||
$http->on('beforeShutdown', function () {
|
||||
global $orchestrationPool;
|
||||
Console::info('Cleaning up containers before shutdown...');
|
||||
|
||||
|
|
@ -709,7 +710,7 @@ $http->on('beforeShutdown', function() {
|
|||
$orchestrationPool->put($orchestration);
|
||||
|
||||
foreach ($functionsToRemove as $container) {
|
||||
go(function () use ($orchestrationPool, $container) {
|
||||
go(function () use ($orchestrationPool, $container) {
|
||||
try {
|
||||
$orchestration = $orchestrationPool->get();
|
||||
$orchestration->remove($container->getId(), true);
|
||||
|
|
@ -735,7 +736,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
logError($th, "serverError");
|
||||
$swooleResponse->setStatusCode(500);
|
||||
$output = [
|
||||
'message' => 'Error: '. $th->getMessage(),
|
||||
'message' => 'Error: ' . $th->getMessage(),
|
||||
'code' => 500,
|
||||
'file' => $th->getFile(),
|
||||
'line' => $th->getLine(),
|
||||
|
|
|
|||
71
app/http.php
71
app/http.php
|
|
@ -1,6 +1,6 @@
|
|||
<?php
|
||||
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Process;
|
||||
|
|
@ -38,15 +38,15 @@ $http
|
|||
])
|
||||
;
|
||||
|
||||
$http->on('WorkerStart', function($server, $workerId) {
|
||||
Console::success('Worker '.++$workerId.' started successfully');
|
||||
$http->on('WorkerStart', function ($server, $workerId) {
|
||||
Console::success('Worker ' . ++$workerId . ' started successfully');
|
||||
});
|
||||
|
||||
$http->on('BeforeReload', function($server, $workerId) {
|
||||
$http->on('BeforeReload', function ($server, $workerId) {
|
||||
Console::success('Starting reload...');
|
||||
});
|
||||
|
||||
$http->on('AfterReload', function($server, $workerId) {
|
||||
$http->on('AfterReload', function ($server, $workerId) {
|
||||
Console::success('Reload completed...');
|
||||
});
|
||||
|
||||
|
|
@ -57,7 +57,7 @@ include __DIR__ . '/controllers/general.php';
|
|||
$http->on('start', function (Server $http) use ($payloadSize, $register) {
|
||||
$app = new App('UTC');
|
||||
|
||||
go(function() use ($register, $app) {
|
||||
go(function () use ($register, $app) {
|
||||
// wait for database to be ready
|
||||
$attempts = 0;
|
||||
$max = 10;
|
||||
|
|
@ -69,10 +69,10 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
$db = $register->get('dbPool')->get();
|
||||
$redis = $register->get('redisPool')->get();
|
||||
break; // leave the do-while if successful
|
||||
} catch(\Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Database not ready. Retrying connection ({$attempts})...");
|
||||
if ($attempts >= $max) {
|
||||
throw new \Exception('Failed to connect to database: '. $e->getMessage());
|
||||
throw new \Exception('Failed to connect to database: ' . $e->getMessage());
|
||||
}
|
||||
sleep($sleep);
|
||||
}
|
||||
|
|
@ -86,7 +86,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
Console::success('[Setup] - Server database init started...');
|
||||
$collections = Config::getParam('collections', []); /** @var array $collections */
|
||||
|
||||
if(!$dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'))) {
|
||||
if (!$dbForConsole->exists(App::getEnv('_APP_DB_SCHEMA', 'appwrite'))) {
|
||||
$redis->flushAll();
|
||||
|
||||
Console::success('[Setup] - Creating database: appwrite...');
|
||||
|
|
@ -101,7 +101,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
Console::success('[Setup] - Skip: metadata table already exists');
|
||||
}
|
||||
|
||||
if($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
if ($dbForConsole->getCollection(Audit::COLLECTION)->isEmpty()) {
|
||||
$audit = new Audit($dbForConsole);
|
||||
$audit->setup();
|
||||
}
|
||||
|
|
@ -112,10 +112,10 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
}
|
||||
|
||||
foreach ($collections as $key => $collection) {
|
||||
if(($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
if (($collection['$collection'] ?? '') !== Database::METADATA) {
|
||||
continue;
|
||||
}
|
||||
if(!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
if (!$dbForConsole->getCollection($key)->isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
Console::success('[Setup] - Creating collection: ' . $collection['$id'] . '...');
|
||||
|
|
@ -150,7 +150,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
$dbForConsole->createCollection($key, $attributes, $indexes);
|
||||
}
|
||||
|
||||
if($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
if ($dbForConsole->getDocument('buckets', 'default')->isEmpty()) {
|
||||
Console::success('[Setup] - Creating default bucket...');
|
||||
$dbForConsole->createDocument('buckets', new Document([
|
||||
'$id' => 'default',
|
||||
|
|
@ -170,16 +170,16 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
]));
|
||||
|
||||
$bucket = $dbForConsole->getDocument('buckets', 'default');
|
||||
|
||||
|
||||
Console::success('[Setup] - Creating files collection for default bucket...');
|
||||
$files = $collections['files'] ?? [];
|
||||
if(empty($files)) {
|
||||
if (empty($files)) {
|
||||
throw new Exception('Files collection is not configured.');
|
||||
}
|
||||
|
||||
|
||||
$attributes = [];
|
||||
$indexes = [];
|
||||
|
||||
|
||||
foreach ($files['attributes'] as $attribute) {
|
||||
$attributes[] = new Document([
|
||||
'$id' => $attribute['$id'],
|
||||
|
|
@ -193,7 +193,7 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
'format' => $attribute['format'] ?? ''
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
foreach ($files['indexes'] as $index) {
|
||||
$indexes[] = new Document([
|
||||
'$id' => $index['$id'],
|
||||
|
|
@ -203,15 +203,14 @@ $http->on('start', function (Server $http) use ($payloadSize, $register) {
|
|||
'orders' => $index['orders'],
|
||||
]);
|
||||
}
|
||||
|
||||
|
||||
$dbForConsole->createCollection('bucket_' . $bucket->getInternalId(), $attributes, $indexes);
|
||||
}
|
||||
|
||||
Console::success('[Setup] - Server database init completed...');
|
||||
});
|
||||
|
||||
Console::success('Server started successfully (max payload is '.number_format($payloadSize).' bytes)');
|
||||
|
||||
Console::success('Server started successfully (max payload is ' . number_format($payloadSize) . ' bytes)');
|
||||
Console::info("Master pid {$http->master_pid}, manager pid {$http->manager_pid}");
|
||||
|
||||
// listen ctrl + c
|
||||
|
|
@ -225,13 +224,13 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$request = new Request($swooleRequest);
|
||||
$response = new Response($swooleResponse);
|
||||
|
||||
if(Files::isFileLoaded($request->getURI())) {
|
||||
if (Files::isFileLoaded($request->getURI())) {
|
||||
$time = (60 * 60 * 24 * 365 * 2); // 45 days cache
|
||||
|
||||
$response
|
||||
->setContentType(Files::getFileMimeType($request->getURI()))
|
||||
->addHeader('Cache-Control', 'public, max-age='.$time)
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time).' GMT') // 45 days cache
|
||||
->addHeader('Cache-Control', 'public, max-age=' . $time)
|
||||
->addHeader('Expires', \date('D, d M Y H:i:s', \time() + $time) . ' GMT') // 45 days cache
|
||||
->send(Files::getFileContents($request->getURI()))
|
||||
;
|
||||
|
||||
|
|
@ -255,11 +254,11 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$logger = $app->getResource("logger");
|
||||
if($logger) {
|
||||
if ($logger) {
|
||||
try {
|
||||
/** @var Utopia\Database\Document $user */
|
||||
$user = $app->getResource('user');
|
||||
} catch(\Throwable $_th) {
|
||||
} catch (\Throwable $_th) {
|
||||
// All good, user is optional information for logger
|
||||
}
|
||||
|
||||
|
|
@ -268,7 +267,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
|
||||
$log = new Utopia\Logger\Log();
|
||||
|
||||
if(isset($user) && !$user->isEmpty()) {
|
||||
if (isset($user) && !$user->isEmpty()) {
|
||||
$log->setUser(new User($user->getId()));
|
||||
}
|
||||
|
||||
|
|
@ -279,7 +278,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$log->setMessage($th->getMessage());
|
||||
|
||||
$log->addTag('method', $route->getMethod());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('url', $route->getPath());
|
||||
$log->addTag('verboseType', get_class($th));
|
||||
$log->addTag('code', $th->getCode());
|
||||
// $log->addTag('projectId', $project->getId()); // TODO: Figure out how to get ProjectID, if it becomes relevant
|
||||
|
|
@ -298,18 +297,18 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$isProduction = App::getEnv('_APP_ENV', 'development') === 'production';
|
||||
$log->setEnvironment($isProduction ? Log::ENVIRONMENT_PRODUCTION : Log::ENVIRONMENT_STAGING);
|
||||
|
||||
foreach($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
foreach ($loggerBreadcrumbs as $loggerBreadcrumb) {
|
||||
$log->addBreadcrumb($loggerBreadcrumb);
|
||||
}
|
||||
|
||||
$responseCode = $logger->addLog($log);
|
||||
Console::info('Log pushed with status code: '.$responseCode);
|
||||
Console::info('Log pushed with status code: ' . $responseCode);
|
||||
}
|
||||
|
||||
Console::error('[Error] Type: '.get_class($th));
|
||||
Console::error('[Error] Message: '.$th->getMessage());
|
||||
Console::error('[Error] File: '.$th->getFile());
|
||||
Console::error('[Error] Line: '.$th->getLine());
|
||||
Console::error('[Error] Type: ' . get_class($th));
|
||||
Console::error('[Error] Message: ' . $th->getMessage());
|
||||
Console::error('[Error] File: ' . $th->getFile());
|
||||
Console::error('[Error] Line: ' . $th->getLine());
|
||||
|
||||
/**
|
||||
* Reset Database connection if PDOException was thrown.
|
||||
|
|
@ -321,7 +320,7 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
$swooleResponse->setStatusCode(500);
|
||||
|
||||
$output = ((App::isDevelopment())) ? [
|
||||
'message' => 'Error: '. $th->getMessage(),
|
||||
'message' => 'Error: ' . $th->getMessage(),
|
||||
'code' => 500,
|
||||
'file' => $th->getFile(),
|
||||
'line' => $th->getLine(),
|
||||
|
|
@ -345,4 +344,4 @@ $http->on('request', function (SwooleRequest $swooleRequest, SwooleResponse $swo
|
|||
}
|
||||
});
|
||||
|
||||
$http->start();
|
||||
$http->start();
|
||||
|
|
|
|||
484
app/init.php
484
app/init.php
|
|
@ -2,16 +2,17 @@
|
|||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
*
|
||||
* Initializes both Appwrite API entry point, queue workers, and CLI tasks.
|
||||
* Set configuration, framework resources & app constants
|
||||
*
|
||||
*
|
||||
*/
|
||||
if (\file_exists(__DIR__.'/../vendor/autoload.php')) {
|
||||
require_once __DIR__.'/../vendor/autoload.php';
|
||||
|
||||
if (\file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
require_once __DIR__ . '/../vendor/autoload.php';
|
||||
}
|
||||
|
||||
ini_set('memory_limit','512M');
|
||||
ini_set('memory_limit', '512M');
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
ini_set('default_socket_timeout', -1);
|
||||
|
|
@ -67,7 +68,7 @@ const APP_NAME = 'Appwrite';
|
|||
const APP_DOMAIN = 'appwrite.io';
|
||||
const APP_EMAIL_TEAM = 'team@localhost.test'; // Default email address
|
||||
const APP_EMAIL_SECURITY = ''; // Default security email address
|
||||
const APP_USERAGENT = APP_NAME.'-Server v%s. Please report abuse at %s';
|
||||
const APP_USERAGENT = APP_NAME . '-Server v%s. Please report abuse at %s';
|
||||
const APP_MODE_DEFAULT = 'default';
|
||||
const APP_MODE_ADMIN = 'admin';
|
||||
const APP_PAGING_LIMIT = 12;
|
||||
|
|
@ -77,6 +78,7 @@ const APP_LIMIT_ANTIVIRUS = 20000000; //20MB
|
|||
const APP_LIMIT_ENCRYPTION = 20000000; //20MB
|
||||
const APP_LIMIT_COMPRESSION = 20000000; //20MB
|
||||
const APP_LIMIT_ARRAY_PARAMS_SIZE = 100; // Default maximum of how many elements can there be in API parameter that expects array value
|
||||
const APP_LIMIT_SUBQUERY = 1000;
|
||||
const APP_CACHE_BUSTER = 305;
|
||||
const APP_VERSION_STABLE = '0.14.2';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
|
|
@ -102,7 +104,7 @@ const APP_SOCIAL_GITHUB = 'https://github.com/appwrite';
|
|||
const APP_SOCIAL_DISCORD = 'https://appwrite.io/discord';
|
||||
const APP_SOCIAL_DISCORD_CHANNEL = '564160730845151244';
|
||||
const APP_SOCIAL_DEV = 'https://dev.to/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_STACKSHARE = 'https://stackshare.io/appwrite';
|
||||
const APP_SOCIAL_YOUTUBE = 'https://www.youtube.com/c/appwrite?sub_confirmation=1';
|
||||
// Database Reconnect
|
||||
const DATABASE_RECONNECT_SLEEP = 2;
|
||||
|
|
@ -122,7 +124,7 @@ const DELETE_TYPE_PROJECTS = 'projects';
|
|||
const DELETE_TYPE_FUNCTIONS = 'functions';
|
||||
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
|
||||
const DELETE_TYPE_USERS = 'users';
|
||||
const DELETE_TYPE_TEAMS= 'teams';
|
||||
const DELETE_TYPE_TEAMS = 'teams';
|
||||
const DELETE_TYPE_EXECUTIONS = 'executions';
|
||||
const DELETE_TYPE_AUDIT = 'audit';
|
||||
const DELETE_TYPE_ABUSE = 'abuse';
|
||||
|
|
@ -142,7 +144,7 @@ const APP_AUTH_TYPE_JWT = 'JWT';
|
|||
const APP_AUTH_TYPE_KEY = 'Key';
|
||||
const APP_AUTH_TYPE_ADMIN = 'Admin';
|
||||
// Response related
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 2*1024*1024; // 2MB
|
||||
const MAX_OUTPUT_CHUNK_SIZE = 2 * 1024 * 1024; // 2MB
|
||||
|
||||
$register = new Registry();
|
||||
|
||||
|
|
@ -151,82 +153,89 @@ App::setMode(App::getEnv('_APP_ENV', App::MODE_TYPE_PRODUCTION));
|
|||
/*
|
||||
* ENV vars
|
||||
*/
|
||||
Config::load('events', __DIR__.'/config/events.php');
|
||||
Config::load('auth', __DIR__.'/config/auth.php');
|
||||
Config::load('errors', __DIR__.'/config/errors.php');
|
||||
Config::load('providers', __DIR__.'/config/providers.php');
|
||||
Config::load('platforms', __DIR__.'/config/platforms.php');
|
||||
Config::load('collections', __DIR__.'/config/collections.php');
|
||||
Config::load('runtimes', __DIR__.'/config/runtimes.php');
|
||||
Config::load('roles', __DIR__.'/config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__.'/config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__.'/config/services.php'); // List of services
|
||||
Config::load('variables', __DIR__.'/config/variables.php'); // List of env variables
|
||||
Config::load('avatar-browsers', __DIR__.'/config/avatars/browsers.php');
|
||||
Config::load('avatar-credit-cards', __DIR__.'/config/avatars/credit-cards.php');
|
||||
Config::load('avatar-flags', __DIR__.'/config/avatars/flags.php');
|
||||
Config::load('locale-codes', __DIR__.'/config/locale/codes.php');
|
||||
Config::load('locale-currencies', __DIR__.'/config/locale/currencies.php');
|
||||
Config::load('locale-eu', __DIR__.'/config/locale/eu.php');
|
||||
Config::load('locale-languages', __DIR__.'/config/locale/languages.php');
|
||||
Config::load('locale-phones', __DIR__.'/config/locale/phones.php');
|
||||
Config::load('locale-countries', __DIR__.'/config/locale/countries.php');
|
||||
Config::load('locale-continents', __DIR__.'/config/locale/continents.php');
|
||||
Config::load('storage-logos', __DIR__.'/config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__.'/config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__.'/config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__.'/config/storage/outputs.php');
|
||||
Config::load('events', __DIR__ . '/config/events.php');
|
||||
Config::load('auth', __DIR__ . '/config/auth.php');
|
||||
Config::load('errors', __DIR__ . '/config/errors.php');
|
||||
Config::load('providers', __DIR__ . '/config/providers.php');
|
||||
Config::load('platforms', __DIR__ . '/config/platforms.php');
|
||||
Config::load('collections', __DIR__ . '/config/collections.php');
|
||||
Config::load('runtimes', __DIR__ . '/config/runtimes.php');
|
||||
Config::load('roles', __DIR__ . '/config/roles.php'); // User roles and scopes
|
||||
Config::load('scopes', __DIR__ . '/config/scopes.php'); // User roles and scopes
|
||||
Config::load('services', __DIR__ . '/config/services.php'); // List of services
|
||||
Config::load('variables', __DIR__ . '/config/variables.php'); // List of env variables
|
||||
Config::load('avatar-browsers', __DIR__ . '/config/avatars/browsers.php');
|
||||
Config::load('avatar-credit-cards', __DIR__ . '/config/avatars/credit-cards.php');
|
||||
Config::load('avatar-flags', __DIR__ . '/config/avatars/flags.php');
|
||||
Config::load('locale-codes', __DIR__ . '/config/locale/codes.php');
|
||||
Config::load('locale-currencies', __DIR__ . '/config/locale/currencies.php');
|
||||
Config::load('locale-eu', __DIR__ . '/config/locale/eu.php');
|
||||
Config::load('locale-languages', __DIR__ . '/config/locale/languages.php');
|
||||
Config::load('locale-phones', __DIR__ . '/config/locale/phones.php');
|
||||
Config::load('locale-countries', __DIR__ . '/config/locale/countries.php');
|
||||
Config::load('locale-continents', __DIR__ . '/config/locale/continents.php');
|
||||
Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
|
||||
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
|
||||
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
|
||||
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
|
||||
|
||||
$user = App::getEnv('_APP_REDIS_USER','');
|
||||
$pass = App::getEnv('_APP_REDIS_PASS','');
|
||||
if(!empty($user) || !empty($pass)) {
|
||||
Resque::setBackend('redis://'.$user.':'.$pass.'@'.App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
|
||||
$user = App::getEnv('_APP_REDIS_USER', '');
|
||||
$pass = App::getEnv('_APP_REDIS_PASS', '');
|
||||
if (!empty($user) || !empty($pass)) {
|
||||
Resque::setBackend('redis://' . $user . ':' . $pass . '@' . App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', ''));
|
||||
} else {
|
||||
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '').':'.App::getEnv('_APP_REDIS_PORT', ''));
|
||||
Resque::setBackend(App::getEnv('_APP_REDIS_HOST', '') . ':' . App::getEnv('_APP_REDIS_PORT', ''));
|
||||
}
|
||||
|
||||
/**
|
||||
* New DB Filters
|
||||
*/
|
||||
Database::addFilter('casting',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'casting',
|
||||
function (mixed $value) {
|
||||
return json_encode(['value' => $value], JSON_PRESERVE_ZERO_FRACTION);
|
||||
},
|
||||
function($value) {
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return json_decode($value, true)['value'];
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('enum',
|
||||
function($value, Document $attribute) {
|
||||
Database::addFilter(
|
||||
'enum',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('elements')) {
|
||||
$attribute->removeAttribute('elements');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function($value, Document $attribute) {
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['elements'])) {
|
||||
$attribute->setAttribute('elements', $formatOptions['elements']);
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('range',
|
||||
function($value, Document $attribute) {
|
||||
Database::addFilter(
|
||||
'range',
|
||||
function (mixed $value, Document $attribute) {
|
||||
if ($attribute->isSet('min')) {
|
||||
$attribute->removeAttribute('min');
|
||||
}
|
||||
if ($attribute->isSet('max')) {
|
||||
$attribute->removeAttribute('max');
|
||||
}
|
||||
|
||||
return $value;
|
||||
},
|
||||
function($value, Document $attribute) {
|
||||
function (mixed $value, Document $attribute) {
|
||||
$formatOptions = json_decode($attribute->getAttribute('formatOptions', '[]'), true);
|
||||
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
|
||||
$attribute
|
||||
|
|
@ -234,124 +243,134 @@ Database::addFilter('range',
|
|||
->setAttribute('max', $formatOptions['max'])
|
||||
;
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryAttributes',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryAttributes',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('attributes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getAttributeLimit(), 0, []);
|
||||
], $database->getAttributeLimit());
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryIndexes',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryIndexes',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('indexes', [
|
||||
new Query('collectionId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], 64, 0, []);
|
||||
], 64);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryPlatforms',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryPlatforms',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('platforms', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []);
|
||||
], APP_LIMIT_SUBQUERY);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryDomains',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryDomains',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('domains', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []);
|
||||
], APP_LIMIT_SUBQUERY);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryKeys',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryKeys',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('keys', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []);
|
||||
], APP_LIMIT_SUBQUERY);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryWebhooks',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryWebhooks',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return $database
|
||||
->find('webhooks', [
|
||||
new Query('projectId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []);
|
||||
], APP_LIMIT_SUBQUERY);
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQuerySessions',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQuerySessions',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
$sessions = Authorization::skip(fn () => $database->find('sessions', [
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn () => $database->find('sessions', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []));
|
||||
|
||||
return $sessions;
|
||||
], APP_LIMIT_SUBQUERY));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryTokens',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryTokens',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn() => $database
|
||||
->find('tokens', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('subQueryMemberships',
|
||||
function($value) {
|
||||
return null;
|
||||
},
|
||||
function($value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn() => $database
|
||||
->find('memberships', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], $database->getIndexLimit(), 0, []));
|
||||
], APP_LIMIT_SUBQUERY));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter('encrypt',
|
||||
function($value) {
|
||||
Database::addFilter(
|
||||
'subQueryMemberships',
|
||||
function (mixed $value) {
|
||||
return null;
|
||||
},
|
||||
function (mixed $value, Document $document, Database $database) {
|
||||
return Authorization::skip(fn() => $database
|
||||
->find('memberships', [
|
||||
new Query('userId', Query::TYPE_EQUAL, [$document->getId()])
|
||||
], APP_LIMIT_SUBQUERY));
|
||||
}
|
||||
);
|
||||
|
||||
Database::addFilter(
|
||||
'encrypt',
|
||||
function (mixed $value) {
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V1');
|
||||
$iv = OpenSSL::randomPseudoBytes(OpenSSL::cipherIVLength(OpenSSL::CIPHER_AES_128_GCM));
|
||||
$tag = null;
|
||||
|
||||
return json_encode([
|
||||
'data' => OpenSSL::encrypt($value, OpenSSL::CIPHER_AES_128_GCM, $key, 0, $iv, $tag),
|
||||
'method' => OpenSSL::CIPHER_AES_128_GCM,
|
||||
|
|
@ -360,12 +379,12 @@ Database::addFilter('encrypt',
|
|||
'version' => '1',
|
||||
]);
|
||||
},
|
||||
function($value) {
|
||||
if(is_null($value)) {
|
||||
function (mixed $value) {
|
||||
if (is_null($value)) {
|
||||
return null;
|
||||
}
|
||||
$value = json_decode($value, true);
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V'.$value['version']);
|
||||
$key = App::getEnv('_APP_OPENSSL_KEY_V' . $value['version']);
|
||||
|
||||
return OpenSSL::decrypt($value['data'], $value['method'], $key, 0, hex2bin($value['iv']), hex2bin($value['tag']));
|
||||
}
|
||||
|
|
@ -374,30 +393,30 @@ Database::addFilter('encrypt',
|
|||
/**
|
||||
* DB Formats
|
||||
*/
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function() {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_EMAIL, function () {
|
||||
return new Email();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function($attribute) {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_ENUM, function ($attribute) {
|
||||
$elements = $attribute['formatOptions']['elements'];
|
||||
return new WhiteList($elements, true);
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function() {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_IP, function () {
|
||||
return new IP();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function() {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_URL, function () {
|
||||
return new URL();
|
||||
}, Database::VAR_STRING);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function($attribute) {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_INT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_INTEGER);
|
||||
}, Database::VAR_INTEGER);
|
||||
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function($attribute) {
|
||||
Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function ($attribute) {
|
||||
$min = $attribute['formatOptions']['min'] ?? -INF;
|
||||
$max = $attribute['formatOptions']['max'] ?? INF;
|
||||
return new Range($min, $max, Range::TYPE_FLOAT);
|
||||
|
|
@ -406,30 +425,33 @@ Structure::addFormat(APP_DATABASE_ATTRIBUTE_FLOAT_RANGE, function($attribute) {
|
|||
/*
|
||||
* Registry
|
||||
*/
|
||||
$register->set('logger', function () { // Register error logger
|
||||
$register->set('logger', function () {
|
||||
// Register error logger
|
||||
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if(empty($providerName) || empty($providerConfig)) {
|
||||
if (empty($providerName) || empty($providerConfig)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if(!Logger::hasProvider($providerName)) {
|
||||
if (!Logger::hasProvider($providerName)) {
|
||||
throw new Exception("Logging provider not supported. Logging disabled.", 500, Exception::GENERAL_SERVER_ERROR);
|
||||
}
|
||||
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\'.\ucfirst($providerName);
|
||||
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
|
||||
$adapter = new $classname($providerConfig);
|
||||
return new Logger($adapter);
|
||||
});
|
||||
$register->set('dbPool', function () { // Register DB connection
|
||||
$register->set('dbPool', function () {
|
||||
// Register DB connection
|
||||
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
||||
$dbPort = App::getEnv('_APP_DB_PORT', '');
|
||||
$dbUser = App::getEnv('_APP_DB_USER', '');
|
||||
$dbPass = App::getEnv('_APP_DB_PASS', '');
|
||||
$dbScheme = App::getEnv('_APP_DB_SCHEMA', '');
|
||||
|
||||
$pool = new PDOPool((new PDOConfig())
|
||||
$pool = new PDOPool(
|
||||
(new PDOConfig())
|
||||
->withHost($dbHost)
|
||||
->withPort($dbPort)
|
||||
->withDbName($dbScheme)
|
||||
|
|
@ -438,8 +460,9 @@ $register->set('dbPool', function () { // Register DB connection
|
|||
->withPassword($dbPass)
|
||||
->withOptions([
|
||||
PDO::ATTR_ERRMODE => App::isDevelopment() ? PDO::ERRMODE_WARNING : PDO::ERRMODE_SILENT, // If in production mode, warnings are not displayed
|
||||
])
|
||||
, 64);
|
||||
]),
|
||||
64
|
||||
);
|
||||
|
||||
return $pool;
|
||||
});
|
||||
|
|
@ -451,19 +474,22 @@ $register->set('redisPool', function () {
|
|||
$redisAuth = '';
|
||||
|
||||
if ($redisUser && $redisPass) {
|
||||
$redisAuth = $redisUser.':'.$redisPass;
|
||||
$redisAuth = $redisUser . ':' . $redisPass;
|
||||
}
|
||||
|
||||
$pool = new RedisPool((new RedisConfig)
|
||||
$pool = new RedisPool(
|
||||
(new RedisConfig())
|
||||
->withHost($redisHost)
|
||||
->withPort($redisPort)
|
||||
->withAuth($redisAuth)
|
||||
->withDbIndex(0)
|
||||
, 64);
|
||||
->withDbIndex(0),
|
||||
64
|
||||
);
|
||||
|
||||
return $pool;
|
||||
});
|
||||
$register->set('influxdb', function () { // Register DB connection
|
||||
$register->set('influxdb', function () {
|
||||
// Register DB connection
|
||||
$host = App::getEnv('_APP_INFLUXDB_HOST', '');
|
||||
$port = App::getEnv('_APP_INFLUXDB_PORT', '');
|
||||
|
||||
|
|
@ -476,7 +502,8 @@ $register->set('influxdb', function () { // Register DB connection
|
|||
|
||||
return $client;
|
||||
});
|
||||
$register->set('statsd', function () { // Register DB connection
|
||||
$register->set('statsd', function () {
|
||||
// Register DB connection
|
||||
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
|
||||
$port = App::getEnv('_APP_STATSD_PORT', 8125);
|
||||
|
||||
|
|
@ -503,7 +530,7 @@ $register->set('smtp', function () {
|
|||
$mail->SMTPAutoTLS = false;
|
||||
$mail->CharSet = 'UTF-8';
|
||||
|
||||
$from = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME.' Server'));
|
||||
$from = \urldecode(App::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server'));
|
||||
$email = App::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
|
||||
|
||||
$mail->setFrom($email, $from);
|
||||
|
|
@ -514,9 +541,10 @@ $register->set('smtp', function () {
|
|||
return $mail;
|
||||
});
|
||||
$register->set('geodb', function () {
|
||||
return new Reader(__DIR__.'/db/DBIP/dbip-country-lite-2022-03.mmdb');
|
||||
return new Reader(__DIR__ . '/db/DBIP/dbip-country-lite-2022-03.mmdb');
|
||||
});
|
||||
$register->set('db', function () { // This is usually for our workers or CLI commands scope
|
||||
$register->set('db', function () {
|
||||
// This is usually for our workers or CLI commands scope
|
||||
$dbHost = App::getEnv('_APP_DB_HOST', '');
|
||||
$dbPort = App::getEnv('_APP_DB_PORT', '');
|
||||
$dbUser = App::getEnv('_APP_DB_USER', '');
|
||||
|
|
@ -533,7 +561,8 @@ $register->set('db', function () { // This is usually for our workers or CLI com
|
|||
|
||||
return $pdo;
|
||||
});
|
||||
$register->set('cache', function () { // This is usually for our workers or CLI commands scope
|
||||
$register->set('cache', function () {
|
||||
// This is usually for our workers or CLI commands scope
|
||||
$redis = new Redis();
|
||||
$redis->pconnect(App::getEnv('_APP_REDIS_HOST', ''), App::getEnv('_APP_REDIS_PORT', ''));
|
||||
$redis->setOption(Redis::OPT_READ_TIMEOUT, -1);
|
||||
|
|
@ -545,59 +574,59 @@ $register->set('cache', function () { // This is usually for our workers or CLI
|
|||
* Localization
|
||||
*/
|
||||
Locale::$exceptions = false;
|
||||
Locale::setLanguageFromJSON('af', __DIR__.'/config/locale/translations/af.json');
|
||||
Locale::setLanguageFromJSON('ar', __DIR__.'/config/locale/translations/ar.json');
|
||||
Locale::setLanguageFromJSON('as', __DIR__.'/config/locale/translations/as.json');
|
||||
Locale::setLanguageFromJSON('az', __DIR__.'/config/locale/translations/az.json');
|
||||
Locale::setLanguageFromJSON('be', __DIR__.'/config/locale/translations/be.json');
|
||||
Locale::setLanguageFromJSON('bg', __DIR__.'/config/locale/translations/bg.json');
|
||||
Locale::setLanguageFromJSON('bh', __DIR__.'/config/locale/translations/bh.json');
|
||||
Locale::setLanguageFromJSON('bn', __DIR__.'/config/locale/translations/bn.json');
|
||||
Locale::setLanguageFromJSON('bs', __DIR__.'/config/locale/translations/bs.json');
|
||||
Locale::setLanguageFromJSON('ca', __DIR__.'/config/locale/translations/ca.json');
|
||||
Locale::setLanguageFromJSON('cs', __DIR__.'/config/locale/translations/cs.json');
|
||||
Locale::setLanguageFromJSON('da', __DIR__.'/config/locale/translations/da.json');
|
||||
Locale::setLanguageFromJSON('de', __DIR__.'/config/locale/translations/de.json');
|
||||
Locale::setLanguageFromJSON('el', __DIR__.'/config/locale/translations/el.json');
|
||||
Locale::setLanguageFromJSON('en', __DIR__.'/config/locale/translations/en.json');
|
||||
Locale::setLanguageFromJSON('eo', __DIR__.'/config/locale/translations/eo.json');
|
||||
Locale::setLanguageFromJSON('es', __DIR__.'/config/locale/translations/es.json');
|
||||
Locale::setLanguageFromJSON('fa', __DIR__.'/config/locale/translations/fa.json');
|
||||
Locale::setLanguageFromJSON('fi', __DIR__.'/config/locale/translations/fi.json');
|
||||
Locale::setLanguageFromJSON('fo', __DIR__.'/config/locale/translations/fo.json');
|
||||
Locale::setLanguageFromJSON('fr', __DIR__.'/config/locale/translations/fr.json');
|
||||
Locale::setLanguageFromJSON('ga', __DIR__.'/config/locale/translations/ga.json');
|
||||
Locale::setLanguageFromJSON('gu', __DIR__.'/config/locale/translations/gu.json');
|
||||
Locale::setLanguageFromJSON('he', __DIR__.'/config/locale/translations/he.json');
|
||||
Locale::setLanguageFromJSON('hi', __DIR__.'/config/locale/translations/hi.json');
|
||||
Locale::setLanguageFromJSON('hr', __DIR__.'/config/locale/translations/hr.json');
|
||||
Locale::setLanguageFromJSON('hu', __DIR__.'/config/locale/translations/hu.json');
|
||||
Locale::setLanguageFromJSON('hy', __DIR__.'/config/locale/translations/hy.json');
|
||||
Locale::setLanguageFromJSON('id', __DIR__.'/config/locale/translations/id.json');
|
||||
Locale::setLanguageFromJSON('is', __DIR__.'/config/locale/translations/is.json');
|
||||
Locale::setLanguageFromJSON('it', __DIR__.'/config/locale/translations/it.json');
|
||||
Locale::setLanguageFromJSON('ja', __DIR__.'/config/locale/translations/ja.json');
|
||||
Locale::setLanguageFromJSON('jv', __DIR__.'/config/locale/translations/jv.json');
|
||||
Locale::setLanguageFromJSON('kn', __DIR__.'/config/locale/translations/kn.json');
|
||||
Locale::setLanguageFromJSON('km', __DIR__.'/config/locale/translations/km.json');
|
||||
Locale::setLanguageFromJSON('ko', __DIR__.'/config/locale/translations/ko.json');
|
||||
Locale::setLanguageFromJSON('la', __DIR__.'/config/locale/translations/la.json');
|
||||
Locale::setLanguageFromJSON('lb', __DIR__.'/config/locale/translations/lb.json');
|
||||
Locale::setLanguageFromJSON('lt', __DIR__.'/config/locale/translations/lt.json');
|
||||
Locale::setLanguageFromJSON('lv', __DIR__.'/config/locale/translations/lv.json');
|
||||
Locale::setLanguageFromJSON('ml', __DIR__.'/config/locale/translations/ml.json');
|
||||
Locale::setLanguageFromJSON('mr', __DIR__.'/config/locale/translations/mr.json');
|
||||
Locale::setLanguageFromJSON('ms', __DIR__.'/config/locale/translations/ms.json');
|
||||
Locale::setLanguageFromJSON('nb', __DIR__.'/config/locale/translations/nb.json');
|
||||
Locale::setLanguageFromJSON('ne', __DIR__.'/config/locale/translations/ne.json');
|
||||
Locale::setLanguageFromJSON('nl', __DIR__.'/config/locale/translations/nl.json');
|
||||
Locale::setLanguageFromJSON('nn', __DIR__.'/config/locale/translations/nn.json');
|
||||
Locale::setLanguageFromJSON('or', __DIR__.'/config/locale/translations/or.json');
|
||||
Locale::setLanguageFromJSON('pa', __DIR__.'/config/locale/translations/pa.json');
|
||||
Locale::setLanguageFromJSON('pl', __DIR__.'/config/locale/translations/pl.json');
|
||||
Locale::setLanguageFromJSON('pt-br', __DIR__.'/config/locale/translations/pt-br.json');
|
||||
Locale::setLanguageFromJSON('pt-pt', __DIR__.'/config/locale/translations/pt-pt.json');
|
||||
Locale::setLanguageFromJSON('ro', __DIR__.'/config/locale/translations/ro.json');
|
||||
Locale::setLanguageFromJSON('af', __DIR__ . '/config/locale/translations/af.json');
|
||||
Locale::setLanguageFromJSON('ar', __DIR__ . '/config/locale/translations/ar.json');
|
||||
Locale::setLanguageFromJSON('as', __DIR__ . '/config/locale/translations/as.json');
|
||||
Locale::setLanguageFromJSON('az', __DIR__ . '/config/locale/translations/az.json');
|
||||
Locale::setLanguageFromJSON('be', __DIR__ . '/config/locale/translations/be.json');
|
||||
Locale::setLanguageFromJSON('bg', __DIR__ . '/config/locale/translations/bg.json');
|
||||
Locale::setLanguageFromJSON('bh', __DIR__ . '/config/locale/translations/bh.json');
|
||||
Locale::setLanguageFromJSON('bn', __DIR__ . '/config/locale/translations/bn.json');
|
||||
Locale::setLanguageFromJSON('bs', __DIR__ . '/config/locale/translations/bs.json');
|
||||
Locale::setLanguageFromJSON('ca', __DIR__ . '/config/locale/translations/ca.json');
|
||||
Locale::setLanguageFromJSON('cs', __DIR__ . '/config/locale/translations/cs.json');
|
||||
Locale::setLanguageFromJSON('da', __DIR__ . '/config/locale/translations/da.json');
|
||||
Locale::setLanguageFromJSON('de', __DIR__ . '/config/locale/translations/de.json');
|
||||
Locale::setLanguageFromJSON('el', __DIR__ . '/config/locale/translations/el.json');
|
||||
Locale::setLanguageFromJSON('en', __DIR__ . '/config/locale/translations/en.json');
|
||||
Locale::setLanguageFromJSON('eo', __DIR__ . '/config/locale/translations/eo.json');
|
||||
Locale::setLanguageFromJSON('es', __DIR__ . '/config/locale/translations/es.json');
|
||||
Locale::setLanguageFromJSON('fa', __DIR__ . '/config/locale/translations/fa.json');
|
||||
Locale::setLanguageFromJSON('fi', __DIR__ . '/config/locale/translations/fi.json');
|
||||
Locale::setLanguageFromJSON('fo', __DIR__ . '/config/locale/translations/fo.json');
|
||||
Locale::setLanguageFromJSON('fr', __DIR__ . '/config/locale/translations/fr.json');
|
||||
Locale::setLanguageFromJSON('ga', __DIR__ . '/config/locale/translations/ga.json');
|
||||
Locale::setLanguageFromJSON('gu', __DIR__ . '/config/locale/translations/gu.json');
|
||||
Locale::setLanguageFromJSON('he', __DIR__ . '/config/locale/translations/he.json');
|
||||
Locale::setLanguageFromJSON('hi', __DIR__ . '/config/locale/translations/hi.json');
|
||||
Locale::setLanguageFromJSON('hr', __DIR__ . '/config/locale/translations/hr.json');
|
||||
Locale::setLanguageFromJSON('hu', __DIR__ . '/config/locale/translations/hu.json');
|
||||
Locale::setLanguageFromJSON('hy', __DIR__ . '/config/locale/translations/hy.json');
|
||||
Locale::setLanguageFromJSON('id', __DIR__ . '/config/locale/translations/id.json');
|
||||
Locale::setLanguageFromJSON('is', __DIR__ . '/config/locale/translations/is.json');
|
||||
Locale::setLanguageFromJSON('it', __DIR__ . '/config/locale/translations/it.json');
|
||||
Locale::setLanguageFromJSON('ja', __DIR__ . '/config/locale/translations/ja.json');
|
||||
Locale::setLanguageFromJSON('jv', __DIR__ . '/config/locale/translations/jv.json');
|
||||
Locale::setLanguageFromJSON('kn', __DIR__ . '/config/locale/translations/kn.json');
|
||||
Locale::setLanguageFromJSON('km', __DIR__ . '/config/locale/translations/km.json');
|
||||
Locale::setLanguageFromJSON('ko', __DIR__ . '/config/locale/translations/ko.json');
|
||||
Locale::setLanguageFromJSON('la', __DIR__ . '/config/locale/translations/la.json');
|
||||
Locale::setLanguageFromJSON('lb', __DIR__ . '/config/locale/translations/lb.json');
|
||||
Locale::setLanguageFromJSON('lt', __DIR__ . '/config/locale/translations/lt.json');
|
||||
Locale::setLanguageFromJSON('lv', __DIR__ . '/config/locale/translations/lv.json');
|
||||
Locale::setLanguageFromJSON('ml', __DIR__ . '/config/locale/translations/ml.json');
|
||||
Locale::setLanguageFromJSON('mr', __DIR__ . '/config/locale/translations/mr.json');
|
||||
Locale::setLanguageFromJSON('ms', __DIR__ . '/config/locale/translations/ms.json');
|
||||
Locale::setLanguageFromJSON('nb', __DIR__ . '/config/locale/translations/nb.json');
|
||||
Locale::setLanguageFromJSON('ne', __DIR__ . '/config/locale/translations/ne.json');
|
||||
Locale::setLanguageFromJSON('nl', __DIR__ . '/config/locale/translations/nl.json');
|
||||
Locale::setLanguageFromJSON('nn', __DIR__ . '/config/locale/translations/nn.json');
|
||||
Locale::setLanguageFromJSON('or', __DIR__ . '/config/locale/translations/or.json');
|
||||
Locale::setLanguageFromJSON('pa', __DIR__ . '/config/locale/translations/pa.json');
|
||||
Locale::setLanguageFromJSON('pl', __DIR__ . '/config/locale/translations/pl.json');
|
||||
Locale::setLanguageFromJSON('pt-br', __DIR__ . '/config/locale/translations/pt-br.json');
|
||||
Locale::setLanguageFromJSON('pt-pt', __DIR__ . '/config/locale/translations/pt-pt.json');
|
||||
Locale::setLanguageFromJSON('ro', __DIR__ . '/config/locale/translations/ro.json');
|
||||
Locale::setLanguageFromJSON('ru', __DIR__ . '/config/locale/translations/ru.json');
|
||||
Locale::setLanguageFromJSON('sa', __DIR__ . '/config/locale/translations/sa.json');
|
||||
Locale::setLanguageFromJSON('sd', __DIR__ . '/config/locale/translations/sd.json');
|
||||
|
|
@ -608,39 +637,41 @@ Locale::setLanguageFromJSON('sn', __DIR__ . '/config/locale/translations/sn.json
|
|||
Locale::setLanguageFromJSON('sq', __DIR__ . '/config/locale/translations/sq.json');
|
||||
Locale::setLanguageFromJSON('sv', __DIR__ . '/config/locale/translations/sv.json');
|
||||
Locale::setLanguageFromJSON('ta', __DIR__ . '/config/locale/translations/ta.json');
|
||||
Locale::setLanguageFromJSON('te', __DIR__.'/config/locale/translations/te.json');
|
||||
Locale::setLanguageFromJSON('th', __DIR__.'/config/locale/translations/th.json');
|
||||
Locale::setLanguageFromJSON('tl', __DIR__.'/config/locale/translations/tl.json');
|
||||
Locale::setLanguageFromJSON('tr', __DIR__.'/config/locale/translations/tr.json');
|
||||
Locale::setLanguageFromJSON('uk', __DIR__.'/config/locale/translations/uk.json');
|
||||
Locale::setLanguageFromJSON('ur', __DIR__.'/config/locale/translations/ur.json');
|
||||
Locale::setLanguageFromJSON('vi', __DIR__.'/config/locale/translations/vi.json');
|
||||
Locale::setLanguageFromJSON('zh-cn', __DIR__.'/config/locale/translations/zh-cn.json');
|
||||
Locale::setLanguageFromJSON('zh-tw', __DIR__.'/config/locale/translations/zh-tw.json');
|
||||
Locale::setLanguageFromJSON('te', __DIR__ . '/config/locale/translations/te.json');
|
||||
Locale::setLanguageFromJSON('th', __DIR__ . '/config/locale/translations/th.json');
|
||||
Locale::setLanguageFromJSON('tl', __DIR__ . '/config/locale/translations/tl.json');
|
||||
Locale::setLanguageFromJSON('tr', __DIR__ . '/config/locale/translations/tr.json');
|
||||
Locale::setLanguageFromJSON('uk', __DIR__ . '/config/locale/translations/uk.json');
|
||||
Locale::setLanguageFromJSON('ur', __DIR__ . '/config/locale/translations/ur.json');
|
||||
Locale::setLanguageFromJSON('vi', __DIR__ . '/config/locale/translations/vi.json');
|
||||
Locale::setLanguageFromJSON('zh-cn', __DIR__ . '/config/locale/translations/zh-cn.json');
|
||||
Locale::setLanguageFromJSON('zh-tw', __DIR__ . '/config/locale/translations/zh-tw.json');
|
||||
|
||||
\stream_context_set_default([ // Set global user agent and http settings
|
||||
'http' => [
|
||||
'method' => 'GET',
|
||||
'user_agent' => \sprintf(APP_USERAGENT,
|
||||
'user_agent' => \sprintf(
|
||||
APP_USERAGENT,
|
||||
App::getEnv('_APP_VERSION', 'UNKNOWN'),
|
||||
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)),
|
||||
App::getEnv('_APP_SYSTEM_SECURITY_EMAIL_ADDRESS', APP_EMAIL_SECURITY)
|
||||
),
|
||||
'timeout' => 2,
|
||||
],
|
||||
]);
|
||||
|
||||
// Runtime Execution
|
||||
App::setResource('logger', function($register) {
|
||||
App::setResource('logger', function ($register) {
|
||||
return $register->get('logger');
|
||||
}, ['register']);
|
||||
|
||||
App::setResource('loggerBreadcrumbs', function() {
|
||||
App::setResource('loggerBreadcrumbs', function () {
|
||||
return [];
|
||||
});
|
||||
|
||||
App::setResource('register', fn() => $register);
|
||||
|
||||
App::setResource('layout', function($locale) {
|
||||
$layout = new View(__DIR__.'/views/layouts/default.phtml');
|
||||
App::setResource('layout', function ($locale) {
|
||||
$layout = new View(__DIR__ . '/views/layouts/default.phtml');
|
||||
$layout->setParam('locale', $locale);
|
||||
|
||||
return $layout;
|
||||
|
|
@ -654,7 +685,7 @@ App::setResource('audits', fn() => new Audit());
|
|||
App::setResource('mails', fn() => new Mail());
|
||||
App::setResource('deletes', fn() => new Delete());
|
||||
App::setResource('database', fn() => new EventDatabase());
|
||||
App::setResource('usage', function($register) {
|
||||
App::setResource('usage', function ($register) {
|
||||
return new Stats($register->get('statsd'));
|
||||
}, ['register']);
|
||||
|
||||
|
|
@ -694,7 +725,7 @@ App::setResource('clients', function ($request, $console, $project) {
|
|||
return $clients;
|
||||
}, ['request', 'console', 'project']);
|
||||
|
||||
App::setResource('user', function($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) {
|
||||
App::setResource('user', function ($mode, $project, $console, $request, $response, $dbForProject, $dbForConsole) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Appwrite\Utopia\Response $response */
|
||||
/** @var Utopia\Database\Document $project */
|
||||
|
|
@ -704,21 +735,28 @@ App::setResource('user', function($mode, $project, $console, $request, $response
|
|||
|
||||
Authorization::setDefaultStatus(true);
|
||||
|
||||
Auth::setCookieName('a_session_'.$project->getId());
|
||||
Auth::setCookieName('a_session_' . $project->getId());
|
||||
|
||||
if (APP_MODE_ADMIN === $mode) {
|
||||
Auth::setCookieName('a_session_'.$console->getId());
|
||||
Auth::setCookieName('a_session_' . $console->getId());
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession(
|
||||
$request->getCookie(Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName.'_legacy', '')));// Get fallback session from old clients (no SameSite support)
|
||||
$request->getCookie(
|
||||
Auth::$cookieName, // Get sessions
|
||||
$request->getCookie(Auth::$cookieName . '_legacy', '')
|
||||
)
|
||||
);// Get fallback session from old clients (no SameSite support)
|
||||
|
||||
// Get fallback session from clients who block 3rd-party cookies
|
||||
if($response) $response->addHeader('X-Debug-Fallback', 'false');
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'false');
|
||||
}
|
||||
|
||||
if(empty($session['id']) && empty($session['secret'])) {
|
||||
if($response) $response->addHeader('X-Debug-Fallback', 'true');
|
||||
if (empty($session['id']) && empty($session['secret'])) {
|
||||
if ($response) {
|
||||
$response->addHeader('X-Debug-Fallback', 'true');
|
||||
}
|
||||
$fallback = $request->getHeader('x-fallback-cookies', '');
|
||||
$fallback = \json_decode($fallback, true);
|
||||
$session = Auth::decodeSession(((isset($fallback[Auth::$cookieName])) ? $fallback[Auth::$cookieName] : ''));
|
||||
|
|
@ -730,17 +768,17 @@ App::setResource('user', function($mode, $project, $console, $request, $response
|
|||
if (APP_MODE_ADMIN !== $mode) {
|
||||
if ($project->isEmpty()) {
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$user = $dbForProject->getDocument('users', Auth::$unique);
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
$user = $dbForConsole->getDocument('users', Auth::$unique);
|
||||
}
|
||||
|
||||
if ($user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)) { // Validate user has valid login token
|
||||
if (
|
||||
$user->isEmpty() // Check a document has been found in the DB
|
||||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret)
|
||||
) { // Validate user has valid login token
|
||||
$user = new Document(['$id' => '', '$collection' => 'users']);
|
||||
}
|
||||
|
||||
|
|
@ -760,13 +798,13 @@ App::setResource('user', function($mode, $project, $console, $request, $response
|
|||
try {
|
||||
$payload = $jwt->decode($authJWT);
|
||||
} catch (JWTException $error) {
|
||||
throw new Exception('Failed to verify JWT. '.$error->getMessage(), 401, Exception::USER_JWT_INVALID);
|
||||
throw new Exception('Failed to verify JWT. ' . $error->getMessage(), 401, Exception::USER_JWT_INVALID);
|
||||
}
|
||||
|
||||
$jwtUserId = $payload['userId'] ?? '';
|
||||
$jwtSessionId = $payload['sessionId'] ?? '';
|
||||
|
||||
if($jwtUserId && $jwtSessionId) {
|
||||
if ($jwtUserId && $jwtSessionId) {
|
||||
$user = $dbForProject->getDocument('users', $jwtUserId);
|
||||
}
|
||||
|
||||
|
|
@ -778,14 +816,14 @@ App::setResource('user', function($mode, $project, $console, $request, $response
|
|||
return $user;
|
||||
}, ['mode', 'project', 'console', 'request', 'response', 'dbForProject', 'dbForConsole']);
|
||||
|
||||
App::setResource('project', function($dbForConsole, $request, $console) {
|
||||
App::setResource('project', function ($dbForConsole, $request, $console) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
/** @var Utopia\Database\Database $dbForConsole */
|
||||
/** @var Utopia\Database\Document $console */
|
||||
|
||||
$projectId = $request->getParam('project', $request->getHeader('x-appwrite-project', 'console'));
|
||||
|
||||
if($projectId === 'console') {
|
||||
if ($projectId === 'console') {
|
||||
return $console;
|
||||
}
|
||||
|
||||
|
|
@ -794,7 +832,7 @@ App::setResource('project', function($dbForConsole, $request, $console) {
|
|||
return $project;
|
||||
}, ['dbForConsole', 'request', 'console']);
|
||||
|
||||
App::setResource('console', function() {
|
||||
App::setResource('console', function () {
|
||||
return new Document([
|
||||
'$id' => 'console',
|
||||
'name' => 'Appwrite',
|
||||
|
|
@ -826,7 +864,7 @@ App::setResource('console', function() {
|
|||
]);
|
||||
}, []);
|
||||
|
||||
App::setResource('dbForProject', function($db, $cache, $project) {
|
||||
App::setResource('dbForProject', function ($db, $cache, $project) {
|
||||
$cache = new Cache(new RedisCache($cache));
|
||||
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
|
|
@ -836,7 +874,7 @@ App::setResource('dbForProject', function($db, $cache, $project) {
|
|||
return $database;
|
||||
}, ['db', 'cache', 'project']);
|
||||
|
||||
App::setResource('dbForConsole', function($db, $cache) {
|
||||
App::setResource('dbForConsole', function ($db, $cache) {
|
||||
$cache = new Cache(new RedisCache($cache));
|
||||
|
||||
$database = new Database(new MariaDB($db), $cache);
|
||||
|
|
@ -847,25 +885,27 @@ App::setResource('dbForConsole', function($db, $cache) {
|
|||
}, ['db', 'cache']);
|
||||
|
||||
|
||||
App::setResource('deviceLocal', function() {
|
||||
App::setResource('deviceLocal', function () {
|
||||
return new Local();
|
||||
});
|
||||
|
||||
App::setResource('deviceFiles', function($project) {
|
||||
App::setResource('deviceFiles', function ($project) {
|
||||
return getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceFunctions', function($project) {
|
||||
App::setResource('deviceFunctions', function ($project) {
|
||||
return getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
App::setResource('deviceBuilds', function($project) {
|
||||
App::setResource('deviceBuilds', function ($project) {
|
||||
return getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
|
||||
}, ['project']);
|
||||
|
||||
function getDevice($root): Device {
|
||||
function getDevice($root): Device
|
||||
{
|
||||
switch (App::getEnv('_APP_STORAGE_DEVICE', Storage::DEVICE_LOCAL)) {
|
||||
case Storage::DEVICE_LOCAL:default:
|
||||
case Storage::DEVICE_LOCAL:
|
||||
default:
|
||||
return new Local($root);
|
||||
case Storage::DEVICE_S3:
|
||||
$s3AccessKey = App::getEnv('_APP_STORAGE_S3_ACCESS_KEY', '');
|
||||
|
|
@ -905,7 +945,7 @@ function getDevice($root): Device {
|
|||
}
|
||||
}
|
||||
|
||||
App::setResource('mode', function($request) {
|
||||
App::setResource('mode', function ($request) {
|
||||
/** @var Appwrite\Utopia\Request $request */
|
||||
|
||||
/**
|
||||
|
|
@ -916,7 +956,7 @@ App::setResource('mode', function($request) {
|
|||
return $request->getParam('mode', $request->getHeader('x-appwrite-mode', APP_MODE_DEFAULT));
|
||||
}, ['request']);
|
||||
|
||||
App::setResource('geodb', function($register) {
|
||||
App::setResource('geodb', function ($register) {
|
||||
/** @var Utopia\Registry\Registry $register */
|
||||
return $register->get('geodb');
|
||||
}, ['register']);
|
||||
|
|
|
|||
|
|
@ -2,27 +2,28 @@
|
|||
|
||||
/**
|
||||
* Init
|
||||
*
|
||||
*
|
||||
* Inializes both Appwrite API entry point, queue workers, and CLI tasks.
|
||||
* Set configuration, framework resources, app constants
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
ini_set('display_errors', 1);
|
||||
ini_set('display_startup_errors', 1);
|
||||
error_reporting(E_ALL);
|
||||
|
||||
if (file_exists(__DIR__.'/../vendor/autoload.php')) {
|
||||
require __DIR__.'/../vendor/autoload.php';
|
||||
if (file_exists(__DIR__ . '/../vendor/autoload.php')) {
|
||||
require __DIR__ . '/../vendor/autoload.php';
|
||||
}
|
||||
|
||||
use Utopia\Preloader\Preloader;
|
||||
|
||||
include __DIR__.'/controllers/general.php';
|
||||
include __DIR__ . '/controllers/general.php';
|
||||
|
||||
$preloader = new Preloader();
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
realpath(__DIR__ . '/../vendor/composer'),
|
||||
realpath(__DIR__ . '/../vendor/amphp'),
|
||||
realpath(__DIR__ . '/../vendor/felixfbecker'),
|
||||
|
|
@ -34,8 +35,9 @@ foreach ([
|
|||
realpath(__DIR__ . '/../vendor/symfony'),
|
||||
realpath(__DIR__ . '/../vendor/mongodb'),
|
||||
realpath(__DIR__ . '/../vendor/utopia-php/websocket'), // TODO: remove workerman autoload
|
||||
] as $key => $value) {
|
||||
if($value !== false) {
|
||||
] as $key => $value
|
||||
) {
|
||||
if ($value !== false) {
|
||||
$preloader->ignore($value);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -129,7 +129,7 @@ function getDatabase(Registry &$register, string $namespace)
|
|||
$register->get('redisPool')->put($redis);
|
||||
}
|
||||
];
|
||||
};
|
||||
}
|
||||
|
||||
$server->onStart(function () use ($stats, $register, $containerId, &$statsDocument, $logError) {
|
||||
sleep(5); // wait for the initial database schema to be ready
|
||||
|
|
@ -187,7 +187,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
});
|
||||
|
||||
$server->onWorkerStart(function (int $workerId) use ($server, $register, $stats, $realtime, $logError) {
|
||||
Console::success('Worker ' . $workerId . ' started succefully');
|
||||
Console::success('Worker ' . $workerId . ' started successfully');
|
||||
|
||||
$attempts = 0;
|
||||
$start = time();
|
||||
|
|
@ -197,7 +197,6 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
* Sending current connections to project channels on the console project every 5 seconds.
|
||||
*/
|
||||
if ($realtime->hasSubscriber('console', 'role:member', 'project')) {
|
||||
|
||||
[$database, $returnDatabase] = getDatabase($register, '_console');
|
||||
|
||||
$payload = [];
|
||||
|
|
|
|||
|
|
@ -19,46 +19,41 @@ $cli
|
|||
/ \ ) __/ ) __/\ /\ / ) / )( )( ) _) _ )(( O )
|
||||
\_/\_/(__) (__) (_/\_)(__\_)(__) (__) (____)(_)(__)\__/ ");
|
||||
|
||||
Console::log("\n".'👩⚕️ Running '.APP_NAME.' Doctor for version '.App::getEnv('_APP_VERSION', 'UNKNOWN').' ...'."\n");
|
||||
Console::log("\n" . '👩⚕️ Running ' . APP_NAME . ' Doctor for version ' . App::getEnv('_APP_VERSION', 'UNKNOWN') . ' ...' . "\n");
|
||||
|
||||
Console::log('Checking for production best practices...');
|
||||
|
||||
$domain = new Domain(App::getEnv('_APP_DOMAIN'));
|
||||
|
||||
if(!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 Hostname has no public suffix ('.$domain->get().')');
|
||||
}
|
||||
else {
|
||||
Console::log('🟢 Hostname has a public suffix ('.$domain->get().')');
|
||||
if (!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 Hostname has no public suffix (' . $domain->get() . ')');
|
||||
} else {
|
||||
Console::log('🟢 Hostname has a public suffix (' . $domain->get() . ')');
|
||||
}
|
||||
|
||||
$domain = new Domain(App::getEnv('_APP_DOMAIN_TARGET'));
|
||||
|
||||
if(!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 CNAME target has no public suffix ('.$domain->get().')');
|
||||
}
|
||||
else {
|
||||
Console::log('🟢 CNAME target has a public suffix ('.$domain->get().')');
|
||||
if (!$domain->isKnown() || $domain->isTest()) {
|
||||
Console::log('🔴 CNAME target has no public suffix (' . $domain->get() . ')');
|
||||
} else {
|
||||
Console::log('🟢 CNAME target has a public suffix (' . $domain->get() . ')');
|
||||
}
|
||||
|
||||
if(App::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1'))) {
|
||||
if (App::getEnv('_APP_OPENSSL_KEY_V1') === 'your-secret-key' || empty(App::getEnv('_APP_OPENSSL_KEY_V1'))) {
|
||||
Console::log('🔴 Not using a unique secret key for encryption');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::log('🟢 Using a unique secret key for encryption');
|
||||
}
|
||||
|
||||
if(App::getEnv('_APP_ENV', 'development') !== 'production') {
|
||||
if (App::getEnv('_APP_ENV', 'development') !== 'production') {
|
||||
Console::log('🔴 App environment is set for development');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::log('🟢 App environment is set for production');
|
||||
}
|
||||
|
||||
if('enabled' !== App::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) {
|
||||
if ('enabled' !== App::getEnv('_APP_OPTIONS_ABUSE', 'disabled')) {
|
||||
Console::log('🔴 Abuse protection is disabled');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::log('🟢 Abuse protection is enabled');
|
||||
}
|
||||
|
||||
|
|
@ -66,20 +61,19 @@ $cli
|
|||
$authWhitelistEmails = App::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null);
|
||||
$authWhitelistIPs = App::getEnv('_APP_CONSOLE_WHITELIST_IPS', null);
|
||||
|
||||
if(empty($authWhitelistRoot)
|
||||
if (
|
||||
empty($authWhitelistRoot)
|
||||
&& empty($authWhitelistEmails)
|
||||
&& empty($authWhitelistIPs)
|
||||
) {
|
||||
Console::log('🔴 Console access limits are disabled');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::log('🟢 Console access limits are enabled');
|
||||
}
|
||||
|
||||
if('enabled' !== App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) {
|
||||
if ('enabled' !== App::getEnv('_APP_OPTIONS_FORCE_HTTPS', 'disabled')) {
|
||||
Console::log('🔴 HTTPS force option is disabled');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::log('🟢 HTTPS force option is enabled');
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +81,7 @@ $cli
|
|||
$providerName = App::getEnv('_APP_LOGGING_PROVIDER', '');
|
||||
$providerConfig = App::getEnv('_APP_LOGGING_CONFIG', '');
|
||||
|
||||
if(empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) {
|
||||
if (empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) {
|
||||
Console::log('🔴 Logging adapter is disabled');
|
||||
} else {
|
||||
Console::log('🟢 Logging adapter is enabled (' . $providerName . ')');
|
||||
|
|
@ -96,7 +90,7 @@ $cli
|
|||
\sleep(0.2);
|
||||
|
||||
try {
|
||||
Console::log("\n".'Checking connectivity...');
|
||||
Console::log("\n" . 'Checking connectivity...');
|
||||
} catch (\Throwable $th) {
|
||||
//throw $th;
|
||||
}
|
||||
|
|
@ -122,15 +116,16 @@ $cli
|
|||
Console::error('Cache............disconnected 👎');
|
||||
}
|
||||
|
||||
if(App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
|
||||
if (App::getEnv('_APP_STORAGE_ANTIVIRUS') === 'enabled') { // Check if scans are enabled
|
||||
try {
|
||||
$antivirus = new Network(App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310));
|
||||
$antivirus = new Network(
|
||||
App::getEnv('_APP_STORAGE_ANTIVIRUS_HOST', 'clamav'),
|
||||
(int) App::getEnv('_APP_STORAGE_ANTIVIRUS_PORT', 3310)
|
||||
);
|
||||
|
||||
if((@$antivirus->ping())) {
|
||||
if ((@$antivirus->ping())) {
|
||||
Console::success('Antivirus...........connected 👍');
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::error('Antivirus........disconnected 👎');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
|
|
@ -155,7 +150,7 @@ $cli
|
|||
$host = App::getEnv('_APP_STATSD_HOST', 'telegraf');
|
||||
$port = App::getEnv('_APP_STATSD_PORT', 8125);
|
||||
|
||||
if($fp = @\fsockopen('udp://'.$host, $port, $errCode, $errStr, 2)){
|
||||
if ($fp = @\fsockopen('udp://' . $host, $port, $errCode, $errStr, 2)) {
|
||||
Console::success('StatsD..............connected 👍');
|
||||
\fclose($fp);
|
||||
} else {
|
||||
|
|
@ -165,7 +160,7 @@ $cli
|
|||
$host = App::getEnv('_APP_INFLUXDB_HOST', '');
|
||||
$port = App::getEnv('_APP_INFLUXDB_PORT', '');
|
||||
|
||||
if($fp = @\fsockopen($host, $port, $errCode, $errStr, 2)){
|
||||
if ($fp = @\fsockopen($host, $port, $errCode, $errStr, 2)) {
|
||||
Console::success('InfluxDB............connected 👍');
|
||||
\fclose($fp);
|
||||
} else {
|
||||
|
|
@ -177,26 +172,26 @@ $cli
|
|||
Console::log('');
|
||||
Console::log('Checking volumes...');
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume) {
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
if (\is_readable($device->getRoot())) {
|
||||
Console::success('🟢 '.$key.' Volume is readable');
|
||||
}
|
||||
else {
|
||||
Console::error('🔴 '.$key.' Volume is unreadable');
|
||||
Console::success('🟢 ' . $key . ' Volume is readable');
|
||||
} else {
|
||||
Console::error('🔴 ' . $key . ' Volume is unreadable');
|
||||
}
|
||||
|
||||
if (\is_writable($device->getRoot())) {
|
||||
Console::success('🟢 '.$key.' Volume is writeable');
|
||||
}
|
||||
else {
|
||||
Console::error('🔴 '.$key.' Volume is unwriteable');
|
||||
Console::success('🟢 ' . $key . ' Volume is writeable');
|
||||
} else {
|
||||
Console::error('🔴 ' . $key . ' Volume is unwriteable');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -205,44 +200,44 @@ $cli
|
|||
Console::log('');
|
||||
Console::log('Checking disk space usage...');
|
||||
|
||||
foreach ([
|
||||
foreach (
|
||||
[
|
||||
'Uploads' => APP_STORAGE_UPLOADS,
|
||||
'Cache' => APP_STORAGE_CACHE,
|
||||
'Config' => APP_STORAGE_CONFIG,
|
||||
'Certs' => APP_STORAGE_CERTIFICATES
|
||||
] as $key => $volume) {
|
||||
] as $key => $volume
|
||||
) {
|
||||
$device = new Local($volume);
|
||||
|
||||
$percentage = (($device->getPartitionTotalSpace() - $device->getPartitionFreeSpace())
|
||||
/ $device->getPartitionTotalSpace()) * 100;
|
||||
|
||||
$message = $key.' Volume has '.Storage::human($device->getPartitionFreeSpace()) . ' free space ('.\round($percentage, 2).'% used)';
|
||||
$message = $key . ' Volume has ' . Storage::human($device->getPartitionFreeSpace()) . ' free space (' . \round($percentage, 2) . '% used)';
|
||||
|
||||
if ($percentage < 80) {
|
||||
Console::success('🟢 ' . $message);
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
Console::error('🔴 ' . $message);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if(App::isProduction()) {
|
||||
if (App::isProduction()) {
|
||||
Console::log('');
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost').'/v1/health/version'), true);
|
||||
$version = \json_decode(@\file_get_contents(App::getEnv('_APP_HOME', 'http://localhost') . '/v1/health/version'), true);
|
||||
|
||||
if ($version && isset($version['version'])) {
|
||||
if(\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) {
|
||||
Console::info('You are running the latest version of '.APP_NAME.'! 🥳');
|
||||
}
|
||||
else {
|
||||
Console::info('A new version ('.$version['version'].') is available! 🥳'."\n");
|
||||
if (\version_compare($version['version'], App::getEnv('_APP_VERSION', 'UNKNOWN')) === 0) {
|
||||
Console::info('You are running the latest version of ' . APP_NAME . '! 🥳');
|
||||
} else {
|
||||
Console::info('A new version (' . $version['version'] . ') is available! 🥳' . "\n");
|
||||
}
|
||||
} else {
|
||||
Console::error('Failed to check for a newer version'."\n");
|
||||
Console::error('Failed to check for a newer version' . "\n");
|
||||
}
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::error('Failed to check for a newer version'."\n");
|
||||
Console::error('Failed to check for a newer version' . "\n");
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ $cli
|
|||
->param('httpsPort', '', new Text(4), 'Server HTTPS port', true)
|
||||
->param('organization', 'appwrite', new Text(0), 'Docker Registry organization', true)
|
||||
->param('image', 'appwrite', new Text(0), 'Main appwrite docker image', true)
|
||||
->param('interactive','Y', new Text(1), 'Run an interactive session', true)
|
||||
->param('interactive', 'Y', new Text(1), 'Run an interactive session', true)
|
||||
->action(function ($httpPort, $httpsPort, $organization, $image, $interactive) {
|
||||
/**
|
||||
* 1. Start - DONE
|
||||
|
|
@ -31,7 +31,7 @@ $cli
|
|||
* 2.4 Ask for all required vars not given as CLI args and if in interactive mode
|
||||
* Otherwise, just use default vars. - DONE
|
||||
* 3. Ask user to backup important volumes, env vars, and SQL tables
|
||||
* In th future we can try and automate this for smaller/medium size setups
|
||||
* In th future we can try and automate this for smaller/medium size setups
|
||||
* 4. Drop new docker-compose.yml setup (located inside the container, no network dependencies with appwrite.io) - DONE
|
||||
* 5. Run docker-compose up -d - DONE
|
||||
* 6. Run data migration
|
||||
|
|
@ -48,8 +48,8 @@ $cli
|
|||
*/
|
||||
$analytics = new GoogleAnalytics('UA-26264668-9', uniqid('server.', true));
|
||||
|
||||
foreach($config as $category) {
|
||||
foreach($category['variables'] ?? [] as $var) {
|
||||
foreach ($config as $category) {
|
||||
foreach ($category['variables'] ?? [] as $var) {
|
||||
$vars[] = $var;
|
||||
}
|
||||
}
|
||||
|
|
@ -59,17 +59,17 @@ $cli
|
|||
// Create directory with write permissions
|
||||
if (null !== $path && !\file_exists(\dirname($path))) {
|
||||
if (!@\mkdir(\dirname($path), 0755, true)) {
|
||||
Console::error('Can\'t create directory '.\dirname($path));
|
||||
Console::error('Can\'t create directory ' . \dirname($path));
|
||||
Console::exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($path.'/docker-compose.yml');
|
||||
$data = @file_get_contents($path . '/docker-compose.yml');
|
||||
|
||||
if($data !== false) {
|
||||
if ($data !== false) {
|
||||
$time = \time();
|
||||
Console::info('Compose file found, creating backup: docker-compose.yml.'.$time.'.backup');
|
||||
file_put_contents($path.'/docker-compose.yml.'.$time.'.backup',$data);
|
||||
Console::info('Compose file found, creating backup: docker-compose.yml.' . $time . '.backup');
|
||||
file_put_contents($path . '/docker-compose.yml.' . $time . '.backup', $data);
|
||||
$compose = new Compose($data);
|
||||
$appwrite = $compose->getService('appwrite');
|
||||
$oldVersion = ($appwrite) ? $appwrite->getImageVersion() : null;
|
||||
|
|
@ -83,35 +83,39 @@ $cli
|
|||
Console::warning('Traefik not found. Falling back to default ports.');
|
||||
}
|
||||
|
||||
if($oldVersion) {
|
||||
foreach($compose->getServices() as $service) { // Fetch all env vars from previous compose file
|
||||
if(!$service) {
|
||||
if ($oldVersion) {
|
||||
foreach ($compose->getServices() as $service) { // Fetch all env vars from previous compose file
|
||||
if (!$service) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$env = $service->getEnvironment()->list();
|
||||
|
||||
foreach ($env as $key => $value) {
|
||||
if (is_null($value)) continue;
|
||||
foreach($vars as &$var) {
|
||||
if($var['name'] === $key) {
|
||||
if (is_null($value)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($vars as &$var) {
|
||||
if ($var['name'] === $key) {
|
||||
$var['default'] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$data = @file_get_contents($path.'/.env');
|
||||
$data = @file_get_contents($path . '/.env');
|
||||
|
||||
if($data !== false) { // Fetch all env vars from previous .env file
|
||||
Console::info('Env file found, creating backup: .env.'.$time.'.backup');
|
||||
file_put_contents($path.'/.env.'.$time.'.backup',$data);
|
||||
if ($data !== false) { // Fetch all env vars from previous .env file
|
||||
Console::info('Env file found, creating backup: .env.' . $time . '.backup');
|
||||
file_put_contents($path . '/.env.' . $time . '.backup', $data);
|
||||
$env = new Env($data);
|
||||
|
||||
foreach ($env->list() as $key => $value) {
|
||||
if (is_null($value)) continue;
|
||||
foreach($vars as &$var) {
|
||||
if($var['name'] === $key) {
|
||||
if (is_null($value)) {
|
||||
continue;
|
||||
}
|
||||
foreach ($vars as &$var) {
|
||||
if ($var['name'] === $key) {
|
||||
$var['default'] = $value;
|
||||
}
|
||||
}
|
||||
|
|
@ -119,60 +123,60 @@ $cli
|
|||
}
|
||||
|
||||
foreach ($ports as $key => $value) {
|
||||
if($value === $defaultHTTPPort) {
|
||||
if ($value === $defaultHTTPPort) {
|
||||
$defaultHTTPPort = $key;
|
||||
}
|
||||
|
||||
if($value === $defaultHTTPSPort) {
|
||||
if ($value === $defaultHTTPSPort) {
|
||||
$defaultHTTPSPort = $key;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(empty($httpPort)) {
|
||||
$httpPort = Console::confirm('Choose your server HTTP port: (default: '.$defaultHTTPPort.')');
|
||||
if (empty($httpPort)) {
|
||||
$httpPort = Console::confirm('Choose your server HTTP port: (default: ' . $defaultHTTPPort . ')');
|
||||
$httpPort = ($httpPort) ? $httpPort : $defaultHTTPPort;
|
||||
}
|
||||
|
||||
if(empty($httpsPort)) {
|
||||
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: '.$defaultHTTPSPort.')');
|
||||
if (empty($httpsPort)) {
|
||||
$httpsPort = Console::confirm('Choose your server HTTPS port: (default: ' . $defaultHTTPSPort . ')');
|
||||
$httpsPort = ($httpsPort) ? $httpsPort : $defaultHTTPSPort;
|
||||
}
|
||||
|
||||
$input = [];
|
||||
|
||||
foreach($vars as $key => $var) {
|
||||
if(!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
|
||||
if($data && $var['default'] !== null) {
|
||||
foreach ($vars as $key => $var) {
|
||||
if (!empty($var['filter']) && ($interactive !== 'Y' || !Console::isInteractive())) {
|
||||
if ($data && $var['default'] !== null) {
|
||||
$input[$var['name']] = $var['default'];
|
||||
continue;
|
||||
}
|
||||
|
||||
if($var['filter'] === 'token') {
|
||||
if ($var['filter'] === 'token') {
|
||||
$input[$var['name']] = Auth::tokenGenerator();
|
||||
continue;
|
||||
}
|
||||
|
||||
if($var['filter'] === 'password') {
|
||||
if ($var['filter'] === 'password') {
|
||||
$input[$var['name']] = Auth::passwordGenerator();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if(!$var['required'] || !Console::isInteractive() || $interactive !== 'Y') {
|
||||
if (!$var['required'] || !Console::isInteractive() || $interactive !== 'Y') {
|
||||
$input[$var['name']] = $var['default'];
|
||||
continue;
|
||||
}
|
||||
|
||||
$input[$var['name']] = Console::confirm($var['question'].' (default: \''.$var['default'].'\')');
|
||||
$input[$var['name']] = Console::confirm($var['question'] . ' (default: \'' . $var['default'] . '\')');
|
||||
|
||||
if(empty($input[$var['name']])) {
|
||||
if (empty($input[$var['name']])) {
|
||||
$input[$var['name']] = $var['default'];
|
||||
}
|
||||
}
|
||||
|
||||
$templateForCompose = new View(__DIR__.'/../views/install/compose.phtml');
|
||||
$templateForEnv = new View(__DIR__.'/../views/install/env.phtml');
|
||||
$templateForCompose = new View(__DIR__ . '/../views/install/compose.phtml');
|
||||
$templateForEnv = new View(__DIR__ . '/../views/install/env.phtml');
|
||||
|
||||
$templateForCompose
|
||||
->setParam('httpPort', $httpPort)
|
||||
|
|
@ -186,16 +190,16 @@ $cli
|
|||
->setParam('vars', $input)
|
||||
;
|
||||
|
||||
if(!file_put_contents($path.'/docker-compose.yml', $templateForCompose->render(false))) {
|
||||
if (!file_put_contents($path . '/docker-compose.yml', $templateForCompose->render(false))) {
|
||||
$message = 'Failed to save Docker Compose file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE.' - '.$message);
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
|
||||
if(!file_put_contents($path.'/.env', $templateForEnv->render(false))) {
|
||||
if (!file_put_contents($path . '/.env', $templateForEnv->render(false))) {
|
||||
$message = 'Failed to save environment variables file';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE.' - '.$message);
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::exit(1);
|
||||
}
|
||||
|
|
@ -205,8 +209,8 @@ $cli
|
|||
$stderr = '';
|
||||
|
||||
foreach ($input as $key => $value) {
|
||||
if($value) {
|
||||
$env .= $key.'='.\escapeshellarg($value).' ';
|
||||
if ($value) {
|
||||
$env .= $key . '=' . \escapeshellarg($value) . ' ';
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -216,13 +220,13 @@ $cli
|
|||
|
||||
if ($exit !== 0) {
|
||||
$message = 'Failed to install Appwrite dockers';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE.' - '.$message);
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::error($message);
|
||||
Console::error($stderr);
|
||||
Console::exit($exit);
|
||||
} else {
|
||||
$message = 'Appwrite installed successfully';
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE.' - '.$message);
|
||||
$analytics->createEvent('install/server', 'install', APP_VERSION_STABLE . ' - ' . $message);
|
||||
Console::success($message);
|
||||
}
|
||||
});
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ $cli
|
|||
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
|
||||
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
|
||||
|
||||
if(!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', 'latest'])) {
|
||||
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', 'latest'])) {
|
||||
throw new Exception('Unknown version given');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ $cli
|
|||
$response = new Response(new HttpResponse());
|
||||
$mocks = ($mode === 'mocks');
|
||||
|
||||
App::setResource('request', fn () => new Request);
|
||||
App::setResource('request', fn () => new Request());
|
||||
App::setResource('response', fn () => $response);
|
||||
App::setResource('db', fn () => $db);
|
||||
App::setResource('cache', fn () => $redis);
|
||||
|
|
@ -125,7 +125,6 @@ $cli
|
|||
|
||||
foreach (['swagger2', 'open-api3'] as $format) {
|
||||
foreach ($platforms as $platform) {
|
||||
|
||||
$routes = [];
|
||||
$models = [];
|
||||
$services = [];
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ use Utopia\Database\Validator\Authorization;
|
|||
* database.collections.{collectionId}.documents.delete
|
||||
*
|
||||
* Storage
|
||||
*
|
||||
*
|
||||
* storage.buckets.create
|
||||
* storage.buckets.read
|
||||
* storage.buckets.update
|
||||
|
|
@ -285,7 +285,7 @@ $cli
|
|||
try {
|
||||
$attempts++;
|
||||
$database = $client->selectDB('telegraf');
|
||||
if(in_array('telegraf', $client->listDatabases())) {
|
||||
if (in_array('telegraf', $client->listDatabases())) {
|
||||
break; // leave the do-while if successful
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
|
|
@ -386,7 +386,7 @@ $cli
|
|||
*/
|
||||
$now = date('d-m-Y H:i:s', time());
|
||||
Console::info("[{$now}] Aggregating database counters.");
|
||||
|
||||
|
||||
$latestProject = null;
|
||||
do { // Loop over all the projects
|
||||
$attempts = 0;
|
||||
|
|
@ -424,7 +424,6 @@ $cli
|
|||
$id = \md5($time . '_30m_storage.deployments.total'); //Construct unique id for each metric using time, period and metric
|
||||
$document = $dbForProject->getDocument('stats', $id);
|
||||
try {
|
||||
|
||||
if ($document->isEmpty()) {
|
||||
$dbForProject->createDocument('stats', new Document([
|
||||
'$id' => $id,
|
||||
|
|
@ -460,7 +459,7 @@ $cli
|
|||
$document->setAttribute('value', $deploymentsTotal)
|
||||
);
|
||||
}
|
||||
} catch(\Exception $e) {
|
||||
} catch (\Exception $e) {
|
||||
Console::warning("Failed to save data for project {$projectId} and metric storage.deployments.total: {$e->getMessage()}");
|
||||
Console::warning($e->getTraceAsString());
|
||||
}
|
||||
|
|
@ -614,7 +613,7 @@ $cli
|
|||
|
||||
// check if sum calculation is required
|
||||
$total = $subOptions['total'] ?? [];
|
||||
if(empty($total)) {
|
||||
if (empty($total)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -639,8 +638,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $total)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -656,10 +658,12 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $total));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $total)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
} while (!empty($parents));
|
||||
|
|
@ -734,8 +738,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -751,12 +758,15 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count)
|
||||
);
|
||||
}
|
||||
|
||||
// aggregate storage.total = storage.files.total + storage.deployments.total
|
||||
if($metricPrefix === 'storage' && $subCollection === 'files') {
|
||||
if ($metricPrefix === 'storage' && $subCollection === 'files') {
|
||||
$metric = 'storage.total';
|
||||
$time = (int) (floor(time() / 1800) * 1800); // Time rounded to nearest 30 minutes
|
||||
$id = \md5($time . '_30m_' . $metric); //Construct unique id for each metric using time, period and metric
|
||||
|
|
@ -771,8 +781,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count + $deploymentsTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count + $deploymentsTotal)
|
||||
);
|
||||
}
|
||||
|
||||
$time = (int) (floor(time() / 86400) * 86400); // Time rounded to nearest day
|
||||
|
|
@ -788,8 +801,11 @@ $cli
|
|||
'type' => 1,
|
||||
]));
|
||||
} else {
|
||||
$dbForProject->updateDocument('stats', $document->getId(),
|
||||
$document->setAttribute('value', $count + $deploymentsTotal));
|
||||
$dbForProject->updateDocument(
|
||||
'stats',
|
||||
$document->getId(),
|
||||
$document->setAttribute('value', $count + $deploymentsTotal)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -807,4 +823,4 @@ $cli
|
|||
|
||||
Console::info("[{$now}] Aggregation took {$loopTook} seconds");
|
||||
}, $interval);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,13 +13,13 @@ $cli
|
|||
$config = Config::getParam('variables', []);
|
||||
$vars = [];
|
||||
|
||||
foreach($config as $category) {
|
||||
foreach($category['variables'] ?? [] as $var) {
|
||||
foreach ($config as $category) {
|
||||
foreach ($category['variables'] ?? [] as $var) {
|
||||
$vars[] = $var;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($vars as $key => $value) {
|
||||
Console::log('- '.$value['name'].'='.App::getEnv($value['name'], ''));
|
||||
Console::log('- ' . $value['name'] . '=' . App::getEnv($value['name'], ''));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -703,7 +703,7 @@ sort($patterns);
|
|||
--functionId={{project-function.$id}} \
|
||||
--activate=true \
|
||||
--entrypoint='scriptFile' \
|
||||
--code='/myrepo/myfunction'" data-forms-code="bash" data-lang="bash" data-lang-label="Bash"></textarea>
|
||||
--code='.'" data-forms-code="bash" data-lang="bash" data-lang-label="Bash"></textarea>
|
||||
</div>
|
||||
|
||||
<p><b>PowerShell</b></p>
|
||||
|
|
@ -713,7 +713,7 @@ sort($patterns);
|
|||
--functionId={{project-function.$id}} `
|
||||
--activate=true `
|
||||
--entrypoint='scriptFile' `
|
||||
--code='/myrepo/myfunction'" data-forms-code="powershell" data-lang="powershell" data-lang-label="PowerShell"></textarea>
|
||||
--code='.'" data-forms-code="powershell" data-lang="powershell" data-lang-label="PowerShell"></textarea>
|
||||
</div>
|
||||
|
||||
<p>Learn more about <a href="https://appwrite.io/docs/server/functions#functionsCreateDeployment" target="_blank">creating deployments</a>, installing and using the <a href="https://appwrite.io/docs/command-line" target="_blank">Appwrite CLI</a>.</p>
|
||||
|
|
|
|||
|
|
@ -137,12 +137,12 @@
|
|||
data-analytics-label="Create Team Membership"
|
||||
data-service="teams.createMembership"
|
||||
data-event="submit"
|
||||
data-loading="Sending invitation, please wait..."
|
||||
data-loading="Adding user, please wait..."
|
||||
data-success="alert,trigger,reset"
|
||||
data-success-param-alert-text="Invitation sent successfully"
|
||||
data-success-param-alert-text="User added successfully"
|
||||
data-success-param-trigger-events="teams.createMembership"
|
||||
data-failure="alert"
|
||||
data-failure-param-alert-text="Failed to send invite"
|
||||
data-failure-param-alert-text="Failed to add user"
|
||||
data-failure-param-alert-classname="error">
|
||||
|
||||
<input name="teamId" id="team-teamId" type="hidden" data-ls-bind="{{team.$id}}">
|
||||
|
|
|
|||
|
|
@ -174,7 +174,7 @@ class BuildsV1 extends Worker
|
|||
$build->setAttribute('status', $response['status']);
|
||||
$build->setAttribute('outputPath', $response['outputPath']);
|
||||
$build->setAttribute('stderr', $response['stderr']);
|
||||
$build->setAttribute('stdout', $response['stdout']);
|
||||
$build->setAttribute('stdout', $response['response']);
|
||||
|
||||
Console::success("Build id: $buildId created");
|
||||
|
||||
|
|
@ -200,7 +200,7 @@ class BuildsV1 extends Worker
|
|||
} finally {
|
||||
$build = $dbForProject->updateDocument('builds', $buildId, $build);
|
||||
|
||||
/**
|
||||
/**
|
||||
* Send realtime Event
|
||||
*/
|
||||
$target = Realtime::fromPayload(
|
||||
|
|
|
|||
|
|
@ -18,10 +18,9 @@ Console::success(APP_NAME . ' certificates worker v1 has started');
|
|||
|
||||
class CertificatesV1 extends Worker
|
||||
{
|
||||
|
||||
/**
|
||||
* Database connection shared across all methods of this file
|
||||
*
|
||||
*
|
||||
* @var Database
|
||||
*/
|
||||
private Database $dbForConsole;
|
||||
|
|
@ -52,20 +51,20 @@ class CertificatesV1 extends Worker
|
|||
* 10. Create/Update our Storage with new Traefik config with new certificate paths
|
||||
* 11. Read certificate file and update 'renewDate' on certificate document
|
||||
* 12. Update 'issueDate' and 'attempts' on certificate
|
||||
*
|
||||
*
|
||||
* If at any point unexpected error occurs, program stops without applying changes to document, and error is thrown into worker
|
||||
*
|
||||
*
|
||||
* If code stops with expected error:
|
||||
* 1. 'log' attribute on document is updated with error message
|
||||
* 2. 'attempts' amount is increased
|
||||
* 3. Console log is shown
|
||||
* 4. Email is sent to security email
|
||||
*
|
||||
*
|
||||
* Unless unexpected error occurs, at the end, we:
|
||||
* 1. Update 'updated' attribute on document
|
||||
* 2. Save document to database
|
||||
* 3. Update all domains documents with current certificate ID
|
||||
*
|
||||
*
|
||||
* Note: Renewals are checked and scheduled from maintenence worker
|
||||
*/
|
||||
|
||||
|
|
@ -148,7 +147,7 @@ class CertificatesV1 extends Worker
|
|||
*
|
||||
* @param string $domain Domain name that certificate is for
|
||||
* @param Document $certificate Certificate document that we need to save
|
||||
*
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
private function saveCertificateDocument(string $domain, Document $certificate): void
|
||||
|
|
|
|||
|
|
@ -7,10 +7,10 @@ use Utopia\CLI\Console;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
|
||||
require_once __DIR__.'/../init.php';
|
||||
require_once __DIR__ . '/../init.php';
|
||||
|
||||
Console::title('Database V1 Worker');
|
||||
Console::success(APP_NAME.' database worker v1 has started'."\n");
|
||||
Console::success(APP_NAME . ' database worker v1 has started' . "\n");
|
||||
|
||||
class DatabaseV1 extends Worker
|
||||
{
|
||||
|
|
@ -27,11 +27,11 @@ class DatabaseV1 extends Worker
|
|||
$collection = new Document($this->args['collection'] ?? []);
|
||||
$document = new Document($this->args['document'] ?? []);
|
||||
|
||||
if($collection->isEmpty()) {
|
||||
if ($collection->isEmpty()) {
|
||||
throw new Exception('Missing collection');
|
||||
}
|
||||
|
||||
if($document->isEmpty()) {
|
||||
if ($document->isEmpty()) {
|
||||
throw new Exception('Missing document');
|
||||
}
|
||||
|
||||
|
|
@ -50,9 +50,9 @@ class DatabaseV1 extends Worker
|
|||
break;
|
||||
|
||||
default:
|
||||
Console::error('No database operation for type: '.$type);
|
||||
Console::error('No database operation for type: ' . $type);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Authorization::reset();
|
||||
}
|
||||
|
|
@ -94,7 +94,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForProject->createAttribute('collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
if (!$dbForProject->createAttribute('collection_' . $collection->getInternalId(), $key, $type, $size, $required, $default, $signed, $array, $format, $formatOptions, $filters)) {
|
||||
throw new Exception('Failed to create Attribute');
|
||||
}
|
||||
$dbForProject->updateDocument('attributes', $attribute->getId(), $attribute->setAttribute('status', 'available'));
|
||||
|
|
@ -151,7 +151,7 @@ class DatabaseV1 extends Worker
|
|||
// - failed: attribute was never created
|
||||
// - stuck: attribute was available but cannot be removed
|
||||
try {
|
||||
if($status !== 'failed' && !$dbForProject->deleteAttribute('collection_' . $collection->getInternalId(), $key)) {
|
||||
if ($status !== 'failed' && !$dbForProject->deleteAttribute('collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete Attribute');
|
||||
}
|
||||
$dbForProject->deleteDocument('attributes', $attribute->getId());
|
||||
|
|
@ -194,7 +194,7 @@ class DatabaseV1 extends Worker
|
|||
|
||||
if ($found !== false) {
|
||||
// If found, remove entry from attributes, lengths, and orders
|
||||
// array_values wraps array_diff to reindex array keys
|
||||
// array_values wraps array_diff to reindex array keys
|
||||
// when found attribute is removed from array
|
||||
$attributes = \array_values(\array_diff($attributes, [$attributes[$found]]));
|
||||
$lengths = \array_values(\array_diff($lengths, [$lengths[$found]]));
|
||||
|
|
@ -212,7 +212,8 @@ class DatabaseV1 extends Worker
|
|||
// Check if an index exists with the same attributes and orders
|
||||
$exists = false;
|
||||
foreach ($indexes as $existing) {
|
||||
if ($existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
|
||||
if (
|
||||
$existing->getAttribute('key') !== $index->getAttribute('key') // Ignore itself
|
||||
&& $existing->getAttribute('attributes') === $index->getAttribute('attributes')
|
||||
&& $existing->getAttribute('orders') === $index->getAttribute('orders')
|
||||
) {
|
||||
|
|
@ -221,7 +222,7 @@ class DatabaseV1 extends Worker
|
|||
}
|
||||
}
|
||||
|
||||
if ($exists) { // Delete the duplicate if created, else update in db
|
||||
if ($exists) { // Delete the duplicate if created, else update in db
|
||||
$this->deleteIndex($collection, $index, $projectId);
|
||||
} else {
|
||||
$dbForProject->updateDocument('indexes', $index->getId(), $index);
|
||||
|
|
@ -257,7 +258,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if(!$dbForProject->createIndex('collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) {
|
||||
if (!$dbForProject->createIndex('collection_' . $collection->getInternalId(), $key, $type, $attributes, $lengths, $orders)) {
|
||||
throw new Exception('Failed to create Index');
|
||||
}
|
||||
$dbForProject->updateDocument('indexes', $index->getId(), $index->setAttribute('status', 'available'));
|
||||
|
|
@ -307,7 +308,7 @@ class DatabaseV1 extends Worker
|
|||
$project = $dbForConsole->getDocument('projects', $projectId);
|
||||
|
||||
try {
|
||||
if($status !== 'failed' && !$dbForProject->deleteIndex('collection_' . $collection->getInternalId(), $key)) {
|
||||
if ($status !== 'failed' && !$dbForProject->deleteIndex('collection_' . $collection->getInternalId(), $key)) {
|
||||
throw new Exception('Failed to delete index');
|
||||
}
|
||||
$dbForProject->deleteDocument('indexes', $index->getId());
|
||||
|
|
|
|||
|
|
@ -358,7 +358,7 @@ class DeletesV1 extends Worker
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Delete Executions
|
||||
*/
|
||||
Console::info("Deleting executions for function " . $functionId);
|
||||
|
|
@ -523,7 +523,7 @@ class DeletesV1 extends Worker
|
|||
}
|
||||
|
||||
/**
|
||||
* @param Document $document certificates document
|
||||
* @param Document $document certificates document
|
||||
*/
|
||||
protected function deleteCertificates(Document $document): void
|
||||
{
|
||||
|
|
@ -574,8 +574,8 @@ class DeletesV1 extends Worker
|
|||
$dbForProject = $this->getProjectDB($projectId);
|
||||
$dbForProject->deleteCollection('bucket_' . $document->getInternalId());
|
||||
|
||||
$device = $this->getDevice(APP_STORAGE_UPLOADS.'/app-'.$projectId);
|
||||
|
||||
$device = $this->getDevice(APP_STORAGE_UPLOADS . '/app-' . $projectId);
|
||||
|
||||
$device->deletePath($document->getId());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@ Console::success(APP_NAME . ' mails worker v1 has started' . "\n");
|
|||
|
||||
class MailsV1 extends Worker
|
||||
{
|
||||
public function getName(): string {
|
||||
public function getName(): string
|
||||
{
|
||||
return "mails";
|
||||
}
|
||||
|
||||
|
|
@ -131,9 +132,9 @@ class MailsV1 extends Worker
|
|||
|
||||
/**
|
||||
* Returns a prefix from a mail type
|
||||
*
|
||||
*
|
||||
* @param $type
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getPrefix(string $type): string
|
||||
|
|
@ -156,10 +157,10 @@ class MailsV1 extends Worker
|
|||
|
||||
/**
|
||||
* Returns true if all the required terms in a locale exist. False otherwise
|
||||
*
|
||||
*
|
||||
* @param $locale
|
||||
* @param $prefix
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
protected function doesLocaleExist(Locale $locale, string $prefix): bool
|
||||
|
|
|
|||
|
|
@ -43,9 +43,11 @@ class WebhooksV1 extends Worker
|
|||
|
||||
protected function execute(array $events, string $payload, Document $webhook, Document $user, Document $project): void
|
||||
{
|
||||
$url = \rawurldecode($webhook->getAttribute('url'));
|
||||
$signatureKey = $webhook->getAttribute('signatureKey');
|
||||
$signature = base64_encode(hash_hmac('sha1', $url . $payload, $signatureKey, true));
|
||||
$httpUser = $webhook->getAttribute('httpUser');
|
||||
$httpPass = $webhook->getAttribute('httpPass');
|
||||
|
||||
$ch = \curl_init($webhook->getAttribute('url'));
|
||||
|
||||
\curl_setopt($ch, CURLOPT_CUSTOMREQUEST, 'POST');
|
||||
|
|
@ -68,7 +70,7 @@ class WebhooksV1 extends Worker
|
|||
'X-' . APP_NAME . '-Webhook-Name: ' . $webhook->getAttribute('name', ''),
|
||||
'X-' . APP_NAME . '-Webhook-User-Id: ' . $user->getId(),
|
||||
'X-' . APP_NAME . '-Webhook-Project-Id: ' . $project->getId(),
|
||||
'X-' . APP_NAME . '-Webhook-Signature: ' . $webhook->getAttribute('signature', 'not-yet-implemented'),
|
||||
'X-' . APP_NAME . '-Webhook-Signature: ' . $signature,
|
||||
]
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,8 @@
|
|||
"phpmailer/phpmailer": "6.6.0",
|
||||
"chillerlan/php-qrcode": "4.3.3",
|
||||
"adhocore/jwt": "1.1.2",
|
||||
"slickdeals/statsd": "3.1.0"
|
||||
"slickdeals/statsd": "3.1.0",
|
||||
"squizlabs/php_codesniffer": "^3.6"
|
||||
},
|
||||
"repositories": [
|
||||
{
|
||||
|
|
@ -74,8 +75,7 @@
|
|||
"appwrite/sdk-generator": "0.18.8",
|
||||
"phpunit/phpunit": "9.5.20",
|
||||
"swoole/ide-helper": "4.8.9",
|
||||
"textalk/websocket": "1.5.7",
|
||||
"vimeo/psalm": "4.13.1"
|
||||
"textalk/websocket": "1.5.7"
|
||||
},
|
||||
"provide": {
|
||||
"ext-phpiredis": "*"
|
||||
|
|
|
|||
1407
composer.lock
generated
1407
composer.lock
generated
File diff suppressed because it is too large
Load diff
|
|
@ -101,7 +101,6 @@ services:
|
|||
- appwrite-certificates:/storage/certificates:rw
|
||||
- appwrite-functions:/storage/functions:rw
|
||||
- ./phpunit.xml:/usr/src/code/phpunit.xml
|
||||
- ./psalm.xml:/usr/src/code/psalm.xml
|
||||
- ./tests:/usr/src/code/tests
|
||||
- ./app:/usr/src/code/app
|
||||
# - ./vendor:/usr/src/code/vendor
|
||||
|
|
|
|||
|
|
@ -7,11 +7,11 @@ Once you add the dependencies, its extremely easy to get started with the SDK; A
|
|||
import 'package:dart_appwrite/dart_appwrite.dart';
|
||||
|
||||
void main() async {
|
||||
Client client = Client();
|
||||
Client client = Client()
|
||||
.setEndpoint('http://[HOSTNAME_OR_IP]/v1') // Make sure your endpoint is accessible
|
||||
.setProject('5ff3379a01d25') // Your project ID
|
||||
.setKey('cd868c7af8bdc893b4...93b7535db89')
|
||||
.setSelfSigned() // Use only on dev mode with a self-signed SSL cert
|
||||
.setSelfSigned(); // Use only on dev mode with a self-signed SSL cert
|
||||
|
||||
Users users = Users(client);
|
||||
|
||||
|
|
|
|||
|
|
@ -10,11 +10,14 @@ If you are building your Flutter application for multiple devices, you have to f
|
|||
### Android
|
||||
For **Android** first add your app <u>name</u> and <u>package name</u>, Your package name is generally the **applicationId** in your app-level <a href="https://github.com/appwrite/playground-for-flutter/blob/0fdbdff98384fff940ed0b1e08cf14cfe3a2be3e/android/app/build.gradle#L41" target="_blank" rel="noopener">build.gradle</a> file. By registering your new app platform, you are allowing your app to communicate with the Appwrite API.
|
||||
|
||||
In order to capture the Appwrite OAuth callback url, the following activity needs to be added to your [AndroidManifest.xml](https://github.com/appwrite/playground-for-flutter/blob/master/android/app/src/main/AndroidManifest.xml). Be sure to replace the **[PROJECT_ID]** string with your actual Appwrite project ID. You can find your Appwrite project ID in your project settings screen in the console.
|
||||
In order to capture the Appwrite OAuth callback url, the following activity needs to be added inside the `<application>` tag, along side the existing `<activity>` tags in your [AndroidManifest.xml](https://github.com/appwrite/playground-for-flutter/blob/master/android/app/src/main/AndroidManifest.xml). Be sure to replace the **[PROJECT_ID]** string with your actual Appwrite project ID. You can find your Appwrite project ID in your project settings screen in the console.
|
||||
|
||||
```xml
|
||||
<manifest>
|
||||
<application>
|
||||
<manifest ...>
|
||||
....
|
||||
<application ...>
|
||||
....
|
||||
<!-- Add this inside the <application> tag, along side the existing <activity> tags -->
|
||||
<activity android:name="com.linusu.flutter_web_auth.CallbackActivity" >
|
||||
<intent-filter android:label="flutter_web_auth">
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
|
|
|||
|
|
@ -4,4 +4,6 @@ Files are managed using buckets. Storage buckets are similar to Collections we h
|
|||
|
||||
Using Appwrite permissions architecture, you can assign read or write access to each bucket or file in your project for either a specific user, team, user role, or even grant it with public access (`role:all`). You can learn more about [how Appwrite handles permissions and access control](/docs/permissions).
|
||||
|
||||
The preview endpoint allows you to generate preview images for your files. Using the preview endpoint, you can also manipulate the resulting image so that it will fit perfectly inside your app in terms of dimensions, file size, and style. The preview endpoint also allows you to change the resulting image file format for better compression or image quality for better delivery over the network.
|
||||
The preview endpoint allows you to generate preview images for your files. Using the preview endpoint, you can also manipulate the resulting image so that it will fit perfectly inside your app in terms of dimensions, file size, and style. The preview endpoint also allows you to change the resulting image file format for better compression or image quality for better delivery over the network.
|
||||
|
||||
The maximum file size allowed for file upload is controlled by the `_APP_STORAGE_LIMIT` environment variable, which defaults to 30 MB. See [Environment Variables](/docs/environment-variables#storage) for more information.
|
||||
|
|
|
|||
22
phpcs.xml
Normal file
22
phpcs.xml
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
<?xml version="1.0"?>
|
||||
<ruleset name="Appwrite Standard" namespace="Appwrite">
|
||||
<rule ref="PSR12"/>
|
||||
<file>./app</file>
|
||||
<file>./src</file>
|
||||
<file>./tests</file>
|
||||
<ini name="memory_limit" value="4096M"/>
|
||||
<!-- Exclude SDK's for performance reasons -->
|
||||
<exclude-pattern>./app/sdks</exclude-pattern>
|
||||
<!-- Ignore max line width -->
|
||||
<rule ref="Generic.Files.LineLength">
|
||||
<exclude-pattern>*</exclude-pattern>
|
||||
</rule>
|
||||
<!-- Allow Side Effects on root level of files -->
|
||||
<rule ref="PSR1.Files.SideEffects.FoundWithSymbols">
|
||||
<exclude-pattern>*</exclude-pattern>
|
||||
</rule>
|
||||
<!-- Exclude namespace check for ./app directory -->
|
||||
<rule ref="PSR1.Classes.ClassDeclaration.MissingNamespace">
|
||||
<exclude-pattern>./app</exclude-pattern>
|
||||
</rule>
|
||||
</ruleset>
|
||||
|
|
@ -6,7 +6,7 @@
|
|||
convertNoticesToExceptions="true"
|
||||
convertWarningsToExceptions="true"
|
||||
processIsolation="false"
|
||||
stopOnFailure="true"
|
||||
stopOnFailure="false"
|
||||
>
|
||||
<extensions>
|
||||
<extension class="Appwrite\Tests\TestHook" />
|
||||
|
|
@ -35,4 +35,4 @@
|
|||
<file>./tests/e2e/Services/Functions/FunctionsCustomClientTest.php</file>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
</phpunit>
|
||||
|
|
|
|||
15
psalm.xml
15
psalm.xml
|
|
@ -1,15 +0,0 @@
|
|||
<?xml version="1.0"?>
|
||||
<psalm
|
||||
errorLevel="3"
|
||||
resolveFromConfigFile="true"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xmlns="https://getpsalm.org/schema/config"
|
||||
xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
|
||||
>
|
||||
<projectFiles>
|
||||
<directory name="src" />
|
||||
<ignoreFiles>
|
||||
<directory name="vendor" />
|
||||
</ignoreFiles>
|
||||
</projectFiles>
|
||||
</psalm>
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 5 KiB |
|
|
@ -10,38 +10,38 @@ class Auth
|
|||
/**
|
||||
* User Roles.
|
||||
*/
|
||||
const USER_ROLE_ALL = 'all';
|
||||
const USER_ROLE_GUEST = 'guest';
|
||||
const USER_ROLE_MEMBER = 'member';
|
||||
const USER_ROLE_ADMIN = 'admin';
|
||||
const USER_ROLE_DEVELOPER = 'developer';
|
||||
const USER_ROLE_OWNER = 'owner';
|
||||
const USER_ROLE_APP = 'app';
|
||||
const USER_ROLE_SYSTEM = 'system';
|
||||
public const USER_ROLE_ALL = 'all';
|
||||
public const USER_ROLE_GUEST = 'guest';
|
||||
public const USER_ROLE_MEMBER = 'member';
|
||||
public const USER_ROLE_ADMIN = 'admin';
|
||||
public const USER_ROLE_DEVELOPER = 'developer';
|
||||
public const USER_ROLE_OWNER = 'owner';
|
||||
public const USER_ROLE_APP = 'app';
|
||||
public const USER_ROLE_SYSTEM = 'system';
|
||||
|
||||
/**
|
||||
* Token Types.
|
||||
*/
|
||||
const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
const TOKEN_TYPE_VERIFICATION = 2;
|
||||
const TOKEN_TYPE_RECOVERY = 3;
|
||||
const TOKEN_TYPE_INVITE = 4;
|
||||
const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
public const TOKEN_TYPE_LOGIN = 1; // Deprecated
|
||||
public const TOKEN_TYPE_VERIFICATION = 2;
|
||||
public const TOKEN_TYPE_RECOVERY = 3;
|
||||
public const TOKEN_TYPE_INVITE = 4;
|
||||
public const TOKEN_TYPE_MAGIC_URL = 5;
|
||||
|
||||
/**
|
||||
* Session Providers.
|
||||
*/
|
||||
const SESSION_PROVIDER_EMAIL = 'email';
|
||||
const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
public const SESSION_PROVIDER_EMAIL = 'email';
|
||||
public const SESSION_PROVIDER_ANONYMOUS = 'anonymous';
|
||||
public const SESSION_PROVIDER_MAGIC_URL = 'magic-url';
|
||||
|
||||
/**
|
||||
* Token Expiration times.
|
||||
*/
|
||||
const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
const TOKEN_EXPIRATION_CONFIRM = 3600 * 24 * 7; /* 7 days */
|
||||
public const TOKEN_EXPIRATION_LOGIN_LONG = 31536000; /* 1 year */
|
||||
public const TOKEN_EXPIRATION_LOGIN_SHORT = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_RECOVERY = 3600; /* 1 hour */
|
||||
public const TOKEN_EXPIRATION_CONFIRM = 3600 * 24 * 7; /* 7 days */
|
||||
|
||||
/**
|
||||
* @var string
|
||||
|
|
@ -163,7 +163,7 @@ class Auth
|
|||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function passwordGenerator(int $length = 20):string
|
||||
public static function passwordGenerator(int $length = 20): string
|
||||
{
|
||||
return \bin2hex(\random_bytes($length));
|
||||
}
|
||||
|
|
@ -179,7 +179,7 @@ class Auth
|
|||
*
|
||||
* @throws \Exception
|
||||
*/
|
||||
public static function tokenGenerator(int $length = 128):string
|
||||
public static function tokenGenerator(int $length = 128): string
|
||||
{
|
||||
return \bin2hex(\random_bytes($length));
|
||||
}
|
||||
|
|
@ -196,12 +196,14 @@ class Auth
|
|||
public static function tokenVerify(array $tokens, int $type, string $secret)
|
||||
{
|
||||
foreach ($tokens as $token) { /** @var Document $token */
|
||||
if ($token->isSet('type') &&
|
||||
if (
|
||||
$token->isSet('type') &&
|
||||
$token->isSet('secret') &&
|
||||
$token->isSet('expire') &&
|
||||
$token->getAttribute('type') == $type &&
|
||||
$token->getAttribute('secret') === self::hash($secret) &&
|
||||
$token->getAttribute('expire') >= \time()) {
|
||||
$token->getAttribute('expire') >= \time()
|
||||
) {
|
||||
return (string)$token->getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -220,11 +222,13 @@ class Auth
|
|||
public static function sessionVerify(array $sessions, string $secret)
|
||||
{
|
||||
foreach ($sessions as $session) { /** @var Document $session */
|
||||
if ($session->isSet('secret') &&
|
||||
if (
|
||||
$session->isSet('secret') &&
|
||||
$session->isSet('expire') &&
|
||||
$session->isSet('provider') &&
|
||||
$session->getAttribute('secret') === self::hash($secret) &&
|
||||
$session->getAttribute('expire') >= \time()) {
|
||||
$session->getAttribute('expire') >= \time()
|
||||
) {
|
||||
return (string)$session->getId();
|
||||
}
|
||||
}
|
||||
|
|
@ -242,9 +246,9 @@ class Auth
|
|||
public static function isPrivilegedUser(array $roles): bool
|
||||
{
|
||||
if (
|
||||
in_array('role:'.self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:'.self::USER_ROLE_ADMIN, $roles)
|
||||
in_array('role:' . self::USER_ROLE_OWNER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_DEVELOPER, $roles) ||
|
||||
in_array('role:' . self::USER_ROLE_ADMIN, $roles)
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
|
@ -261,7 +265,7 @@ class Auth
|
|||
*/
|
||||
public static function isAppUser(array $roles): bool
|
||||
{
|
||||
if (in_array('role:'.self::USER_ROLE_APP, $roles)) {
|
||||
if (in_array('role:' . self::USER_ROLE_APP, $roles)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -280,10 +284,10 @@ class Auth
|
|||
|
||||
if (!self::isPrivilegedUser(Authorization::getRoles()) && !self::isAppUser(Authorization::getRoles())) {
|
||||
if ($user->getId()) {
|
||||
$roles[] = 'user:'.$user->getId();
|
||||
$roles[] = 'role:'.Auth::USER_ROLE_MEMBER;
|
||||
$roles[] = 'user:' . $user->getId();
|
||||
$roles[] = 'role:' . Auth::USER_ROLE_MEMBER;
|
||||
} else {
|
||||
return ['role:'.Auth::USER_ROLE_GUEST];
|
||||
return ['role:' . Auth::USER_ROLE_GUEST];
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -82,9 +82,9 @@ abstract class OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
abstract public function isEmailVerified(string $accessToken): bool;
|
||||
|
|
@ -189,7 +189,7 @@ abstract class OAuth2
|
|||
\curl_setopt($ch, CURLOPT_POSTFIELDS, $payload);
|
||||
}
|
||||
|
||||
$headers[] = 'Content-length: '.\strlen($payload);
|
||||
$headers[] = 'Content-length: ' . \strlen($payload);
|
||||
\curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
|
||||
|
||||
// Send the request & save response to $response
|
||||
|
|
|
|||
|
|
@ -140,11 +140,11 @@ class Amazon extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Amazon sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -137,11 +137,11 @@ class Apple extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://developer.apple.com/forums/thread/121411
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -132,11 +132,11 @@ class Auth0 extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://auth0.com/docs/api/authentication?javascript#user-profile
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
@ -180,7 +180,7 @@ class Auth0 extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
|
|
@ -192,7 +192,7 @@ class Auth0 extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Auth0 Domain from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuth0Domain(): string
|
||||
|
|
@ -204,7 +204,7 @@ class Auth0 extends OAuth2
|
|||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
|
|
|
|||
|
|
@ -124,9 +124,9 @@ class Bitbucket extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use Appwrite\Auth\OAuth2;
|
|||
|
||||
class Bitly extends OAuth2
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -149,11 +148,11 @@ class Bitly extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://dev.bitly.com/api-reference#getUser
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -141,11 +141,11 @@ class Box extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Box sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ class Discord extends OAuth2
|
|||
* @var array
|
||||
*/
|
||||
protected array $user = [];
|
||||
|
||||
|
||||
/**
|
||||
* @var array
|
||||
*/
|
||||
|
|
@ -45,7 +45,7 @@ class Discord extends OAuth2
|
|||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
$url = $this->endpoint . '/oauth2/authorize?'.
|
||||
$url = $this->endpoint . '/oauth2/authorize?' .
|
||||
\http_build_query([
|
||||
'response_type' => 'code',
|
||||
'client_id' => $this->appID,
|
||||
|
|
@ -64,7 +64,7 @@ class Discord extends OAuth2
|
|||
*/
|
||||
protected function getTokens(string $code): array
|
||||
{
|
||||
if(empty($this->tokens)) {
|
||||
if (empty($this->tokens)) {
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
$this->endpoint . '/oauth2/token',
|
||||
|
|
@ -88,7 +88,7 @@ class Discord extends OAuth2
|
|||
*
|
||||
* @return array
|
||||
*/
|
||||
public function refreshTokens(string $refreshToken):array
|
||||
public function refreshTokens(string $refreshToken): array
|
||||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'POST',
|
||||
|
|
@ -102,7 +102,7 @@ class Discord extends OAuth2
|
|||
])
|
||||
), true);
|
||||
|
||||
if(empty($this->tokens['refresh_token'])) {
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
}
|
||||
|
||||
|
|
@ -135,11 +135,11 @@ class Discord extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://discord.com/developers/docs/resources/user
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
@ -176,7 +176,7 @@ class Discord extends OAuth2
|
|||
$user = $this->request(
|
||||
'GET',
|
||||
$this->endpoint . '/users/@me',
|
||||
['Authorization: Bearer '.\urlencode($accessToken)]
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
);
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -125,11 +125,11 @@ class Dropbox extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://www.dropbox.com/developers/documentation/http/documentation#users-get_current_account
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -122,11 +122,11 @@ class Facebook extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Facebook sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -128,11 +128,11 @@ class Github extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://docs.github.com/en/rest/users/emails#list-email-addresses-for-the-authenticated-user
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -125,11 +125,11 @@ class Gitlab extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://docs.gitlab.com/ee/api/users.html#list-current-user-for-normal-users
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -131,11 +131,11 @@ class Google extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://www.oauth.com/oauth2-servers/signing-in-with-google/verifying-the-user-info/
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -137,11 +137,11 @@ class Linkedin extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Linkedin sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -131,11 +131,11 @@ class Microsoft extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Microsoft sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
@ -175,7 +175,7 @@ class Microsoft extends OAuth2
|
|||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
|
|
@ -190,7 +190,7 @@ class Microsoft extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
|
|
@ -202,7 +202,7 @@ class Microsoft extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Tenant Id from the JSON stored in appSecret. Defaults to 'common' as a fallback
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getTenantID(): string
|
||||
|
|
|
|||
|
|
@ -124,9 +124,9 @@ class Mock extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -128,11 +128,11 @@ class Notion extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Notion sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -132,11 +132,11 @@ class Okta extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://developer.okta.com/docs/reference/api/oidc/#userinfo
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
@ -180,7 +180,7 @@ class Okta extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Client Secret from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getClientSecret(): string
|
||||
|
|
@ -192,7 +192,7 @@ class Okta extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Okta Domain from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getOktaDomain(): string
|
||||
|
|
@ -204,7 +204,7 @@ class Okta extends OAuth2
|
|||
|
||||
/**
|
||||
* Extracts the Okta Authorization Server ID from the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
protected function getAuthorizationServerId(): string
|
||||
|
|
@ -216,7 +216,7 @@ class Okta extends OAuth2
|
|||
|
||||
/**
|
||||
* Decode the JSON stored in appSecret
|
||||
*
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
protected function getAppSecret(): array
|
||||
|
|
|
|||
|
|
@ -158,11 +158,11 @@ class Paypal extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://developer.paypal.com/docs/api/identity/v1/#userinfo_get
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -142,11 +142,11 @@ class Salesforce extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://help.salesforce.com/s/articleView?id=sf.remoteaccess_using_userinfo_endpoint.htm&type=5
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -121,13 +121,13 @@ class Slack extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Slack sign up process
|
||||
*
|
||||
*
|
||||
* @link https://slack.com/help/articles/207262907-Change-your-email-address
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
@ -151,7 +151,7 @@ class Slack extends OAuth2
|
|||
|
||||
/**
|
||||
* @link https://api.slack.com/methods/users.identity
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
* @return array
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use Appwrite\Auth\OAuth2;
|
|||
|
||||
class Spotify extends OAuth2
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -135,13 +134,13 @@ class Spotify extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
* Spotify does not assure that the email is verified
|
||||
*
|
||||
*
|
||||
* Spotify does not assure that the email is verified
|
||||
*
|
||||
* @link https://developer.spotify.com/documentation/web-api/reference/#/operations/get-current-users-profile
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -138,11 +138,11 @@ class Stripe extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Stripe sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ use Appwrite\Auth\OAuth2;
|
|||
|
||||
class Tradeshift extends OAuth2
|
||||
{
|
||||
const TRADESHIFT_SANDBOX_API_DOMAIN = 'api-sandbox.tradeshift.com';
|
||||
const TRADESHIFT_API_DOMAIN = 'api.tradeshift.com';
|
||||
public const TRADESHIFT_SANDBOX_API_DOMAIN = 'api-sandbox.tradeshift.com';
|
||||
public const TRADESHIFT_API_DOMAIN = 'api.tradeshift.com';
|
||||
|
||||
private array $apiDomain = [
|
||||
'sandbox' => self::TRADESHIFT_SANDBOX_API_DOMAIN,
|
||||
|
|
@ -42,7 +42,7 @@ class Tradeshift extends OAuth2
|
|||
/**
|
||||
* @var array
|
||||
*/
|
||||
protected array$scopes = [
|
||||
protected array $scopes = [
|
||||
'openid',
|
||||
'offline',
|
||||
];
|
||||
|
|
@ -145,11 +145,11 @@ class Tradeshift extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Tradeshift sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use Appwrite\Auth\OAuth2;
|
|||
|
||||
class Twitch extends OAuth2
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -134,13 +133,13 @@ class Twitch extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified
|
||||
*
|
||||
*
|
||||
* @link https://dev.twitch.tv/docs/api/reference#get-users
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -129,11 +129,11 @@ class WordPress extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://developer.wordpress.com/docs/api/1.1/get/me/
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ use Appwrite\Auth\OAuth2;
|
|||
|
||||
class Yahoo extends OAuth2
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
|
|
@ -155,11 +154,11 @@ class Yahoo extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Yahoo sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -124,11 +124,11 @@ class Yammer extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* If present, the email is verified. This was verfied through a manual Yammer sign up process
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -138,9 +138,9 @@ class Yandex extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ class Zoom extends OAuth2
|
|||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'user_profile'
|
||||
'user_info:read'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -130,11 +130,11 @@ class Zoom extends OAuth2
|
|||
|
||||
/**
|
||||
* Check if the OAuth email is verified
|
||||
*
|
||||
*
|
||||
* @link https://marketplace.zoom.us/docs/api-reference/zoom-api/methods/#operation/user
|
||||
*
|
||||
*
|
||||
* @param string $accessToken
|
||||
*
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isEmailVerified(string $accessToken): bool
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue