mirror of
https://github.com/appwrite/appwrite
synced 2026-05-15 13:08:31 +00:00
Merge branch 'main' of https://github.com/appwrite/appwrite into feat-ssr
This commit is contained in:
commit
b4773a7fbd
13 changed files with 78 additions and 45 deletions
2
.gitmodules
vendored
2
.gitmodules
vendored
|
|
@ -1,4 +1,4 @@
|
|||
[submodule "app/console"]
|
||||
path = app/console
|
||||
url = https://github.com/appwrite/console
|
||||
branch = 3.2.3
|
||||
branch = 3.2.4
|
||||
|
|
|
|||
|
|
@ -1,3 +1,8 @@
|
|||
# Version 1.4.7
|
||||
|
||||
## Fixes
|
||||
- Fix missing body in async function execution in [#6988](https://github.com/appwrite/appwrite/pull/6988)
|
||||
|
||||
# Version 1.4.6
|
||||
|
||||
## Changes
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ $ git push origin [name_of_your_new_branch]
|
|||
|
||||
To set up a working **development environment**, just fork the project git repository and install the backend and frontend dependencies using the proper package manager and create run the docker-compose stack.
|
||||
|
||||
> If you just want to install Appwrite for day-to-day use and not as a contributor, you can reference the [installation guide](https://github.com/appwrite/appwrite#installation), the [getting started guide](https://appwrite.io/docs/getting-started-for-web), or the main [README](README.md) file.
|
||||
> If you just want to install Appwrite for day-to-day use and not as a contributor, you can reference the [installation guide](https://github.com/appwrite/appwrite#installation), the [getting started guide](https://appwrite.io/docs/quick-starts), or the main [README](README.md) file.
|
||||
|
||||
```bash
|
||||
git clone git@github.com:[YOUR_FORK_HERE]/appwrite.git
|
||||
|
|
@ -454,7 +454,7 @@ From time to time, our team will add tutorials that will help contributors find
|
|||
|
||||
- [Adding Support for a New OAuth2 Provider](./docs/tutorials/add-oauth2-provider.md)
|
||||
- [Appwrite Environment Variables](./docs/tutorials/add-environment-variable.md)
|
||||
- [Running in Production](https://appwrite.io/docs/production)
|
||||
- [Running in Production](https://appwrite.io/docs/advanced/self-hosting/production)
|
||||
- [Adding Storage Adapter](./docs/tutorials/add-storage-adapter.md)
|
||||
|
||||
## Other Ways to Help
|
||||
|
|
|
|||
|
|
@ -66,7 +66,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -78,7 +78,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -88,7 +88,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
|
||||
|
|
|
|||
|
|
@ -76,7 +76,7 @@ docker run -it --rm \
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
### Windows
|
||||
|
|
@ -88,7 +88,7 @@ docker run -it --rm ^
|
|||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
#### PowerShell
|
||||
|
|
@ -98,7 +98,7 @@ docker run -it --rm `
|
|||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.4.6
|
||||
appwrite/appwrite:1.4.7
|
||||
```
|
||||
|
||||
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
|
||||
|
|
|
|||
|
|
@ -86,7 +86,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' => 426,
|
||||
],
|
||||
Exception::GENERAL_CODES_DISABLED => [
|
||||
'name' => Exception::GENERAL_CODES_DISABLED,
|
||||
|
|
@ -732,4 +732,21 @@ return [
|
|||
'description' => 'Migration is already in progress. You can check the status of the migration in your Appwrite Console\'s "Settings" > "Migrations".',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Realtime */
|
||||
Exception::REALTIME_MESSAGE_FORMAT_INVALID => [
|
||||
'name' => Exception::REALTIME_MESSAGE_FORMAT_INVALID,
|
||||
'description' => 'Message format is not valid.',
|
||||
'code' => 1003,
|
||||
],
|
||||
Exception::REALTIME_POLICY_VIOLATION => [
|
||||
'name' => Exception::REALTIME_POLICY_VIOLATION,
|
||||
'description' => 'Policy violation.',
|
||||
'code' => 1008,
|
||||
],
|
||||
Exception::REALTIME_TOO_MANY_MESSAGES => [
|
||||
'name' => Exception::REALTIME_TOO_MANY_MESSAGES,
|
||||
'description' => 'Too many messages.',
|
||||
'code' => 1013,
|
||||
],
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
Subproject commit e9657389879c8d76a9b3a0d3486c1d86f43c3bb9
|
||||
Subproject commit fe835e50328ed80f67c66d2d449c0f7b51ade544
|
||||
|
|
@ -101,9 +101,9 @@ App::post('/v1/projects')
|
|||
|
||||
$databases = Config::getParam('pools-database', []);
|
||||
$databaseSelfHosted = 'database_db_fra1_self_hosted_0_0';
|
||||
$index = array_search($databaseSelfHosted, $databases);
|
||||
if ($index !== false) {
|
||||
unset($databases[$index]);
|
||||
$selfHostedIndex = array_search($databaseSelfHosted, $databases);
|
||||
if ($selfHostedIndex !== false) {
|
||||
unset($databases[$selfHostedIndex]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -179,10 +179,10 @@ App::post('/v1/projects')
|
|||
* Update database with self-managed db every $mod projects
|
||||
*/
|
||||
$mod = 20;
|
||||
$index = in_array($databaseSelfHosted, $databases);
|
||||
if ($project->getInternalId() % $mod === 0 && $index !== false) {
|
||||
$project->setAttribute('database', $databaseSelfHosted);
|
||||
$dbForConsole->updateDocument('projects', $project);
|
||||
if ($project->getInternalId() % $mod === 0 && $selfHostedIndex !== false) {
|
||||
$database = $databaseSelfHosted;
|
||||
$project->setAttribute('database', $database);
|
||||
$dbForConsole->updateDocument('projects', $project->getId(), $project);
|
||||
}
|
||||
|
||||
$dbForProject = new Database($pools->get($database)->pop()->getResource(), $cache);
|
||||
|
|
|
|||
|
|
@ -110,7 +110,7 @@ const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
|
|||
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
|
||||
const APP_CACHE_BUSTER = 514;
|
||||
const APP_VERSION_STABLE = '1.4.6';
|
||||
const APP_VERSION_STABLE = '1.4.7';
|
||||
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
|
||||
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
|
||||
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
<?php
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
use Appwrite\Network\Validator\Origin;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Swoole\Http\Response as SwooleResponse;
|
||||
|
|
@ -20,7 +22,6 @@ use Utopia\Database\DateTime;
|
|||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Utopia\Cache\Adapter\Sharding;
|
||||
use Utopia\Cache\Cache;
|
||||
use Utopia\Config\Config;
|
||||
|
|
@ -28,6 +29,9 @@ use Utopia\Database\Database;
|
|||
use Utopia\WebSocket\Server;
|
||||
use Utopia\WebSocket\Adapter;
|
||||
|
||||
/**
|
||||
* @var \Utopia\Registry\Registry $register
|
||||
*/
|
||||
require_once __DIR__ . '/init.php';
|
||||
|
||||
Runtime::enableCoroutine(SWOOLE_HOOK_ALL);
|
||||
|
|
@ -122,12 +126,12 @@ $server = new Server($adapter);
|
|||
$logError = function (Throwable $error, string $action) use ($register) {
|
||||
$logger = $register->get('logger');
|
||||
|
||||
if ($logger) {
|
||||
if ($logger && !$error instanceof Exception) {
|
||||
$version = App::getEnv('_APP_VERSION', 'UNKNOWN');
|
||||
|
||||
$log = new Log();
|
||||
$log->setNamespace("realtime");
|
||||
$log->setServer(\gethostname());
|
||||
$log->setServer(gethostname());
|
||||
$log->setVersion($version);
|
||||
$log->setType(Log::TYPE_ERROR);
|
||||
$log->setMessage($error->getMessage());
|
||||
|
|
@ -182,7 +186,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
|
||||
$statsDocument = Authorization::skip(fn () => $database->createDocument('realtime', $document));
|
||||
break;
|
||||
} catch (\Throwable $th) {
|
||||
} catch (Throwable) {
|
||||
Console::warning("Collection not ready. Retrying connection ({$attempts})...");
|
||||
sleep(DATABASE_RECONNECT_SLEEP);
|
||||
}
|
||||
|
|
@ -210,7 +214,7 @@ $server->onStart(function () use ($stats, $register, $containerId, &$statsDocume
|
|||
->setAttribute('value', json_encode($payload));
|
||||
|
||||
Authorization::skip(fn () => $database->updateDocument('realtime', $statsDocument->getId(), $statsDocument));
|
||||
} catch (\Throwable $th) {
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "updateWorkerDocument");
|
||||
} finally {
|
||||
$register->get('pools')->reclaim();
|
||||
|
|
@ -362,7 +366,7 @@ $server->onWorkerStart(function (int $workerId) use ($server, $register, $stats,
|
|||
$stats->incr($event['project'], 'messages', $num);
|
||||
}
|
||||
});
|
||||
} catch (\Throwable $th) {
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "pubSubConnection");
|
||||
|
||||
Console::error('Pub/sub error: ' . $th->getMessage());
|
||||
|
|
@ -389,19 +393,19 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
App::setResource('response', fn() => $response);
|
||||
|
||||
try {
|
||||
/** @var \Utopia\Database\Document $project */
|
||||
/** @var Document $project */
|
||||
$project = $app->getResource('project');
|
||||
|
||||
/*
|
||||
* Project Check
|
||||
*/
|
||||
if (empty($project->getId())) {
|
||||
throw new Exception('Missing or unknown project ID', 1008);
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing or unknown project ID');
|
||||
}
|
||||
|
||||
$dbForProject = getProjectDB($project);
|
||||
$console = $app->getResource('console'); /** @var \Utopia\Database\Document $console */
|
||||
$user = $app->getResource('user'); /** @var \Utopia\Database\Document $user */
|
||||
$console = $app->getResource('console'); /** @var Document $console */
|
||||
$user = $app->getResource('user'); /** @var Document $user */
|
||||
|
||||
/*
|
||||
* Abuse Check
|
||||
|
|
@ -416,7 +420,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
if (App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled' && $abuse->check()) {
|
||||
throw new Exception('Too many requests', 1013);
|
||||
throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many requests');
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -425,10 +429,10 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
* Skip this check for non-web platforms which are not required to send an origin header.
|
||||
*/
|
||||
$origin = $request->getOrigin();
|
||||
$originValidator = new Origin(\array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
$originValidator = new Origin(array_merge($project->getAttribute('platforms', []), $console->getAttribute('platforms', [])));
|
||||
|
||||
if (!$originValidator->isValid($origin) && $project->getId() !== 'console') {
|
||||
throw new Exception($originValidator->getDescription(), 1008);
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, $originValidator->getDescription());
|
||||
}
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
|
|
@ -439,7 +443,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
* Channels Check
|
||||
*/
|
||||
if (empty($channels)) {
|
||||
throw new Exception('Missing channels', 1008);
|
||||
throw new Exception(Exception::REALTIME_POLICY_VIOLATION, 'Missing channels');
|
||||
}
|
||||
|
||||
$realtime->subscribe($project->getId(), $connection, $roles, $channels);
|
||||
|
|
@ -460,7 +464,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
]);
|
||||
$stats->incr($project->getId(), 'connections');
|
||||
$stats->incr($project->getId(), 'connectionsTotal');
|
||||
} catch (\Throwable $th) {
|
||||
} catch (Throwable $th) {
|
||||
call_user_func($logError, $th, "initServer");
|
||||
|
||||
$response = [
|
||||
|
|
@ -486,7 +490,6 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
|
|||
|
||||
$server->onMessage(function (int $connection, string $message) use ($server, $register, $realtime, $containerId) {
|
||||
try {
|
||||
$app = new App('UTC');
|
||||
$response = new Response(new SwooleResponse());
|
||||
$projectId = $realtime->connections[$connection]['projectId'];
|
||||
$database = getConsoleDB();
|
||||
|
|
@ -494,6 +497,8 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
|||
if ($projectId !== 'console') {
|
||||
$project = Authorization::skip(fn() => $database->getDocument('projects', $projectId));
|
||||
$database = getProjectDB($project);
|
||||
} else {
|
||||
$project = null;
|
||||
}
|
||||
|
||||
/*
|
||||
|
|
@ -510,22 +515,22 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
|||
$abuse = new Abuse($timeLimit);
|
||||
|
||||
if ($abuse->check() && App::getEnv('_APP_OPTIONS_ABUSE', 'enabled') === 'enabled') {
|
||||
throw new Exception('Too many messages', 1013);
|
||||
throw new Exception(Exception::REALTIME_TOO_MANY_MESSAGES, 'Too many messages.');
|
||||
}
|
||||
|
||||
$message = json_decode($message, true);
|
||||
|
||||
if (is_null($message) || (!array_key_exists('type', $message) && !array_key_exists('data', $message))) {
|
||||
throw new Exception('Message format is not valid.', 1003);
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Message format is not valid.');
|
||||
}
|
||||
|
||||
switch ($message['type']) {
|
||||
/**
|
||||
/**
|
||||
* This type is used to authenticate.
|
||||
*/
|
||||
case 'authentication':
|
||||
if (!array_key_exists('session', $message['data'])) {
|
||||
throw new Exception('Payload is not valid.', 1003);
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Payload is not valid.');
|
||||
}
|
||||
|
||||
$session = Auth::decodeSession($message['data']['session']);
|
||||
|
|
@ -540,7 +545,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
|||
|| !Auth::sessionVerify($user->getAttribute('sessions', []), Auth::$secret, $authDuration) // Validate user has valid login token
|
||||
) {
|
||||
// cookie not valid
|
||||
throw new Exception('Session is not valid.', 1003);
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Session is not valid.');
|
||||
}
|
||||
|
||||
$roles = Auth::getRoles($user);
|
||||
|
|
@ -560,9 +565,9 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
|
|||
break;
|
||||
|
||||
default:
|
||||
throw new Exception('Message type is not valid.', 1003);
|
||||
throw new Exception(Exception::REALTIME_MESSAGE_FORMAT_INVALID, 'Message type is not valid.');
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
} catch (Throwable $th) {
|
||||
$response = [
|
||||
'type' => 'error',
|
||||
'data' => [
|
||||
|
|
|
|||
|
|
@ -225,8 +225,13 @@ class Exception extends \Exception
|
|||
public const MIGRATION_ALREADY_EXISTS = 'migration_already_exists';
|
||||
public const MIGRATION_IN_PROGRESS = 'migration_in_progress';
|
||||
|
||||
protected $type = '';
|
||||
protected $errors = [];
|
||||
/** Realtime */
|
||||
public const REALTIME_MESSAGE_FORMAT_INVALID = 'realtime_message_format_invalid';
|
||||
public const REALTIME_TOO_MANY_MESSAGES = 'realtime_too_many_messages';
|
||||
public const REALTIME_POLICY_VIOLATION = 'realtime_policy_violation';
|
||||
|
||||
protected string $type = '';
|
||||
protected array $errors = [];
|
||||
|
||||
public function __construct(string $type = Exception::GENERAL_UNKNOWN, string $message = null, int $code = null, \Throwable $previous = null)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ abstract class Migration
|
|||
'1.4.4' => 'V19',
|
||||
'1.4.5' => 'V19',
|
||||
'1.4.6' => 'V19',
|
||||
'1.4.7' => 'V19',
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class Functions extends Action
|
|||
|
||||
$type = $payload['type'] ?? '';
|
||||
$events = $payload['events'] ?? [];
|
||||
$data = $payload['data'] ?? '';
|
||||
$data = $payload['body'] ?? '';
|
||||
$eventData = $payload['payload'] ?? '';
|
||||
$project = new Document($payload['project'] ?? []);
|
||||
$function = new Document($payload['function'] ?? []);
|
||||
|
|
|
|||
Loading…
Reference in a new issue