mirror of
https://github.com/appwrite/appwrite
synced 2026-05-23 00:49:02 +00:00
Resolve merge conflicts
This commit is contained in:
commit
6155fad3e0
52 changed files with 3177 additions and 1390 deletions
16
.github/workflows/static-analysis.yml
vendored
Normal file
16
.github/workflows/static-analysis.yml
vendored
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
name: "Static code analysis"
|
||||
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
lint:
|
||||
name: CodeQL
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v2
|
||||
|
||||
- name: Run CodeQL
|
||||
run: |
|
||||
docker run --rm -v $PWD:/app composer:2.6 sh -c \
|
||||
"composer install --profile --ignore-platform-reqs && composer check"
|
||||
|
|
@ -1185,6 +1185,39 @@ return [
|
|||
'default' => false,
|
||||
'array' => false,
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalAccessToken'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalAccessTokenExpiry'),
|
||||
'type' => Database::VAR_DATETIME,
|
||||
'format' => '',
|
||||
'size' => 0,
|
||||
'signed' => false,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['datetime'],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('personalRefreshToken'),
|
||||
'type' => Database::VAR_STRING,
|
||||
'format' => '',
|
||||
'size' => 256,
|
||||
'signed' => true,
|
||||
'required' => false,
|
||||
'default' => null,
|
||||
'array' => false,
|
||||
'filters' => ['encrypt'],
|
||||
],
|
||||
],
|
||||
'indexes' => [
|
||||
|
||||
|
|
|
|||
|
|
@ -375,6 +375,13 @@ return [
|
|||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Console */
|
||||
Exception::RESOURCE_ALREADY_EXISTS => [
|
||||
'name' => Exception::RESOURCE_ALREADY_EXISTS,
|
||||
'description' => 'Resource with the requested ID already exists. Please choose a different ID and try again.',
|
||||
'code' => 409,
|
||||
],
|
||||
|
||||
/** Membership */
|
||||
Exception::MEMBERSHIP_NOT_FOUND => [
|
||||
'name' => Exception::MEMBERSHIP_NOT_FOUND,
|
||||
|
|
@ -868,6 +875,11 @@ return [
|
|||
'description' => 'Variable with the same ID already exists in this project. Try again with a different ID.',
|
||||
'code' => 409,
|
||||
],
|
||||
Exception::VARIABLE_CANNOT_UNSET_SECRET => [
|
||||
'name' => Exception::VARIABLE_CANNOT_UNSET_SECRET,
|
||||
'description' => 'Secret variables cannot be marked as non-secret. Please re-create the variable if this is your intention.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::GRAPHQL_NO_QUERY => [
|
||||
'name' => Exception::GRAPHQL_NO_QUERY,
|
||||
'description' => 'Param "query" is not optional.',
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ $member = [
|
|||
'subscribers.write',
|
||||
'subscribers.read',
|
||||
'assistant.read',
|
||||
'rules.read'
|
||||
];
|
||||
|
||||
$admins = [
|
||||
|
|
|
|||
|
|
@ -1,9 +1,21 @@
|
|||
<?php
|
||||
|
||||
use Utopia\System\System;
|
||||
|
||||
/**
|
||||
* List of Appwrite Sites templates
|
||||
*/
|
||||
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
// TODO: Development override
|
||||
if (System::getEnv('_APP_ENV') === 'development') {
|
||||
$hostname = 'localhost';
|
||||
}
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
// TODO: @Meldiron Angular
|
||||
|
||||
const TEMPLATE_FRAMEWORKS = [
|
||||
|
|
@ -77,11 +89,200 @@ function getFramework(string $frameworkEnum, array $overrides)
|
|||
|
||||
return [
|
||||
[
|
||||
'key' => 'nextjs-starter',
|
||||
'name' => 'Next.js starter website',
|
||||
'key' => 'starter-for-svelte',
|
||||
'name' => 'Svelte starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://nextjs-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/nextjs-starter.png',
|
||||
'demoImage' => $url . '/console/images/sites/templates/starter-for-svelte.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-svelte',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'starter-for-nextjs',
|
||||
'name' => 'Next.js starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => $url . '/console/images/sites/templates/starter-for-nextjs.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'starter-for-nextjs',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_PROJECT_NAME',
|
||||
'description' => 'Your Appwrite project name',
|
||||
'value' => '{projectName}',
|
||||
'placeholder' => '{projectName}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-event',
|
||||
'name' => 'Event template',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => $url . '/console/images/sites/templates/template-for-event.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
'installCommand' => 'pnpm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-event',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_FUNCTION_PROJECT_ID',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'NEXT_PUBLIC_APPWRITE_FUNCTION_API_ENDPOINT',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-portfolio',
|
||||
'name' => 'Portfolio template',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => $url . '/console/images/sites/templates/template-for-portfolio.png',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-portfolio',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-store',
|
||||
'name' => 'Store template',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => $url . '/console/images/sites/templates/template-for-store.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-store',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => [
|
||||
[
|
||||
'name' => 'STRIPE_SECRET_KEY',
|
||||
'description' => 'Your Stripe secret key',
|
||||
'value' => 'disabled',
|
||||
'placeholder' => 'sk_.....',
|
||||
'required' => false,
|
||||
'type' => 'password'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_ENDPOINT',
|
||||
'description' => 'Endpoint of Appwrite server',
|
||||
'value' => '{apiEndpoint}',
|
||||
'placeholder' => '{apiEndpoint}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
[
|
||||
'name' => 'PUBLIC_APPWRITE_PROJECT_ID',
|
||||
'description' => 'Your Appwrite project ID',
|
||||
'value' => '{projectId}',
|
||||
'placeholder' => '{projectId}',
|
||||
'required' => true,
|
||||
'type' => 'text'
|
||||
],
|
||||
]
|
||||
],
|
||||
[
|
||||
'key' => 'template-for-blog',
|
||||
'name' => 'Blog template',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => $url . '/console/images/sites/templates/template-for-blog.png',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-blog',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'nextjs-starter',
|
||||
'name' => 'Next.js starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('NEXTJS', [
|
||||
'providerRootDirectory' => './nextjs/starter',
|
||||
|
|
@ -95,10 +296,9 @@ return [
|
|||
],
|
||||
[
|
||||
'key' => 'nuxt-starter',
|
||||
'name' => 'Nuxt starter website',
|
||||
'name' => 'Nuxt starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://nuxt-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/nuxt-starter.png',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('NUXT', [
|
||||
'providerRootDirectory' => './nuxt/starter',
|
||||
|
|
@ -112,10 +312,9 @@ return [
|
|||
],
|
||||
[
|
||||
'key' => 'sveltekit-starter',
|
||||
'name' => 'SvelteKit starter website',
|
||||
'name' => 'SvelteKit starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://sveltekit-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/sveltekit-starter.png',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('SVELTEKIT', [
|
||||
'providerRootDirectory' => './sveltekit/starter',
|
||||
|
|
@ -129,10 +328,9 @@ return [
|
|||
],
|
||||
[
|
||||
'key' => 'astro-starter',
|
||||
'name' => 'Astro starter website',
|
||||
'name' => 'Astro starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://astro-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/astro-starter.png',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('ASTRO', [
|
||||
'providerRootDirectory' => './astro/starter',
|
||||
|
|
@ -146,10 +344,9 @@ return [
|
|||
],
|
||||
[
|
||||
'key' => 'remix-starter',
|
||||
'name' => 'Remix starter website',
|
||||
'name' => 'Remix starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://remix-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/remix-starter.png',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('REMIX', [
|
||||
'providerRootDirectory' => './remix/starter',
|
||||
|
|
@ -163,10 +360,9 @@ return [
|
|||
],
|
||||
[
|
||||
'key' => 'flutter-starter',
|
||||
'name' => 'Flutter starter website',
|
||||
'name' => 'Flutter starter',
|
||||
'useCases' => ['starter'],
|
||||
'demoUrl' => 'https://flutter-starter.sites.qa17.appwrite.org/',
|
||||
'demoImage' => 'https://qa17.appwrite.org/console/images/sites/templates/flutter-starter.png',
|
||||
'demoImage' => '',
|
||||
'frameworks' => [
|
||||
getFramework('FLUTTER', [
|
||||
'providerRootDirectory' => './flutter/starter',
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"openapi": "3.0.0",
|
||||
"info": {
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0",
|
||||
"title": "Appwrite",
|
||||
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
|
||||
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
|
||||
|
|
@ -4761,7 +4761,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"weight": 301,
|
||||
"weight": 300,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -4846,7 +4846,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"weight": 300,
|
||||
"weight": 299,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -4960,7 +4960,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"weight": 302,
|
||||
"weight": 301,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5033,7 +5033,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"weight": 327,
|
||||
"weight": 326,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
|
|
@ -5084,7 +5084,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"weight": 326,
|
||||
"weight": 325,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
|
|
@ -5543,7 +5543,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"weight": 372,
|
||||
"weight": 371,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5625,7 +5625,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"weight": 376,
|
||||
"weight": 375,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5699,7 +5699,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"weight": 208,
|
||||
"weight": 207,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5784,7 +5784,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"weight": 207,
|
||||
"weight": 206,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"deprecated": false,
|
||||
|
|
@ -5881,7 +5881,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"weight": 209,
|
||||
"weight": 208,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5952,7 +5952,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"weight": 214,
|
||||
"weight": 213,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6040,7 +6040,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"weight": 215,
|
||||
"weight": 214,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6106,7 +6106,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"weight": 211,
|
||||
"weight": 210,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6172,7 +6172,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"weight": 210,
|
||||
"weight": 209,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6388,7 +6388,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"weight": 212,
|
||||
"weight": 211,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6461,7 +6461,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"weight": 219,
|
||||
"weight": 218,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6536,7 +6536,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"weight": 218,
|
||||
"weight": 217,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6620,7 +6620,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"weight": 220,
|
||||
"weight": 219,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6681,7 +6681,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"weight": 222,
|
||||
"weight": 221,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6754,7 +6754,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"weight": 224,
|
||||
"weight": 223,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6817,7 +6817,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"weight": 226,
|
||||
"weight": 225,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6902,7 +6902,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"weight": 225,
|
||||
"weight": 224,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7012,7 +7012,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"weight": 227,
|
||||
"weight": 226,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7083,7 +7083,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"weight": 228,
|
||||
"weight": 227,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7169,7 +7169,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"weight": 230,
|
||||
"weight": 229,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7242,7 +7242,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"weight": 229,
|
||||
"weight": 228,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7339,7 +7339,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"weight": 221,
|
||||
"weight": 220,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7399,7 +7399,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"weight": 223,
|
||||
"weight": 222,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"swagger": "2.0",
|
||||
"info": {
|
||||
"version": "1.6.1",
|
||||
"version": "1.7.0",
|
||||
"title": "Appwrite",
|
||||
"description": "Appwrite backend as a service cuts up to 70% of the time and costs required for building a modern application. We abstract and simplify common development tasks behind a REST APIs, to help you develop your app in a fast and secure way. For full API documentation and tutorials go to [https:\/\/appwrite.io\/docs](https:\/\/appwrite.io\/docs)",
|
||||
"termsOfService": "https:\/\/appwrite.io\/policy\/terms",
|
||||
|
|
@ -4927,7 +4927,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listExecutions",
|
||||
"weight": 301,
|
||||
"weight": 300,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5009,7 +5009,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createExecution",
|
||||
"weight": 300,
|
||||
"weight": 299,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5127,7 +5127,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getExecution",
|
||||
"weight": 302,
|
||||
"weight": 301,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5198,7 +5198,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "query",
|
||||
"weight": 327,
|
||||
"weight": 326,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
|
|
@ -5271,7 +5271,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "mutation",
|
||||
"weight": 326,
|
||||
"weight": 325,
|
||||
"cookies": false,
|
||||
"type": "graphql",
|
||||
"deprecated": false,
|
||||
|
|
@ -5768,7 +5768,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createSubscriber",
|
||||
"weight": 372,
|
||||
"weight": 371,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5852,7 +5852,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteSubscriber",
|
||||
"weight": 376,
|
||||
"weight": 375,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -5924,7 +5924,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listFiles",
|
||||
"weight": 208,
|
||||
"weight": 207,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6006,7 +6006,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createFile",
|
||||
"weight": 207,
|
||||
"weight": 206,
|
||||
"cookies": false,
|
||||
"type": "upload",
|
||||
"deprecated": false,
|
||||
|
|
@ -6097,7 +6097,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFile",
|
||||
"weight": 209,
|
||||
"weight": 208,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6166,7 +6166,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateFile",
|
||||
"weight": 214,
|
||||
"weight": 213,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6254,7 +6254,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteFile",
|
||||
"weight": 215,
|
||||
"weight": 214,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6325,7 +6325,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFileDownload",
|
||||
"weight": 211,
|
||||
"weight": 210,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6396,7 +6396,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFilePreview",
|
||||
"weight": 210,
|
||||
"weight": 209,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6595,7 +6595,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getFileView",
|
||||
"weight": 212,
|
||||
"weight": 211,
|
||||
"cookies": false,
|
||||
"type": "location",
|
||||
"deprecated": false,
|
||||
|
|
@ -6666,7 +6666,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "list",
|
||||
"weight": 219,
|
||||
"weight": 218,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6740,7 +6740,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "create",
|
||||
"weight": 218,
|
||||
"weight": 217,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6831,7 +6831,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "get",
|
||||
"weight": 220,
|
||||
"weight": 219,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6892,7 +6892,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateName",
|
||||
"weight": 222,
|
||||
"weight": 221,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -6966,7 +6966,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "delete",
|
||||
"weight": 224,
|
||||
"weight": 223,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7029,7 +7029,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "listMemberships",
|
||||
"weight": 226,
|
||||
"weight": 225,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7111,7 +7111,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "createMembership",
|
||||
"weight": 225,
|
||||
"weight": 224,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7225,7 +7225,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getMembership",
|
||||
"weight": 227,
|
||||
"weight": 226,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7294,7 +7294,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateMembership",
|
||||
"weight": 228,
|
||||
"weight": 227,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7379,7 +7379,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "deleteMembership",
|
||||
"weight": 230,
|
||||
"weight": 229,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7450,7 +7450,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updateMembershipStatus",
|
||||
"weight": 229,
|
||||
"weight": 228,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7545,7 +7545,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "getPrefs",
|
||||
"weight": 221,
|
||||
"weight": 220,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
@ -7605,7 +7605,7 @@
|
|||
},
|
||||
"x-appwrite": {
|
||||
"method": "updatePrefs",
|
||||
"weight": 223,
|
||||
"weight": 222,
|
||||
"cookies": false,
|
||||
"type": "",
|
||||
"deprecated": false,
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
|
|
@ -7,4 +7,5 @@ return [
|
|||
"gif" => "image/gif",
|
||||
"png" => "image/png",
|
||||
"heic" => "image/heic",
|
||||
"webp" => "image/webp",
|
||||
];
|
||||
|
|
|
|||
|
|
@ -1585,7 +1585,7 @@ App::post('/v1/functions/:functionId/variables')
|
|||
->param('functionId', '', new UID(), 'Function unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
|
|
@ -1729,10 +1729,11 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only functions can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $functionId, string $variableId, string $key, ?string $value, ?bool $secret, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
|
|
@ -1749,9 +1750,14 @@ App::put('/v1/functions/:functionId/variables/:variableId')
|
|||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $function->getId(), $key, 'function']));
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -388,7 +388,7 @@ App::post('/v1/project/variables')
|
|||
))
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
|
|
@ -509,19 +509,25 @@ App::put('/v1/project/variables/:variableId')
|
|||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only projects can read them during build and runtime.', true)
|
||||
->inject('project')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->action(function (string $variableId, string $key, ?string $value, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
->action(function (string $variableId, string $key, ?string $value, ?bool $secret, Document $project, Response $response, Database $dbForProject, Database $dbForPlatform) {
|
||||
$variable = $dbForProject->getDocument('variables', $variableId);
|
||||
if ($variable === false || $variable->isEmpty() || $variable->getAttribute('resourceType') !== 'project') {
|
||||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $key, 'project']));
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -15,6 +15,8 @@ use Appwrite\SDK\AuthType;
|
|||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Transformation\Adapter\Preview;
|
||||
use Appwrite\Transformation\Transformation;
|
||||
use Appwrite\Utopia\Request;
|
||||
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
|
||||
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
|
||||
|
|
@ -123,14 +125,14 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$type = $rule->getAttribute('resourceType');
|
||||
|
||||
if ($type === 'function' || $type === 'site' || $type === 'deployment') {
|
||||
$resourceCollection = match($type) {
|
||||
$resourceCollection = match ($type) {
|
||||
'function' => 'functions',
|
||||
'site' => 'sites',
|
||||
'deployment' => 'deployments',
|
||||
};
|
||||
}
|
||||
|
||||
if ($type === 'function' || $type === 'site') {
|
||||
if ($type === 'function' || $type === 'site' || $type === 'deployment') {
|
||||
$method = $utopia->getRoute()?->getLabel('sdk', null);
|
||||
|
||||
if (empty($method)) {
|
||||
|
|
@ -198,7 +200,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
throw new AppwriteException(AppwriteException::GENERAL_RESOURCE_BLOCKED);
|
||||
}
|
||||
|
||||
$version = match($type) {
|
||||
$version = match ($type) {
|
||||
'function' => $resource->getAttribute('version', 'v2'),
|
||||
'site' => 'v4',
|
||||
'deployment' => 'v4'
|
||||
|
|
@ -207,7 +209,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$runtimes = Config::getParam($version === 'v2' ? 'runtimes-v2' : 'runtimes', []);
|
||||
$spec = Config::getParam('runtime-specifications')[$resource->getAttribute('specification', APP_COMPUTE_SPECIFICATION_DEFAULT)];
|
||||
|
||||
$runtime = match($type) {
|
||||
$runtime = match ($type) {
|
||||
'function' => $runtimes[$resource->getAttribute('runtime')] ?? null,
|
||||
'site' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null,
|
||||
'deployment' => $runtimes[$resource->getAttribute('buildRuntime')] ?? null,
|
||||
|
|
@ -222,7 +224,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
$deploymentId = match($type) {
|
||||
$deploymentId = match ($type) {
|
||||
'function' => $resource->getAttribute('deployment', ''),
|
||||
'site' => $resource->getAttribute('deploymentId', ''),
|
||||
'deployment' => $subResource->getId()
|
||||
|
|
@ -388,12 +390,12 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
/** Execute function */
|
||||
$executor = new Executor(System::getEnv('_APP_EXECUTOR_HOST'));
|
||||
try {
|
||||
$version = match($type) {
|
||||
$version = match ($type) {
|
||||
'function' => $resource->getAttribute('version', 'v2'),
|
||||
'site' => 'v4',
|
||||
'deployment' => 'v4'
|
||||
};
|
||||
$entrypoint = match($type) {
|
||||
$entrypoint = match ($type) {
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
'deployment' => ''
|
||||
|
|
@ -420,7 +422,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
$runtimeEntrypoint = 'cp /tmp/code.tar.gz /mnt/code/code.tar.gz && nohup helpers/start.sh "' . $startCommand . '"';
|
||||
}
|
||||
|
||||
$entrypoint = match($type) {
|
||||
$entrypoint = match ($type) {
|
||||
'function' => $deployment->getAttribute('entrypoint', ''),
|
||||
'site' => '',
|
||||
'deployment' => ''
|
||||
|
|
@ -446,6 +448,22 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
requestTimeout: 30
|
||||
);
|
||||
|
||||
|
||||
// Branded banner for previews
|
||||
$transformation = new Transformation();
|
||||
$transformation->addAdapter(new Preview());
|
||||
$transformation->setInput($executionResponse['body']);
|
||||
$transformation->setTraits($executionResponse['headers']);
|
||||
if ($type === 'deployment' && $transformation->transform()) {
|
||||
$executionResponse['body'] = $transformation->getOutput();
|
||||
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$headersFiltered = [];
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\in_array(\strtolower($key), FUNCTION_ALLOWLIST_HEADERS_RESPONSE)) {
|
||||
|
|
@ -472,8 +490,8 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
|
||||
if ($type === 'function') {
|
||||
$execution
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
->setAttribute('status', 'failed')
|
||||
->setAttribute('errors', $th->getMessage() . '\nError Code: ' . $th->getCode());
|
||||
}
|
||||
Console::error($th->getMessage());
|
||||
|
||||
|
|
@ -540,8 +558,7 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
|||
->addMetric(METRIC_EXECUTIONS_MB_SECONDS, (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
->addMetric(str_replace('{functionInternalId}', $resource->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(($spec['memory'] ?? APP_COMPUTE_MEMORY_DEFAULT) * $execution->getAttribute('duration', 0) * ($spec['cpus'] ?? APP_COMPUTE_CPUS_DEFAULT)))
|
||||
->setProject($project)
|
||||
->trigger()
|
||||
;
|
||||
->trigger();
|
||||
|
||||
return true;
|
||||
} elseif ($type === 'api') {
|
||||
|
|
@ -720,6 +737,12 @@ App::init()
|
|||
$validator = new Hostname($clients);
|
||||
if ($validator->isValid($origin)) {
|
||||
$refDomainOrigin = $origin;
|
||||
} else {
|
||||
// Auto-allow domains with linked rule
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin)));
|
||||
if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) {
|
||||
$refDomainOrigin = $origin;
|
||||
}
|
||||
}
|
||||
|
||||
$refDomain = (!empty($protocol) ? $protocol : $request->getProtocol()) . '://' . $refDomainOrigin . (!empty($port) ? ':' . $port : '');
|
||||
|
|
@ -769,7 +792,7 @@ App::init()
|
|||
$response->addFilter(new ResponseV18());
|
||||
}
|
||||
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
|
||||
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
||||
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is " . APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1118,6 +1141,7 @@ App::error()
|
|||
->setParam('trace', $output['trace'] ?? []);
|
||||
|
||||
$response->html($layout->render());
|
||||
return;
|
||||
}
|
||||
|
||||
$response->dynamic(
|
||||
|
|
|
|||
|
|
@ -14,7 +14,8 @@
|
|||
"test": "vendor/bin/phpunit",
|
||||
"lint": "vendor/bin/pint --test",
|
||||
"format": "vendor/bin/pint",
|
||||
"bench": "vendor/bin/phpbench run --report=benchmark"
|
||||
"bench": "vendor/bin/phpbench run --report=benchmark",
|
||||
"check": "./vendor/bin/phpstan analyse -c phpstan.neon"
|
||||
},
|
||||
"autoload": {
|
||||
"psr-4": {
|
||||
|
|
@ -90,7 +91,8 @@
|
|||
"swoole/ide-helper": "5.1.2",
|
||||
"textalk/websocket": "1.5.7",
|
||||
"laravel/pint": "^1.14",
|
||||
"phpbench/phpbench": "^1.2"
|
||||
"phpbench/phpbench": "^1.2",
|
||||
"phpstan/phpstan": "1.8.*"
|
||||
},
|
||||
"provide": {
|
||||
"ext-phpiredis": "*"
|
||||
|
|
|
|||
109
composer.lock
generated
109
composer.lock
generated
|
|
@ -4,7 +4,7 @@
|
|||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
|
||||
"This file is @generated automatically"
|
||||
],
|
||||
"content-hash": "c238139c9b44c646364734186c481036",
|
||||
"content-hash": "5473f6b65d54314228e69b6bed489d78",
|
||||
"packages": [
|
||||
{
|
||||
"name": "adhocore/jwt",
|
||||
|
|
@ -4553,16 +4553,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/queue",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.6",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/queue.git",
|
||||
"reference": "a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0"
|
||||
"reference": "b713b997285c29d120bbcbe3d6e93762d850f87c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0",
|
||||
"reference": "a6ec26a787e8292ca2d7b8f5a0ad179b46b2c4d0",
|
||||
"url": "https://api.github.com/repos/utopia-php/queue/zipball/b713b997285c29d120bbcbe3d6e93762d850f87c",
|
||||
"reference": "b713b997285c29d120bbcbe3d6e93762d850f87c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4612,9 +4612,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/queue/issues",
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.8.2"
|
||||
"source": "https://github.com/utopia-php/queue/tree/0.8.6"
|
||||
},
|
||||
"time": "2025-02-06T11:01:15+00:00"
|
||||
"time": "2025-02-10T03:35:00+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/registry",
|
||||
|
|
@ -4670,16 +4670,16 @@
|
|||
},
|
||||
{
|
||||
"name": "utopia-php/storage",
|
||||
"version": "0.18.8",
|
||||
"version": "0.18.9",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/storage.git",
|
||||
"reference": "84737afa634e6a833fc4f8b0c967553234d3f215"
|
||||
"reference": "1cf455404e8700b3093fd73d74a38d41cdced90c"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/84737afa634e6a833fc4f8b0c967553234d3f215",
|
||||
"reference": "84737afa634e6a833fc4f8b0c967553234d3f215",
|
||||
"url": "https://api.github.com/repos/utopia-php/storage/zipball/1cf455404e8700b3093fd73d74a38d41cdced90c",
|
||||
"reference": "1cf455404e8700b3093fd73d74a38d41cdced90c",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -4719,9 +4719,9 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/storage/issues",
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.18.8"
|
||||
"source": "https://github.com/utopia-php/storage/tree/0.18.9"
|
||||
},
|
||||
"time": "2024-12-04T08:30:35+00:00"
|
||||
"time": "2025-02-11T13:10:40+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
|
|
@ -5623,16 +5623,16 @@
|
|||
},
|
||||
{
|
||||
"name": "myclabs/deep-copy",
|
||||
"version": "1.12.1",
|
||||
"version": "1.13.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/myclabs/DeepCopy.git",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845"
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"reference": "123267b2c49fbf30d78a7b2d333f6be754b94845",
|
||||
"url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"reference": "024473a478be9df5fdaca2c793f2232fe788e414",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -5671,7 +5671,7 @@
|
|||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/myclabs/DeepCopy/issues",
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.12.1"
|
||||
"source": "https://github.com/myclabs/DeepCopy/tree/1.13.0"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
|
|
@ -5679,7 +5679,7 @@
|
|||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-11-08T17:47:46+00:00"
|
||||
"time": "2025-02-12T12:17:51+00:00"
|
||||
},
|
||||
{
|
||||
"name": "nikic/php-parser",
|
||||
|
|
@ -6253,16 +6253,16 @@
|
|||
},
|
||||
{
|
||||
"name": "phpstan/phpdoc-parser",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.1",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpdoc-parser.git",
|
||||
"reference": "c00d78fb6b29658347f9d37ebe104bffadf36299"
|
||||
"reference": "72e51f7c32c5aef7c8b462195b8c599b11199893"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/c00d78fb6b29658347f9d37ebe104bffadf36299",
|
||||
"reference": "c00d78fb6b29658347f9d37ebe104bffadf36299",
|
||||
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/72e51f7c32c5aef7c8b462195b8c599b11199893",
|
||||
"reference": "72e51f7c32c5aef7c8b462195b8c599b11199893",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
|
@ -6294,9 +6294,68 @@
|
|||
"description": "PHPDoc parser with support for nullable, intersection and generic types",
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.0"
|
||||
"source": "https://github.com/phpstan/phpdoc-parser/tree/2.0.1"
|
||||
},
|
||||
"time": "2024-10-13T11:29:49+00:00"
|
||||
"time": "2025-02-13T12:25:43+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpstan/phpstan",
|
||||
"version": "1.8.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/phpstan/phpstan.git",
|
||||
"reference": "46e223dd68a620da18855c23046ddb00940b4014"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/46e223dd68a620da18855c23046ddb00940b4014",
|
||||
"reference": "46e223dd68a620da18855c23046ddb00940b4014",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
"php": "^7.2|^8.0"
|
||||
},
|
||||
"conflict": {
|
||||
"phpstan/phpstan-shim": "*"
|
||||
},
|
||||
"bin": [
|
||||
"phpstan",
|
||||
"phpstan.phar"
|
||||
],
|
||||
"type": "library",
|
||||
"autoload": {
|
||||
"files": [
|
||||
"bootstrap.php"
|
||||
]
|
||||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"MIT"
|
||||
],
|
||||
"description": "PHPStan - PHP Static Analysis Tool",
|
||||
"keywords": [
|
||||
"dev",
|
||||
"static analysis"
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/phpstan/phpstan/issues",
|
||||
"source": "https://github.com/phpstan/phpstan/tree/1.8.11"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
"url": "https://github.com/ondrejmirtes",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://github.com/phpstan",
|
||||
"type": "github"
|
||||
},
|
||||
{
|
||||
"url": "https://tidelift.com/funding/github/packagist/phpstan/phpstan",
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2022-10-24T15:45:13+00:00"
|
||||
},
|
||||
{
|
||||
"name": "phpunit/php-code-coverage",
|
||||
|
|
|
|||
|
|
@ -204,7 +204,7 @@ services:
|
|||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.3.0-sites-rc.2
|
||||
image: appwrite/console:5.3.0-sites-rc.9
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
|
|
|||
8
phpstan.neon
Normal file
8
phpstan.neon
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
parameters:
|
||||
level: 8
|
||||
paths:
|
||||
- src/Appwrite/Transformation
|
||||
scanDirectories:
|
||||
- vendor/swoole/ide-helper
|
||||
excludePaths:
|
||||
- tests/resources
|
||||
|
|
@ -20,10 +20,9 @@ class Slack extends OAuth2
|
|||
* @var array
|
||||
*/
|
||||
protected array $scopes = [
|
||||
'identity.avatar',
|
||||
'identity.basic',
|
||||
'identity.email',
|
||||
'identity.team'
|
||||
'openid',
|
||||
'email',
|
||||
'profile'
|
||||
];
|
||||
|
||||
/**
|
||||
|
|
@ -35,14 +34,15 @@ class Slack extends OAuth2
|
|||
}
|
||||
|
||||
/**
|
||||
* @link https://api.slack.com/authentication/oauth-v2
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function getLoginURL(): string
|
||||
{
|
||||
// https://api.slack.com/docs/oauth#step_1_-_sending_users_to_authorize_and_or_install
|
||||
return 'https://slack.com/oauth/authorize?' . \http_build_query([
|
||||
return 'https://slack.com/oauth/v2/authorize?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'scope' => \implode(' ', $this->getScopes()),
|
||||
'user_scope' => \implode(' ', $this->getScopes()),
|
||||
'redirect_uri' => $this->callback,
|
||||
'state' => \json_encode($this->state)
|
||||
]);
|
||||
|
|
@ -56,16 +56,15 @@ class Slack extends OAuth2
|
|||
protected function getTokens(string $code): array
|
||||
{
|
||||
if (empty($this->tokens)) {
|
||||
// https://api.slack.com/docs/oauth#step_3_-_exchanging_a_verification_code_for_an_access_token
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
'https://slack.com/api/oauth.access?' . \http_build_query([
|
||||
'https://slack.com/api/oauth.v2.access?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'code' => $code,
|
||||
'redirect_uri' => $this->callback
|
||||
])
|
||||
), true);
|
||||
), true)['authed_user'] ?? [];
|
||||
}
|
||||
|
||||
return $this->tokens;
|
||||
|
|
@ -80,13 +79,13 @@ class Slack extends OAuth2
|
|||
{
|
||||
$this->tokens = \json_decode($this->request(
|
||||
'GET',
|
||||
'https://slack.com/api/oauth.access?' . \http_build_query([
|
||||
'https://slack.com/api/oauth.v2.access?' . \http_build_query([
|
||||
'client_id' => $this->appID,
|
||||
'client_secret' => $this->appSecret,
|
||||
'refresh_token' => $refreshToken,
|
||||
'grant_type' => 'refresh_token'
|
||||
])
|
||||
), true);
|
||||
), true)['authed_user'] ?? [];
|
||||
|
||||
if (empty($this->tokens['refresh_token'])) {
|
||||
$this->tokens['refresh_token'] = $refreshToken;
|
||||
|
|
@ -161,9 +160,9 @@ class Slack extends OAuth2
|
|||
if (empty($this->user)) {
|
||||
$user = $this->request(
|
||||
'GET',
|
||||
'https://slack.com/api/users.identity?token=' . \urlencode($accessToken)
|
||||
'https://slack.com/api/users.identity',
|
||||
['Authorization: Bearer ' . \urlencode($accessToken)]
|
||||
);
|
||||
|
||||
$this->user = \json_decode($user, true);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -118,6 +118,9 @@ class Exception extends \Exception
|
|||
public const TEAM_INVITE_MISMATCH = 'team_invite_mismatch';
|
||||
public const TEAM_ALREADY_EXISTS = 'team_already_exists';
|
||||
|
||||
/** Console */
|
||||
public const RESOURCE_ALREADY_EXISTS = 'resource_already_exists';
|
||||
|
||||
/** Membership */
|
||||
public const MEMBERSHIP_NOT_FOUND = 'membership_not_found';
|
||||
public const MEMBERSHIP_ALREADY_CONFIRMED = 'membership_already_confirmed';
|
||||
|
|
@ -255,6 +258,7 @@ class Exception extends \Exception
|
|||
/** Variables */
|
||||
public const VARIABLE_NOT_FOUND = 'variable_not_found';
|
||||
public const VARIABLE_ALREADY_EXISTS = 'variable_already_exists';
|
||||
public const VARIABLE_CANNOT_UNSET_SECRET = 'variable_cannot_unset_secret';
|
||||
|
||||
/** Platform */
|
||||
public const PLATFORM_NOT_FOUND = 'platform_not_found';
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace Appwrite\Platform;
|
||||
|
||||
use Appwrite\Platform\Modules\Console;
|
||||
use Appwrite\Platform\Modules\Core;
|
||||
use Appwrite\Platform\Modules\Functions;
|
||||
use Appwrite\Platform\Modules\Sites;
|
||||
|
|
@ -14,5 +15,6 @@ class Appwrite extends Platform
|
|||
parent::__construct(new Core());
|
||||
$this->addModule(new Functions\Module());
|
||||
$this->addModule(new Sites\Module());
|
||||
$this->addModule(new Console\Module());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ use Utopia\VCS\Exception\RepositoryNotFound;
|
|||
|
||||
class Base extends Action
|
||||
{
|
||||
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github)
|
||||
public function redeployVcsFunction(Request $request, Document $function, Document $project, Document $installation, Database $dbForProject, Build $queueForBuilds, Document $template, GitHub $github): Document
|
||||
{
|
||||
$deploymentId = ID::unique();
|
||||
$entrypoint = $function->getAttribute('entrypoint', '');
|
||||
|
|
@ -91,9 +91,11 @@ class Base extends Action
|
|||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
||||
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github)
|
||||
public function redeployVcsSite(Request $request, Document $site, Document $project, Document $installation, Database $dbForProject, Database $dbForPlatform, Build $queueForBuilds, Document $template, GitHub $github): Document
|
||||
{
|
||||
$deploymentId = ID::unique();
|
||||
$providerInstallationId = $installation->getAttribute('providerInstallationId', '');
|
||||
|
|
@ -187,5 +189,7 @@ class Base extends Action
|
|||
->setResource($site)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
85
src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php
Normal file
85
src/Appwrite/Platform/Modules/Console/Http/Resources/Get.php
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Http\Resources;
|
||||
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\ContentType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Query;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Domain;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
|
||||
class Get extends Action
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'getResources';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/console/resources')
|
||||
->desc('Check resource ID availability')
|
||||
->groups(['api', 'projects'])
|
||||
->label('scope', 'rules.read')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'console',
|
||||
name: 'getResource',
|
||||
description: <<<EOT
|
||||
Check if a resource ID is available.
|
||||
EOT,
|
||||
auth: [AuthType::ADMIN],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_NOCONTENT,
|
||||
model: Response::MODEL_NONE,
|
||||
)
|
||||
],
|
||||
contentType: ContentType::NONE,
|
||||
))
|
||||
->label('abuse-limit', 10)
|
||||
->label('abuse-key', 'userId:{userId}, url:{url}')
|
||||
->label('abuse-time', 60)
|
||||
->param('value', '', new Text(256), 'Resource value.')
|
||||
->param('type', '', new WhiteList(['rules']), 'Resource type.')
|
||||
->inject('response')
|
||||
->inject('dbForPlatform')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $value, string $type, Response $response, Database $dbForPlatform)
|
||||
{
|
||||
if ($type === 'rules') {
|
||||
$validator = new Domain($value);
|
||||
|
||||
if (!$validator->isValid($value)) {
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, $validator->getDescription());
|
||||
}
|
||||
|
||||
$document = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
|
||||
Query::equal('domain', [$value]),
|
||||
]));
|
||||
|
||||
if (!$document->isEmpty()) {
|
||||
throw new Exception(Exception::RESOURCE_ALREADY_EXISTS);
|
||||
}
|
||||
|
||||
$response->noContent();
|
||||
}
|
||||
|
||||
// Only occurs if type is added into whitelist, but not supported in action
|
||||
throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Invalid type');
|
||||
}
|
||||
}
|
||||
14
src/Appwrite/Platform/Modules/Console/Module.php
Normal file
14
src/Appwrite/Platform/Modules/Console/Module.php
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Services\Http;
|
||||
use Utopia\Platform;
|
||||
|
||||
class Module extends Platform\Module
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->addService('http', new Http());
|
||||
}
|
||||
}
|
||||
16
src/Appwrite/Platform/Modules/Console/Services/Http.php
Normal file
16
src/Appwrite/Platform/Modules/Console/Services/Http.php
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Console\Services;
|
||||
|
||||
use Appwrite\Platform\Modules\Console\Http\Resources\Get as GetResourceAvailability;
|
||||
use Utopia\Platform\Service;
|
||||
|
||||
class Http extends Service
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->type = Service::TYPE_HTTP;
|
||||
// Resources
|
||||
$this->addAction(GetResourceAvailability::getName(), new GetResourceAvailability());
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Deployments\Template;
|
||||
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createTemplateDeployment';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/functions/:functionId/deployments/template')
|
||||
->desc('Create template deployment')
|
||||
->groups(['api', 'functions'])
|
||||
->label('scope', 'functions.write')
|
||||
->label('event', 'functions.[functionId].deployments.[deploymentId].create')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('audits.event', 'deployment.create')
|
||||
->label('audits.resource', 'function/{request.functionId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
name: 'createTemplateDeployment',
|
||||
description: <<<EOT
|
||||
Create a deployment based on a template.
|
||||
|
||||
Use this endpoint with combination of [listTemplates](https://appwrite.io/docs/server/functions#listTemplates) to find the template details.
|
||||
EOT,
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_ACCEPTED,
|
||||
model: Response::MODEL_DEPLOYMENT,
|
||||
)
|
||||
],
|
||||
))
|
||||
->param('functionId', '', new UID(), 'Function ID.')
|
||||
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
|
||||
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
|
||||
->param('rootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.')
|
||||
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.')
|
||||
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('queueForEvents')
|
||||
->inject('project')
|
||||
->inject('queueForBuilds')
|
||||
->inject('gitHub')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $functionId, string $repository, string $owner, string $rootDirectory, string $version, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Event $queueForEvents, Document $project, Build $queueForBuilds, GitHub $github)
|
||||
{
|
||||
$function = $dbForProject->getDocument('functions', $functionId);
|
||||
|
||||
if ($function->isEmpty()) {
|
||||
throw new Exception(Exception::FUNCTION_NOT_FOUND);
|
||||
}
|
||||
|
||||
$template = new Document([
|
||||
'repositoryName' => $repository,
|
||||
'ownerName' => $owner,
|
||||
'rootDirectory' => $rootDirectory,
|
||||
'version' => $version
|
||||
]);
|
||||
|
||||
if (!empty($function->getAttribute('providerRepositoryId'))) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $function->getAttribute('installationId'));
|
||||
|
||||
$deployment = $this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceInternalId' => $function->getInternalId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $function->getAttribute('entrypoint', ''),
|
||||
'commands' => $function->getAttribute('commands', ''),
|
||||
'type' => 'manual',
|
||||
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('functionId', $function->getId())
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Appwrite\Platform\Modules\Functions\Http\Functions;
|
||||
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Event\Validator\FunctionEvent;
|
||||
use Appwrite\Extend\Exception;
|
||||
|
|
@ -29,14 +28,12 @@ use Utopia\Database\Validator\Authorization;
|
|||
use Utopia\Database\Validator\Roles;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\ArrayList;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
|
|
@ -90,30 +87,22 @@ class Create extends Base
|
|||
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
|
||||
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
|
||||
->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
|
||||
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
|
||||
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
|
||||
->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
|
||||
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
|
||||
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new RuntimeSpecification(
|
||||
$plan,
|
||||
Config::getParam('runtime-specifications', []),
|
||||
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
|
||||
), 'Runtime specification for the function and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('timelimit')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForBuilds')
|
||||
->inject('dbForPlatform')
|
||||
->inject('gitHub')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, callable $timelimit, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github)
|
||||
public function action(string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, callable $timelimit, Document $project, Event $queueForEvents, Database $dbForPlatform)
|
||||
{
|
||||
|
||||
// Temporary abuse check
|
||||
|
|
@ -153,20 +142,6 @@ class Create extends Base
|
|||
throw new Exception(Exception::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $runtime . '" is not supported');
|
||||
}
|
||||
|
||||
// build from template
|
||||
$template = new Document([]);
|
||||
if (
|
||||
!empty($templateRepository)
|
||||
&& !empty($templateOwner)
|
||||
&& !empty($templateRootDirectory)
|
||||
&& !empty($templateVersion)
|
||||
) {
|
||||
$template->setAttribute('repositoryName', $templateRepository)
|
||||
->setAttribute('ownerName', $templateOwner)
|
||||
->setAttribute('rootDirectory', $templateRootDirectory)
|
||||
->setAttribute('version', $templateVersion);
|
||||
}
|
||||
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if (!empty($installationId) && $installation->isEmpty()) {
|
||||
|
|
@ -254,36 +229,6 @@ class Create extends Base
|
|||
|
||||
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
|
||||
|
||||
if (!empty($providerRepositoryId)) {
|
||||
// Deploy VCS
|
||||
$this->redeployVcsFunction($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
|
||||
} elseif (!$template->isEmpty()) {
|
||||
// Deploy non-VCS from template
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $function->getId(),
|
||||
'resourceInternalId' => $function->getInternalId(),
|
||||
'resourceType' => 'functions',
|
||||
'entrypoint' => $function->getAttribute('entrypoint', ''),
|
||||
'commands' => $function->getAttribute('commands', ''),
|
||||
'type' => 'manual',
|
||||
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
|
||||
'activate' => true,
|
||||
]));
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($function)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
}
|
||||
|
||||
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
|
||||
if (!empty($functionsDomain)) {
|
||||
$routeSubdomain = ID::unique();
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@
|
|||
namespace Appwrite\Platform\Modules\Functions\Services;
|
||||
|
||||
use Appwrite\Platform\Modules\Functions\Http\Deployments\Create as CreateDeployment;
|
||||
use Appwrite\Platform\Modules\Functions\Http\Deployments\Template\Create as CreateTemplateDeployment;
|
||||
use Appwrite\Platform\Modules\Functions\Http\Functions\Create as CreateFunction;
|
||||
use Appwrite\Platform\Modules\Functions\Http\Functions\Update as UpdateFunction;
|
||||
use Appwrite\Platform\Modules\Functions\Http\Functions\XList as ListFunctions;
|
||||
|
|
@ -19,5 +20,6 @@ class Http extends Service
|
|||
$this->addAction(ListFunctions::getName(), new ListFunctions());
|
||||
$this->addAction(ListRuntimes::getName(), new ListRuntimes());
|
||||
$this->addAction(CreateDeployment::getName(), new CreateDeployment());
|
||||
$this->addAction(CreateTemplateDeployment::getName(), new CreateTemplateDeployment());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -869,22 +869,27 @@ class Builds extends Action
|
|||
} elseif ($resource->getCollection() === 'sites') {
|
||||
$commands = [];
|
||||
|
||||
$commands[] = $deployment->getAttribute('installCommand', '');
|
||||
$commands[] = $deployment->getAttribute('buildCommand', '');
|
||||
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
|
||||
$envCommand = '';
|
||||
$bundleCommand = '';
|
||||
|
||||
if (!is_null($framework)) {
|
||||
$adapter = ($framework['adapters'] ?? [])[$resource->getAttribute('adapter', '')] ?? null;
|
||||
if (!is_null($adapter) && isset($adapter['bundleCommand'])) {
|
||||
$commands[] = $adapter['bundleCommand'];
|
||||
}
|
||||
if (!is_null($adapter) && isset($adapter['envCommand'])) {
|
||||
$commands[] = $adapter['envCommand'];
|
||||
$envCommand = $adapter['envCommand'];
|
||||
}
|
||||
if (!is_null($adapter) && isset($adapter['bundleCommand'])) {
|
||||
$bundleCommand = $adapter['bundleCommand'];
|
||||
}
|
||||
}
|
||||
|
||||
$commands[] = $envCommand;
|
||||
$commands[] = $deployment->getAttribute('installCommand', '');
|
||||
$commands[] = $deployment->getAttribute('buildCommand', '');
|
||||
$commands[] = $bundleCommand;
|
||||
|
||||
$commands = array_filter($commands, fn ($command) => !empty($command));
|
||||
|
||||
return implode(' && ', $commands);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,165 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Deployments\Template;
|
||||
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Platform\Modules\Compute\Base;
|
||||
use Appwrite\SDK\AuthType;
|
||||
use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Response;
|
||||
use Utopia\Database\Database;
|
||||
use Utopia\Database\Document;
|
||||
use Utopia\Database\Helpers\ID;
|
||||
use Utopia\Database\Helpers\Permission;
|
||||
use Utopia\Database\Helpers\Role;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
use HTTP;
|
||||
|
||||
public static function getName()
|
||||
{
|
||||
return 'createTemplateDeployment';
|
||||
}
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/sites/:siteId/deployments/template')
|
||||
->desc('Create deployment')
|
||||
->groups(['api', 'sites'])
|
||||
->label('scope', 'sites.write')
|
||||
->label('event', 'sites.[siteId].deployments.[deploymentId].create')
|
||||
->label('audits.event', 'deployment.create')
|
||||
->label('audits.resource', 'site/{request.siteId}')
|
||||
->label('sdk', new Method(
|
||||
namespace: 'sites',
|
||||
name: 'createTemplateDeployment',
|
||||
description: <<<EOT
|
||||
Create a deployment based on a template.
|
||||
|
||||
Use this endpoint with combination of [listTemplates](https://appwrite.io/docs/server/sites#listTemplates) to find the template details.
|
||||
EOT,
|
||||
auth: [AuthType::KEY],
|
||||
responses: [
|
||||
new SDKResponse(
|
||||
code: Response::STATUS_CODE_ACCEPTED,
|
||||
model: Response::MODEL_DEPLOYMENT,
|
||||
)
|
||||
],
|
||||
))
|
||||
->param('siteId', '', new UID(), 'Site ID.')
|
||||
->param('repository', '', new Text(128, 0), 'Repository name of the template.')
|
||||
->param('owner', '', new Text(128, 0), 'The name of the owner of the template.')
|
||||
->param('rootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.')
|
||||
->param('version', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.')
|
||||
->param('activate', false, new Boolean(), 'Automatically activate the deployment when it is finished building.', true)
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->inject('project')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForBuilds')
|
||||
->inject('gitHub')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $repository, string $owner, string $rootDirectory, string $version, bool $activate, Request $request, Response $response, Database $dbForProject, Database $dbForPlatform, Document $project, Event $queueForEvents, Build $queueForBuilds, GitHub $github)
|
||||
{
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
if ($site->isEmpty()) {
|
||||
throw new Exception(Exception::SITE_NOT_FOUND);
|
||||
}
|
||||
|
||||
$template = new Document([
|
||||
'repositoryName' => $repository,
|
||||
'ownerName' => $owner,
|
||||
'rootDirectory' => $rootDirectory,
|
||||
'version' => $version
|
||||
]);
|
||||
|
||||
if (!empty($providerRepositoryId)) {
|
||||
$installation = $dbForPlatform->getDocument('installations', $site->getAttribute('installationId'));
|
||||
|
||||
$deployment = $this->redeployVcsSite($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, $template, $github);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('siteId', $site->getId())
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $site->getId(),
|
||||
'resourceInternalId' => $site->getInternalId(),
|
||||
'resourceType' => 'sites',
|
||||
'installCommand' => $site->getAttribute('installCommand', ''),
|
||||
'buildCommand' => $site->getAttribute('buildCommand', ''),
|
||||
'outputDirectory' => $site->getAttribute('outputDirectory', ''),
|
||||
'type' => 'manual',
|
||||
'search' => implode(' ', [$deploymentId]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
|
||||
// Preview deployments url
|
||||
$projectId = $project->getId();
|
||||
|
||||
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
|
||||
$previewDomain = "{$deploymentId}-{$projectId}.{$sitesDomain}";
|
||||
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => \md5($previewDomain),
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $previewDomain,
|
||||
'resourceType' => 'deployment',
|
||||
'resourceId' => $deploymentId,
|
||||
'resourceInternalId' => $deployment->getInternalId(),
|
||||
'status' => 'verified',
|
||||
'certificateId' => '',
|
||||
]))
|
||||
);
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($site)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
|
||||
$queueForEvents
|
||||
->setParam('siteId', $site->getId())
|
||||
->setParam('deploymentId', $deployment->getId());
|
||||
|
||||
$response
|
||||
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
|
||||
->dynamic($deployment, Response::MODEL_DEPLOYMENT);
|
||||
}
|
||||
}
|
||||
|
|
@ -2,7 +2,6 @@
|
|||
|
||||
namespace Appwrite\Platform\Modules\Sites\Http\Sites;
|
||||
|
||||
use Appwrite\Event\Build;
|
||||
use Appwrite\Event\Event;
|
||||
use Appwrite\Extend\Exception;
|
||||
use Appwrite\Messaging\Adapter\Realtime;
|
||||
|
|
@ -24,13 +23,11 @@ use Utopia\Database\Helpers\Role;
|
|||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Swoole\Request;
|
||||
use Utopia\System\System;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Range;
|
||||
use Utopia\Validator\Text;
|
||||
use Utopia\Validator\WhiteList;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
class Create extends Base
|
||||
{
|
||||
|
|
@ -83,29 +80,21 @@ class Create extends Base
|
|||
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the site.', true)
|
||||
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the site? In silent mode, comments will not be made on commits and pull requests.', true)
|
||||
->param('providerRootDirectory', '', new Text(128, 0), 'Path to site code in the linked repo.', true)
|
||||
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
|
||||
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
|
||||
->param('templateRootDirectory', '', new Text(128, 0), 'Path to site code in the template repo.', true)
|
||||
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the site template.', true)
|
||||
->param('specification', APP_COMPUTE_SPECIFICATION_DEFAULT, fn (array $plan) => new FrameworkSpecification(
|
||||
$plan,
|
||||
Config::getParam('framework-specifications', []),
|
||||
App::getEnv('_APP_COMPUTE_CPUS', APP_COMPUTE_CPUS_DEFAULT),
|
||||
App::getEnv('_APP_COMPUTE_MEMORY', APP_COMPUTE_MEMORY_DEFAULT)
|
||||
), 'Framework specification for the site and builds.', true, ['plan'])
|
||||
->inject('request')
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('project')
|
||||
->inject('user')
|
||||
->inject('queueForEvents')
|
||||
->inject('queueForBuilds')
|
||||
->inject('dbForPlatform')
|
||||
->inject('gitHub')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $subdomain, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, string $specification, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForPlatform, GitHub $github)
|
||||
public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $subdomain, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForPlatform)
|
||||
{
|
||||
if (!empty($adapter)) {
|
||||
$configFramework = Config::getParam('frameworks')[$framework] ?? [];
|
||||
|
|
@ -139,20 +128,6 @@ class Create extends Base
|
|||
throw new Exception(Exception::SITE_FRAMEWORK_UNSUPPORTED, 'Framework "' . $framework . '" is not supported');
|
||||
}
|
||||
|
||||
// build from template
|
||||
$template = new Document([]);
|
||||
if (
|
||||
!empty($templateRepository)
|
||||
&& !empty($templateOwner)
|
||||
&& !empty($templateRootDirectory)
|
||||
&& !empty($templateVersion)
|
||||
) {
|
||||
$template->setAttribute('repositoryName', $templateRepository)
|
||||
->setAttribute('ownerName', $templateOwner)
|
||||
->setAttribute('rootDirectory', $templateRootDirectory)
|
||||
->setAttribute('version', $templateVersion);
|
||||
}
|
||||
|
||||
$installation = $dbForPlatform->getDocument('installations', $installationId);
|
||||
|
||||
if (!empty($installationId) && $installation->isEmpty()) {
|
||||
|
|
@ -220,57 +195,6 @@ class Create extends Base
|
|||
|
||||
$site = $dbForProject->updateDocument('sites', $site->getId(), $site);
|
||||
|
||||
if (!empty($providerRepositoryId)) {
|
||||
// Deploy VCS
|
||||
$this->redeployVcsSite($request, $site, $project, $installation, $dbForProject, $dbForPlatform, $queueForBuilds, $template, $github);
|
||||
} elseif (!$template->isEmpty()) {
|
||||
// Deploy non-VCS from template
|
||||
$deploymentId = ID::unique();
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
Permission::update(Role::any()),
|
||||
Permission::delete(Role::any()),
|
||||
],
|
||||
'resourceId' => $site->getId(),
|
||||
'resourceInternalId' => $site->getInternalId(),
|
||||
'resourceType' => 'sites',
|
||||
'installCommand' => $site->getAttribute('installCommand', ''),
|
||||
'buildCommand' => $site->getAttribute('buildCommand', ''),
|
||||
'outputDirectory' => $site->getAttribute('outputDirectory', ''),
|
||||
'type' => 'manual',
|
||||
'search' => implode(' ', [$deploymentId]),
|
||||
'activate' => true,
|
||||
]));
|
||||
|
||||
// Preview deployments url
|
||||
$projectId = $project->getId();
|
||||
|
||||
$sitesDomain = System::getEnv('_APP_DOMAIN_SITES', '');
|
||||
$previewDomain = "{$deploymentId}-{$projectId}.{$sitesDomain}";
|
||||
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
'$id' => \md5($previewDomain),
|
||||
'projectId' => $project->getId(),
|
||||
'projectInternalId' => $project->getInternalId(),
|
||||
'domain' => $previewDomain,
|
||||
'resourceType' => 'deployment',
|
||||
'resourceId' => $deploymentId,
|
||||
'resourceInternalId' => $deployment->getInternalId(),
|
||||
'status' => 'verified',
|
||||
'certificateId' => '',
|
||||
]))
|
||||
);
|
||||
|
||||
$queueForBuilds
|
||||
->setType(BUILD_TYPE_DEPLOYMENT)
|
||||
->setResource($site)
|
||||
->setDeployment($deployment)
|
||||
->setTemplate($template);
|
||||
}
|
||||
|
||||
if (!empty($sitesDomain)) {
|
||||
$rule = Authorization::skip(
|
||||
fn () => $dbForPlatform->createDocument('rules', new Document([
|
||||
|
|
|
|||
|
|
@ -56,14 +56,13 @@ class Create extends Base
|
|||
->param('siteId', '', new UID(), 'Site unique ID.', false)
|
||||
->param('key', null, new Text(Database::LENGTH_KEY), 'Variable key. Max length: ' . Database::LENGTH_KEY . ' chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', false)
|
||||
->param('secret', false, new Boolean(), 'Is secret? Secret variables can only be updated or deleted, they cannot be read.', true)
|
||||
->param('secret', true, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->inject('dbForPlatform')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $key, string $value, bool $secret, Response $response, Database $dbForProject, Database $dbForPlatform)
|
||||
public function action(string $siteId, string $key, string $value, bool $secret, Response $response, Database $dbForProject)
|
||||
{
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ use Utopia\Database\Exception\Duplicate as DuplicateException;
|
|||
use Utopia\Database\Validator\UID;
|
||||
use Utopia\Platform\Action;
|
||||
use Utopia\Platform\Scope\HTTP;
|
||||
use Utopia\Validator\Boolean;
|
||||
use Utopia\Validator\Text;
|
||||
|
||||
class Update extends Base
|
||||
|
|
@ -52,12 +53,13 @@ class Update extends Base
|
|||
->param('variableId', '', new UID(), 'Variable unique ID.', false)
|
||||
->param('key', null, new Text(255), 'Variable key. Max length: 255 chars.', false)
|
||||
->param('value', null, new Text(8192, 0), 'Variable value. Max length: 8192 chars.', true)
|
||||
->param('secret', null, new Boolean(), 'Secret variables can be updated or deleted, but only sites can read them during build and runtime.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->callback([$this, 'action']);
|
||||
}
|
||||
|
||||
public function action(string $siteId, string $variableId, string $key, ?string $value, Response $response, Database $dbForProject)
|
||||
public function action(string $siteId, string $variableId, string $key, ?string $value, ?bool $secret, Response $response, Database $dbForProject)
|
||||
{
|
||||
$site = $dbForProject->getDocument('sites', $siteId);
|
||||
|
||||
|
|
@ -74,9 +76,14 @@ class Update extends Base
|
|||
throw new Exception(Exception::VARIABLE_NOT_FOUND);
|
||||
}
|
||||
|
||||
if ($variable->getAttribute('secret') === true && $secret === false) {
|
||||
throw new Exception(Exception::VARIABLE_CANNOT_UNSET_SECRET);
|
||||
}
|
||||
|
||||
$variable
|
||||
->setAttribute('key', $key)
|
||||
->setAttribute('value', $value ?? $variable->getAttribute('value'))
|
||||
->setAttribute('secret', $secret ?? $variable->getAttribute('secret'))
|
||||
->setAttribute('search', implode(' ', [$variableId, $site->getId(), $key, 'site']));
|
||||
|
||||
try {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use Appwrite\Platform\Modules\Sites\Http\Deployments\Create as CreateDeployment;
|
|||
use Appwrite\Platform\Modules\Sites\Http\Deployments\Delete as DeleteDeployment;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Deployments\Download\Get as DownloadDeployment;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Deployments\Get as GetDeployment;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Deployments\Template\Create as CreateTemplateDeployment;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Deployments\Update as UpdateDeployment;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Deployments\XList as ListDeployments;
|
||||
use Appwrite\Platform\Modules\Sites\Http\Frameworks\XList as ListFrameworks;
|
||||
|
|
@ -49,6 +50,7 @@ class Http extends Service
|
|||
|
||||
// Deployments
|
||||
$this->addAction(CreateDeployment::getName(), new CreateDeployment());
|
||||
$this->addAction(CreateTemplateDeployment::getName(), new CreateTemplateDeployment());
|
||||
$this->addAction(GetDeployment::getName(), new GetDeployment());
|
||||
$this->addAction(ListDeployments::getName(), new ListDeployments());
|
||||
$this->addAction(UpdateDeployment::getName(), new UpdateDeployment());
|
||||
|
|
|
|||
32
src/Appwrite/Transformation/Adapter.php
Normal file
32
src/Appwrite/Transformation/Adapter.php
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Transformation;
|
||||
|
||||
abstract class Adapter
|
||||
{
|
||||
protected mixed $input;
|
||||
protected mixed $output;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public function setInput(mixed $input): self
|
||||
{
|
||||
$this->input = $input;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOutput(): mixed
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $traits
|
||||
*/
|
||||
abstract public function isValid(array $traits): bool;
|
||||
|
||||
abstract public function transform(): void;
|
||||
}
|
||||
26
src/Appwrite/Transformation/Adapter/Mock.php
Normal file
26
src/Appwrite/Transformation/Adapter/Mock.php
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Transformation\Adapter;
|
||||
|
||||
use Appwrite\Transformation\Adapter;
|
||||
|
||||
class Mock extends Adapter
|
||||
{
|
||||
/**
|
||||
* @param array<mixed> $traits Mock traits
|
||||
*/
|
||||
public function isValid(array $traits): bool
|
||||
{
|
||||
if ($traits['mock'] === true) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function transform(): void
|
||||
{
|
||||
$this->output = $this->input;
|
||||
$this->output = "Mock: " . $this->output;
|
||||
}
|
||||
}
|
||||
201
src/Appwrite/Transformation/Adapter/Preview.php
Normal file
201
src/Appwrite/Transformation/Adapter/Preview.php
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Transformation\Adapter;
|
||||
|
||||
use Appwrite\Transformation\Adapter;
|
||||
|
||||
class Preview extends Adapter
|
||||
{
|
||||
/**
|
||||
* @param array<mixed> $traits Proxied response headers
|
||||
*/
|
||||
public function isValid(array $traits): bool
|
||||
{
|
||||
$contentType = '';
|
||||
|
||||
foreach ($traits as $key => $value) {
|
||||
if (\strtolower($key) === 'content-type') {
|
||||
$contentType = $value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (\str_contains($contentType, 'text/html')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function transform(): void
|
||||
{
|
||||
$this->output = $this->input;
|
||||
|
||||
$banner = <<<EOT
|
||||
<style>
|
||||
#appwrite-preview {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
right: 16px;
|
||||
bottom: 16px;
|
||||
z-index: 1;
|
||||
border-radius: var(--border-radius-S, 8px);
|
||||
border: var(--border-width-S, 1px) solid var(--color-border-neutral, #EDEDF0);
|
||||
background: var(--color-bgColor-neutral-primary, #FFF);
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04);
|
||||
padding: var(--space-3, 6px) var(--space-4, 8px);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: var(--gap-XXS, 4px);
|
||||
cursor: pointer;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#appwrite-preview-close {
|
||||
position: absolute;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
border-radius: var(--border-radius-S, 8px);
|
||||
background: linear-gradient(270deg, #FFF 69.64%, rgba(255, 255, 255, 0.00) 114.29%);
|
||||
height: 100%;
|
||||
aspect-ratio: 1 / 1;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
}
|
||||
|
||||
#appwrite-preview-logo-dark {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appwrite-preview:hover #appwrite-preview-close {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
#appwrite-preview-text {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
line-height: 130%;
|
||||
letter-spacing: -0.12px;
|
||||
}
|
||||
|
||||
#appwrite-preview-close-text {
|
||||
opacity: 0;
|
||||
transition: opacity 0.3s;
|
||||
position: absolute;
|
||||
bottom: calc(15px + 4px);
|
||||
display: flex;
|
||||
padding: var(--space-1, 2px) var(--space-2, 4px);
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 130%;
|
||||
letter-spacing: -0.12px;
|
||||
border-radius: var(--border-radius-XS, 6px);
|
||||
background: #EDEDF0;
|
||||
}
|
||||
|
||||
#appwrite-preview-close:hover #appwrite-preview-close-text {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#appwrite-preview {
|
||||
border: var(--border-width-S, 1px) solid var(--color-border-neutral, #2D2D31);
|
||||
background: var(--color-bgColor-neutral-primary, #1D1D21);
|
||||
box-shadow: 0px 1px 3px 0px rgba(0, 0, 0, 0.03), 0px 4px 4px 0px rgba(0, 0, 0, 0.04);
|
||||
}
|
||||
|
||||
#appwrite-preview-text {
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
}
|
||||
|
||||
#appwrite-preview-logo-dark {
|
||||
display: block;
|
||||
}
|
||||
|
||||
#appwrite-preview-logo-light {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#appwrite-preview-close {
|
||||
background: linear-gradient(270deg, #1D1D21 69.64%, rgba(29, 29, 33, 0.00) 114.29%);
|
||||
}
|
||||
|
||||
#appwrite-preview-close-text {
|
||||
background: #2D2D31;
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<button id="appwrite-preview">
|
||||
<p id="appwrite-preview-text">Preview by</p>
|
||||
|
||||
<div id="appwrite-preview-close">
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M3.43451 3.43439C3.74693 3.12197 4.25346 3.12197 4.56588 3.43439L8.0002 6.8687L11.4345 3.43439C11.7469 3.12197 12.2535 3.12197 12.5659 3.43439C12.8783 3.74681 12.8783 4.25334 12.5659 4.56576L9.13157 8.00007L12.5659 11.4344C12.8783 11.7468 12.8783 12.2533 12.5659 12.5658C12.2535 12.8782 11.7469 12.8782 11.4345 12.5658L8.0002 9.13144L4.56588 12.5658C4.25346 12.8782 3.74693 12.8782 3.43451 12.5658C3.12209 12.2533 3.12209 11.7468 3.43451 11.4344L6.86882 8.00007L3.43451 4.56576C3.12209 4.25334 3.12209 3.74681 3.43451 3.43439Z" fill="#97979B"/>
|
||||
</svg>
|
||||
|
||||
<p id="appwrite-preview-close-text">Hide</p>
|
||||
</div>
|
||||
|
||||
<svg id="appwrite-preview-logo-light" width="65" height="12" viewBox="0 0 65 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.9862 9.74762C20.0493 9.74762 20.5867 9.191 20.8204 8.81202H20.9255C20.9722 9.21468 21.2526 9.59366 21.8017 9.59366H22.8414V8.40936H22.5727C22.3858 8.40936 22.2924 8.30277 22.2924 8.13697V3.38795H20.9138V4.1459H20.8087C20.54 3.76692 19.9792 3.23399 18.9511 3.23399C17.3156 3.23399 16.1006 4.60777 16.1006 6.4908C16.1006 8.37383 17.3389 9.74762 18.9862 9.74762ZM19.2315 8.39752C18.2619 8.39752 17.5025 7.6751 17.5025 6.50265C17.5025 5.35388 18.2385 4.57224 19.2198 4.57224C20.1544 4.57224 20.9372 5.27098 20.9372 6.50265C20.9372 7.55667 20.2713 8.39752 19.2315 8.39752Z" fill="#2D2D31"/>
|
||||
<path d="M23.6553 12H25.0339V8.81202H25.139C25.396 9.191 25.9451 9.74762 27.0316 9.74762C28.6672 9.74762 29.8588 8.35015 29.8588 6.4908C29.8588 4.61962 28.5854 3.23399 26.9381 3.23399C25.8867 3.23399 25.3727 3.81429 25.1273 4.13405H25.0222V3.38795H23.6553V12ZM26.7395 8.43305C25.7933 8.43305 25.0105 7.72247 25.0105 6.4908C25.0105 5.43678 25.6764 4.54856 26.7162 4.54856C27.6858 4.54856 28.4452 5.31835 28.4452 6.4908C28.4452 7.63957 27.7092 8.43305 26.7395 8.43305Z" fill="#2D2D31"/>
|
||||
<path d="M30.5701 12H31.9487V8.81202H32.0538C32.3108 9.191 32.8599 9.74762 33.9464 9.74762C35.582 9.74762 36.66 8.35015 36.66 6.4908C36.66 4.61962 35.5002 3.23399 33.8529 3.23399C32.8015 3.23399 32.2875 3.81429 32.0421 4.13405H31.937V3.38795H30.5701V12ZM33.6543 8.43305C32.708 8.43305 31.9253 7.72247 31.9253 6.4908C31.9253 5.43678 32.5912 4.54856 33.631 4.54856C34.6006 4.54856 35.36 5.31835 35.36 6.4908C35.36 7.63957 34.624 8.43305 33.6543 8.43305Z" fill="#2D2D31"/>
|
||||
<path d="M38.4823 9.73776H40.4333L41.5431 4.87031H41.6132L42.7231 9.73776H44.6624L46.2153 3.53205H44.8259L43.7161 8.41135H43.6109L42.5011 3.53205H40.6669L39.5454 8.41135H39.4403L38.3421 3.53205H36.8701L38.4823 9.73776Z" fill="#2D2D31"/>
|
||||
<path d="M46.9137 9.73776H48.2923V6.67044C48.2923 5.49798 48.8297 4.77556 49.8344 4.77556H50.4418V3.37809H49.9862C49.2035 3.37809 48.6077 3.92287 48.374 4.44396H48.2806V3.53205H46.9137V9.73776Z" fill="#2D2D31"/>
|
||||
<path d="M57.2829 9.73776H58.3577V8.49425H57.2946C56.874 8.49425 56.6988 8.30476 56.6988 7.86657V4.76372H58.4278V3.53205H56.6988V1.79114H55.3903V3.53205H54.2454V4.76372H55.3085V7.87842C55.3085 9.19299 56.0913 9.73776 57.2829 9.73776Z" fill="#2D2D31"/>
|
||||
<path d="M62.0561 9.74762C63.3295 9.74762 64.451 9.1081 64.8482 7.81721L63.5865 7.5093C63.3645 8.19619 62.722 8.55148 62.0444 8.55148C61.0397 8.55148 60.3738 7.88827 60.3621 6.84609H65.0001V6.45527C65.0001 4.60777 63.8669 3.23399 61.9977 3.23399C60.3504 3.23399 58.9368 4.54856 58.9368 6.50265C58.9368 8.39752 60.1869 9.74762 62.0561 9.74762ZM60.3738 5.8276C60.4556 5.08149 61.1215 4.45381 61.9977 4.45381C62.8388 4.45381 63.5281 4.98675 63.5982 5.8276H60.3738Z" fill="#2D2D31"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.6325 9.73776H52.2539V4.76372H51.1791V3.53205H53.6325V9.73776Z" fill="#2D2D31"/>
|
||||
<path d="M52.841 2.67085C53.3434 2.67085 53.7172 2.29187 53.7172 1.79447C53.7172 1.30891 53.3434 0.929932 52.841 0.929932C52.3387 0.929932 51.9648 1.30891 51.9648 1.79447C51.9648 2.29187 52.3387 2.67085 52.841 2.67085Z" fill="#2D2D31"/>
|
||||
<path d="M12.0363 8.21609V10.9548H5.29451C3.33035 10.9548 1.61537 9.85334 0.697814 8.21609C0.564426 7.97807 0.447681 7.72836 0.349751 7.46917C0.157509 6.96127 0.0366636 6.41627 0 5.84762V5.10717C0.00795985 4.98044 0.0205026 4.85471 0.0369048 4.73048C0.0704326 4.47553 0.121086 4.22606 0.18766 3.98356C0.817453 1.68455 2.86531 0 5.29451 0C7.72371 0 9.77132 1.68455 10.4011 3.98356H7.51844C7.04519 3.23415 6.22605 2.7387 5.29451 2.7387C4.36296 2.7387 3.54382 3.23415 3.07057 3.98356C2.92633 4.21137 2.81441 4.46258 2.74108 4.73048C2.67596 4.968 2.64122 5.21846 2.64122 5.47739C2.64122 6.2624 2.96106 6.96999 3.47387 7.46917C3.94905 7.93251 4.5897 8.21609 5.29451 8.21609H12.0363Z" fill="#FD366E"/>
|
||||
<path d="M12.0364 4.73047V7.46917H7.11523C7.62804 6.96998 7.94788 6.2624 7.94788 5.47739C7.94788 5.21846 7.91315 4.96799 7.84802 4.73047H12.0364Z" fill="#FD366E"/>
|
||||
</svg>
|
||||
|
||||
<svg id="appwrite-preview-logo-dark" width="65" height="12" viewBox="0 0 65 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M18.9862 9.74762C20.0493 9.74762 20.5867 9.191 20.8204 8.81202H20.9255C20.9722 9.21468 21.2526 9.59366 21.8017 9.59366H22.8414V8.40936H22.5727C22.3858 8.40936 22.2924 8.30277 22.2924 8.13697V3.38795H20.9138V4.1459H20.8087C20.54 3.76692 19.9792 3.23399 18.9511 3.23399C17.3156 3.23399 16.1006 4.60777 16.1006 6.4908C16.1006 8.37383 17.3389 9.74762 18.9862 9.74762ZM19.2315 8.39752C18.2619 8.39752 17.5025 7.6751 17.5025 6.50265C17.5025 5.35388 18.2385 4.57224 19.2198 4.57224C20.1544 4.57224 20.9372 5.27098 20.9372 6.50265C20.9372 7.55667 20.2713 8.39752 19.2315 8.39752Z" fill="#EDEDF0"/>
|
||||
<path d="M23.6553 12H25.0339V8.81202H25.139C25.396 9.191 25.9451 9.74762 27.0316 9.74762C28.6672 9.74762 29.8588 8.35015 29.8588 6.4908C29.8588 4.61962 28.5854 3.23399 26.9381 3.23399C25.8867 3.23399 25.3727 3.81429 25.1273 4.13405H25.0222V3.38795H23.6553V12ZM26.7395 8.43305C25.7933 8.43305 25.0105 7.72247 25.0105 6.4908C25.0105 5.43678 25.6764 4.54856 26.7162 4.54856C27.6858 4.54856 28.4452 5.31835 28.4452 6.4908C28.4452 7.63957 27.7092 8.43305 26.7395 8.43305Z" fill="#EDEDF0"/>
|
||||
<path d="M30.5701 12H31.9487V8.81202H32.0538C32.3108 9.191 32.8599 9.74762 33.9464 9.74762C35.582 9.74762 36.66 8.35015 36.66 6.4908C36.66 4.61962 35.5002 3.23399 33.8529 3.23399C32.8015 3.23399 32.2875 3.81429 32.0421 4.13405H31.937V3.38795H30.5701V12ZM33.6543 8.43305C32.708 8.43305 31.9253 7.72247 31.9253 6.4908C31.9253 5.43678 32.5912 4.54856 33.631 4.54856C34.6006 4.54856 35.36 5.31835 35.36 6.4908C35.36 7.63957 34.624 8.43305 33.6543 8.43305Z" fill="#EDEDF0"/>
|
||||
<path d="M38.4823 9.73776H40.4333L41.5431 4.87031H41.6132L42.7231 9.73776H44.6624L46.2153 3.53205H44.8259L43.7161 8.41135H43.6109L42.5011 3.53205H40.6669L39.5454 8.41135H39.4403L38.3421 3.53205H36.8701L38.4823 9.73776Z" fill="#EDEDF0"/>
|
||||
<path d="M46.9137 9.73776H48.2923V6.67044C48.2923 5.49798 48.8297 4.77556 49.8344 4.77556H50.4418V3.37809H49.9862C49.2035 3.37809 48.6077 3.92287 48.374 4.44396H48.2806V3.53205H46.9137V9.73776Z" fill="#EDEDF0"/>
|
||||
<path d="M57.2829 9.73776H58.3577V8.49425H57.2946C56.874 8.49425 56.6988 8.30476 56.6988 7.86657V4.76372H58.4278V3.53205H56.6988V1.79114H55.3903V3.53205H54.2454V4.76372H55.3085V7.87842C55.3085 9.19299 56.0913 9.73776 57.2829 9.73776Z" fill="#EDEDF0"/>
|
||||
<path d="M62.0561 9.74762C63.3295 9.74762 64.451 9.1081 64.8482 7.81721L63.5865 7.5093C63.3645 8.19619 62.722 8.55148 62.0444 8.55148C61.0397 8.55148 60.3738 7.88827 60.3621 6.84609H65.0001V6.45527C65.0001 4.60777 63.8669 3.23399 61.9977 3.23399C60.3504 3.23399 58.9368 4.54856 58.9368 6.50265C58.9368 8.39752 60.1869 9.74762 62.0561 9.74762ZM60.3738 5.8276C60.4556 5.08149 61.1215 4.45381 61.9977 4.45381C62.8388 4.45381 63.5281 4.98675 63.5982 5.8276H60.3738Z" fill="#EDEDF0"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M53.6325 9.73776H52.2539V4.76372H51.1791V3.53205H53.6325V9.73776Z" fill="#EDEDF0"/>
|
||||
<path d="M52.841 2.67085C53.3434 2.67085 53.7172 2.29187 53.7172 1.79447C53.7172 1.30891 53.3434 0.929932 52.841 0.929932C52.3387 0.929932 51.9648 1.30891 51.9648 1.79447C51.9648 2.29187 52.3387 2.67085 52.841 2.67085Z" fill="#EDEDF0"/>
|
||||
<path d="M12.0363 8.21609V10.9548H5.29451C3.33035 10.9548 1.61537 9.85334 0.697814 8.21609C0.564426 7.97807 0.447681 7.72836 0.349751 7.46917C0.157509 6.96127 0.0366636 6.41627 0 5.84762V5.10717C0.00795985 4.98044 0.0205026 4.85471 0.0369048 4.73048C0.0704326 4.47553 0.121086 4.22606 0.18766 3.98356C0.817453 1.68455 2.86531 0 5.29451 0C7.72371 0 9.77132 1.68455 10.4011 3.98356H7.51844C7.04519 3.23415 6.22605 2.7387 5.29451 2.7387C4.36296 2.7387 3.54382 3.23415 3.07057 3.98356C2.92633 4.21137 2.81441 4.46258 2.74108 4.73048C2.67596 4.968 2.64122 5.21846 2.64122 5.47739C2.64122 6.2624 2.96106 6.96999 3.47387 7.46917C3.94905 7.93251 4.5897 8.21609 5.29451 8.21609H12.0363Z" fill="#FD366E"/>
|
||||
<path d="M12.0364 4.73047V7.46917H7.11523C7.62804 6.96998 7.94788 6.2624 7.94788 5.47739C7.94788 5.21846 7.91315 4.96799 7.84802 4.73047H12.0364Z" fill="#FD366E"/>
|
||||
</svg>
|
||||
</button>
|
||||
|
||||
<script>
|
||||
(function() {
|
||||
var banner = document.getElementById("appwrite-preview");
|
||||
banner.addEventListener("click", function() {
|
||||
banner.style.opacity = 0;
|
||||
setTimeout(() => {
|
||||
banner.style.display = 'none';
|
||||
}, 350);
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
EOT;
|
||||
|
||||
$this->output .= $banner;
|
||||
}
|
||||
}
|
||||
74
src/Appwrite/Transformation/Transformation.php
Normal file
74
src/Appwrite/Transformation/Transformation.php
Normal file
|
|
@ -0,0 +1,74 @@
|
|||
<?php
|
||||
|
||||
namespace Appwrite\Transformation;
|
||||
|
||||
class Transformation
|
||||
{
|
||||
/**
|
||||
* @var array<Adapter> $adapters
|
||||
*/
|
||||
protected array $adapters;
|
||||
|
||||
/**
|
||||
* @var array<mixed> $traits
|
||||
*/
|
||||
protected array $traits;
|
||||
|
||||
protected mixed $input;
|
||||
protected mixed $output;
|
||||
|
||||
/**
|
||||
* @param array<Adapter> $adapters
|
||||
*/
|
||||
public function __construct(array $adapters = [])
|
||||
{
|
||||
$this->adapters = $adapters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<mixed> $traits
|
||||
*/
|
||||
public function setTraits(array $traits): self
|
||||
{
|
||||
$this->traits = $traits;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setInput(mixed $input): self
|
||||
{
|
||||
$this->input = $input;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addAdapter(Adapter $adapter): self
|
||||
{
|
||||
$this->adapters[] = $adapter;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function transform(): bool
|
||||
{
|
||||
foreach ($this->adapters as $adapter) {
|
||||
if (!$adapter->isValid($this->traits)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
$output = $this->input;
|
||||
|
||||
foreach ($this->adapters as $adapter) {
|
||||
$adapter->setInput($output);
|
||||
$adapter->transform();
|
||||
$output = $adapter->getOutput();
|
||||
}
|
||||
|
||||
$this->output = $output;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getOutput(): mixed
|
||||
{
|
||||
return $this->output;
|
||||
}
|
||||
}
|
||||
|
|
@ -2,8 +2,10 @@
|
|||
|
||||
namespace Appwrite\Utopia;
|
||||
|
||||
use Appwrite\Auth\Auth;
|
||||
use Appwrite\Utopia\Request\Filter;
|
||||
use Swoole\Http\Request as SwooleRequest;
|
||||
use Utopia\Database\Validator\Authorization;
|
||||
use Utopia\Route;
|
||||
use Utopia\Swoole\Request as UtopiaRequest;
|
||||
|
||||
|
|
@ -180,4 +182,27 @@ class Request extends UtopiaRequest
|
|||
$headers = $this->getHeaders();
|
||||
return $headers[$key] ?? $default;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get User Agent
|
||||
*
|
||||
* Method for getting User Agent. Preferring forwarded agent for privileged users; otherwise returns default.
|
||||
*
|
||||
* @param string $default
|
||||
* @return string
|
||||
*/
|
||||
public function getUserAgent(string $default = ''): string
|
||||
{
|
||||
$forwardedUserAgent = $this->getHeader('x-forwarded-user-agent');
|
||||
if (!empty($forwardedUserAgent)) {
|
||||
$roles = Authorization::getRoles();
|
||||
$isAppUser = Auth::isAppUser($roles);
|
||||
|
||||
if ($isAppUser) {
|
||||
return $forwardedUserAgent;
|
||||
}
|
||||
}
|
||||
|
||||
return UtopiaRequest::getUserAgent($default);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2307,6 +2307,60 @@ class AccountCustomClientTest extends Scope
|
|||
$this->assertNotEmpty($response['body']['$id']);
|
||||
$this->assertNotEmpty($response['body']['expire']);
|
||||
$this->assertEmpty($response['body']['secret']);
|
||||
$this->assertEquals('browser', $response['body']['clientType']);
|
||||
$this->assertEquals('CH', $response['body']['clientCode']);
|
||||
$this->assertEquals('Chrome', $response['body']['clientName']);
|
||||
|
||||
// Forwarded User Agent with API Key
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['id'] . '/tokens', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => 60
|
||||
]);
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
$secret = $response['body']['secret'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
'x-forwarded-user-agent' => 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||
], [
|
||||
'userId' => $userId,
|
||||
'secret' => $secret
|
||||
]);
|
||||
|
||||
$this->assertEquals('browser', $response['body']['clientType']);
|
||||
$this->assertEquals('CM', actual: $response['body']['clientCode']);
|
||||
$this->assertEquals('Chrome Mobile', $response['body']['clientName']);
|
||||
|
||||
// Forwarded User Agent without API Key
|
||||
$response = $this->client->call(Client::METHOD_POST, '/users/' . $data['id'] . '/tokens', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
], [
|
||||
'expire' => 60
|
||||
]);
|
||||
|
||||
$userId = $response['body']['userId'];
|
||||
$secret = $response['body']['secret'];
|
||||
|
||||
$response = $this->client->call(Client::METHOD_POST, '/account/sessions/token', [
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'x-forwarded-user-agent' => 'Mozilla/5.0 (Linux; Android 10; K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/114.0.0.0 Mobile Safari/537.36'
|
||||
], [
|
||||
'userId' => $userId,
|
||||
'secret' => $secret
|
||||
]);
|
||||
|
||||
$this->assertEquals('browser', $response['body']['clientType']);
|
||||
$this->assertEquals('CH', $response['body']['clientCode']);
|
||||
$this->assertEquals('Chrome', $response['body']['clientName']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
|
|||
|
|
@ -82,6 +82,46 @@ trait FunctionsBase
|
|||
return $variable;
|
||||
}
|
||||
|
||||
protected function getVariable(string $functionId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function updateVariable(string $functionId, string $variableId, mixed $params): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_PUT, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function listVariables(string $functionId, mixed $params = []): mixed
|
||||
{
|
||||
$variables = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
protected function deleteVariable(string $functionId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_DELETE, '/functions/' . $functionId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getFunction(string $functionId): mixed
|
||||
{
|
||||
$function = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId, array_merge([
|
||||
|
|
@ -166,6 +206,16 @@ trait FunctionsBase
|
|||
return $deployment;
|
||||
}
|
||||
|
||||
protected function createTemplateDeployment(string $functionId, mixed $params = []): mixed
|
||||
{
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/functions/' . $functionId . '/deployments/template', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
||||
protected function getFunctionUsage(string $functionId, mixed $params): mixed
|
||||
{
|
||||
$usage = $this->client->call(Client::METHOD_GET, '/functions/' . $functionId . '/usage', array_merge([
|
||||
|
|
|
|||
|
|
@ -110,7 +110,8 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$data['functionId'],
|
||||
[
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'TESTINGVALUE'
|
||||
'value' => 'TESTINGVALUE',
|
||||
'secret' => false
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -131,6 +132,7 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST_1', $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
$this->assertTrue($variable['body']['secret']);
|
||||
|
||||
$secretVariableId = $variable['body']['$id'];
|
||||
|
||||
|
|
@ -142,7 +144,8 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$data['functionId'],
|
||||
[
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'ANOTHERTESTINGVALUE'
|
||||
'value' => 'ANOTHERTESTINGVALUE',
|
||||
'secret' => false
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -320,6 +323,41 @@ class FunctionsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST_UPDATE_2", $variable['body']['key']);
|
||||
$this->assertEquals("TESTINGVALUEUPDATED", $variable['body']['value']);
|
||||
|
||||
// convert non-secret variable to secret
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_2',
|
||||
'secret' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_2", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
$this->assertTrue($response['body']['secret']);
|
||||
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_UPDATE_2", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
$this->assertTrue($variable['body']['secret']);
|
||||
|
||||
// convert secret variable to non-secret
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/functions/' . $data['functionId'] . '/variables/' . $data['variableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE',
|
||||
'secret' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
|
@ -410,4 +448,53 @@ class FunctionsConsoleClientTest extends Scope
|
|||
|
||||
return $data;
|
||||
}
|
||||
|
||||
public function testVariableE2E(): void
|
||||
{
|
||||
$function = $this->createFunction([
|
||||
'functionId' => ID::unique(),
|
||||
'runtime' => 'node-18.0',
|
||||
'name' => 'Variable E2E Test',
|
||||
'entrypoint' => 'index.js',
|
||||
'logging' => false,
|
||||
'execute' => ['any']
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertFalse($function['body']['logging']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
// create variable
|
||||
$variable = $this->createVariable($functionId, [
|
||||
'key' => 'CUSTOM_VARIABLE',
|
||||
'value' => 'a_secret_value',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('CUSTOM_VARIABLE', $variable['body']['key']);
|
||||
$this->assertEquals('', $variable['body']['value']);
|
||||
$this->assertEquals(true, $variable['body']['secret']);
|
||||
|
||||
$deploymentId = $this->setupDeployment($functionId, [
|
||||
'entrypoint' => 'index.js',
|
||||
'code' => $this->packageFunction('node'),
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$execution = $this->createExecution($functionId);
|
||||
|
||||
$this->assertEquals(201, $execution['headers']['status-code']);
|
||||
$this->assertEmpty($execution['body']['logs']);
|
||||
$this->assertEmpty($execution['body']['errors']);
|
||||
$body = json_decode($execution['body']['responseBody']);
|
||||
$this->assertEquals('a_secret_value', $body->CUSTOM_VARIABLE);
|
||||
|
||||
$this->cleanupFunction($functionId);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,7 +38,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
'timeout' => 10,
|
||||
]);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$dateValidator = new DatetimeValidator();
|
||||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
|
|
@ -356,7 +356,22 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertEquals(201, $function['headers']['status-code']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$functionId,
|
||||
[
|
||||
'functionId' => ID::unique(),
|
||||
'activate' => true,
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'rootDirectory' => $phpRuntime['providerRootDirectory'],
|
||||
'version' => $starterTemplate['body']['providerVersion'],
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployments = $this->listDeployments($functionId);
|
||||
|
||||
|
|
@ -1898,7 +1913,7 @@ class FunctionsCustomServerTest extends Scope
|
|||
$this->assertFalse($function['body']['logging']);
|
||||
$this->assertNotEmpty($function['body']['$id']);
|
||||
|
||||
$functionId = $functionId = $function['body']['$id'] ?? '';
|
||||
$functionId = $function['body']['$id'] ?? '';
|
||||
|
||||
$this->setupDeployment($functionId, [
|
||||
'code' => $this->packageFunction('node'),
|
||||
|
|
|
|||
|
|
@ -3923,10 +3923,14 @@ class ProjectsConsoleClientTest extends Scope
|
|||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST',
|
||||
'value' => 'TESTINGVALUE'
|
||||
'value' => 'TESTINGVALUE',
|
||||
'secret' => false
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertEquals('APP_TEST', $variable['body']['key']);
|
||||
$this->assertEquals('TESTINGVALUE', $variable['body']['value']);
|
||||
$this->assertFalse($variable['body']['secret']);
|
||||
$variableId = $variable['body']['$id'];
|
||||
|
||||
// test for secret variable
|
||||
|
|
@ -4047,6 +4051,7 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertEquals("APP_TEST_1", $response['body']['key']);
|
||||
$this->assertEmpty($response['body']['value']);
|
||||
$this->assertTrue($response['body']['secret']);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
|
|
@ -4118,6 +4123,17 @@ class ProjectsConsoleClientTest extends Scope
|
|||
$this->assertEquals("APP_TEST_UPDATE_1", $variable['body']['key']);
|
||||
$this->assertEmpty($variable['body']['value']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_PUT, '/project/variables/' . $data['secretVariableId'], array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
'x-appwrite-mode' => 'admin',
|
||||
], $this->getHeaders()), [
|
||||
'key' => 'APP_TEST_UPDATE_1',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/project/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $data['projectId'],
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ use Appwrite\Tests\Async;
|
|||
use CURLFile;
|
||||
use Tests\E2E\Client;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Query;
|
||||
|
||||
trait SitesBase
|
||||
{
|
||||
|
|
@ -46,7 +47,7 @@ trait SitesBase
|
|||
'x-appwrite-key' => $this->getProject()['apiKey'],
|
||||
]));
|
||||
$this->assertEquals('ready', $deployment['body']['status'], 'Deployment status is not ready, deployment: ' . json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
}, 50000, 500);
|
||||
}, 100000, 500);
|
||||
|
||||
return $deploymentId;
|
||||
}
|
||||
|
|
@ -103,6 +104,46 @@ trait SitesBase
|
|||
return $variable;
|
||||
}
|
||||
|
||||
protected function getVariable(string $siteId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function listVariables(string $siteId, mixed $params = []): mixed
|
||||
{
|
||||
$variables = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/variables', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variables;
|
||||
}
|
||||
|
||||
protected function updateVariable(string $siteId, string $variableId, mixed $params): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_PUT, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function deleteVariable(string $siteId, string $variableId): mixed
|
||||
{
|
||||
$variable = $this->client->call(Client::METHOD_DELETE, '/sites/' . $siteId . '/variables/' . $variableId, array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
return $variable;
|
||||
}
|
||||
|
||||
protected function getSite(string $siteId): mixed
|
||||
{
|
||||
$site = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId, array_merge([
|
||||
|
|
@ -187,6 +228,16 @@ trait SitesBase
|
|||
return $deployment;
|
||||
}
|
||||
|
||||
protected function createTemplateDeployment(string $siteId, mixed $params = []): mixed
|
||||
{
|
||||
$deployment = $this->client->call(Client::METHOD_POST, '/sites/' . $siteId . '/deployments/template', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), $params);
|
||||
|
||||
return $deployment;
|
||||
}
|
||||
|
||||
protected function getSiteUsage(string $siteId, mixed $params): mixed
|
||||
{
|
||||
$usage = $this->client->call(Client::METHOD_GET, '/sites/' . $siteId . '/usage', array_merge([
|
||||
|
|
@ -215,4 +266,49 @@ trait SitesBase
|
|||
|
||||
return $site;
|
||||
}
|
||||
|
||||
protected function getSiteDomain(string $siteId): string
|
||||
{
|
||||
$rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('resourceId', [$siteId])->toString(),
|
||||
Query::equal('resourceType', ['site'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertGreaterThanOrEqual(1, $rules['body']['total']);
|
||||
$this->assertGreaterThanOrEqual(1, \count($rules['body']['rules']));
|
||||
$this->assertNotEmpty($rules['body']['rules'][0]['domain']);
|
||||
|
||||
$domain = $rules['body']['rules'][0]['domain'];
|
||||
|
||||
return $domain;
|
||||
}
|
||||
|
||||
|
||||
protected function getDeploymentDomain(string $deploymentId): string
|
||||
{
|
||||
$rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('resourceId', [$deploymentId])->toString(),
|
||||
Query::equal('resourceType', ['deployment'])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertGreaterThanOrEqual(1, $rules['body']['total']);
|
||||
$this->assertGreaterThanOrEqual(1, \count($rules['body']['rules']));
|
||||
$this->assertNotEmpty($rules['body']['rules'][0]['domain']);
|
||||
|
||||
$domain = $rules['body']['rules'][0]['domain'];
|
||||
|
||||
return $domain;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,6 +65,264 @@ class SitesCustomServerTest extends Scope
|
|||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testConsoleAvailabilityEndpoint(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Test Site',
|
||||
'framework' => 'other',
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'outputDirectory' => './',
|
||||
'subdomain' => 'test-site',
|
||||
'fallbackFile' => null,
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$rule = $this->getSiteDomain($siteId);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $rule,
|
||||
]);
|
||||
|
||||
$this->assertEquals(409, $response['headers']['status-code']); // domain unavailable
|
||||
|
||||
$nonExistingDomain = "non-existent-subdomain.sites.localhost";
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $nonExistingDomain,
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']); // domain available
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$rule = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('resourceId', [$siteId])
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rule['headers']['status-code']);
|
||||
$this->assertEquals(0, $rule['body']['total']);
|
||||
}, 5000, 500);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/console/resources', [
|
||||
'origin' => 'http://localhost',
|
||||
'content-type' => 'application/json',
|
||||
'cookie' => 'a_session_console=' . $this->getRoot()['session'],
|
||||
'x-appwrite-project' => 'console',
|
||||
], [
|
||||
'type' => 'rules',
|
||||
'value' => $rule,
|
||||
]);
|
||||
|
||||
$this->assertEquals(204, $response['headers']['status-code']); // domain available as site is deleted
|
||||
}
|
||||
|
||||
public function testVariables(): void
|
||||
{
|
||||
$site = $this->createSite([
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'fallbackFile' => null,
|
||||
'framework' => 'other',
|
||||
'name' => 'Test Site',
|
||||
'outputDirectory' => './',
|
||||
'siteId' => ID::unique()
|
||||
]);
|
||||
|
||||
$siteId = $site['body']['$id'] ?? '';
|
||||
|
||||
$this->assertEquals(201, $site['headers']['status-code']);
|
||||
$this->assertNotEmpty($site['body']['$id']);
|
||||
$this->assertEquals('Test Site', $site['body']['name']);
|
||||
|
||||
$variable = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey1',
|
||||
'value' => 'siteValue1',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$variable2 = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey2',
|
||||
'value' => 'siteValue2',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $variable2['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable2['body']['$id']);
|
||||
$this->assertEquals('siteKey2', $variable2['body']['key']);
|
||||
$this->assertEquals('siteValue2', $variable2['body']['value']);
|
||||
$this->assertEquals(false, $variable2['body']['secret']);
|
||||
|
||||
$secretVariable = $this->createVariable($siteId, [
|
||||
'key' => 'siteKey3',
|
||||
'value' => 'siteValue3',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variable = $this->getVariable($siteId, $variable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$secretVariable = $this->getVariable($siteId, $secretVariable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variable = $this->updateVariable($siteId, $variable['body']['$id'], [
|
||||
'key' => 'siteKey1Updated',
|
||||
'value' => 'siteValue1Updated',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1Updated', $variable['body']['key']);
|
||||
$this->assertEquals('siteValue1Updated', $variable['body']['value']);
|
||||
$this->assertEquals(false, $variable['body']['secret']);
|
||||
|
||||
$variable = $this->updateVariable($siteId, $variable['body']['$id'], [
|
||||
'key' => 'siteKey1Updated',
|
||||
'secret' => true,
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $variable['headers']['status-code']);
|
||||
$this->assertNotEmpty($variable['body']['$id']);
|
||||
$this->assertEquals('siteKey1Updated', $variable['body']['key']);
|
||||
$this->assertEquals('', $variable['body']['value']);
|
||||
$this->assertEquals(true, $variable['body']['secret']);
|
||||
|
||||
$secretVariable = $this->updateVariable($siteId, $secretVariable['body']['$id'], [
|
||||
'key' => 'siteKey3',
|
||||
'value' => 'siteValue3Updated',
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$response = $this->updateVariable($siteId, $secretVariable['body']['$id'], [
|
||||
'key' => 'siteKey3',
|
||||
'secret' => false,
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $response['headers']['status-code']);
|
||||
|
||||
$secretVariable = $this->getVariable($siteId, $secretVariable['body']['$id']);
|
||||
|
||||
$this->assertEquals(200, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('siteKey3', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$variables = $this->listVariables($siteId);
|
||||
|
||||
$this->assertEquals(200, $variables['headers']['status-code']);
|
||||
$this->assertCount(3, $variables['body']['variables']);
|
||||
|
||||
$response = $this->deleteVariable($siteId, $variable['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$response = $this->deleteVariable($siteId, $variable2['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
$response = $this->deleteVariable($siteId, $secretVariable['body']['$id']);
|
||||
$this->assertEquals(204, $response['headers']['status-code']);
|
||||
|
||||
$variables = $this->listVariables($siteId);
|
||||
|
||||
$this->assertEquals(200, $variables['headers']['status-code']);
|
||||
$this->assertCount(0, $variables['body']['variables']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testVariablesE2E(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Astro site',
|
||||
'framework' => 'astro',
|
||||
'adapter' => 'ssr',
|
||||
'buildRuntime' => 'ssr-22',
|
||||
'outputDirectory' => './dist',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$secretVariable = $this->createVariable($siteId, [
|
||||
'key' => 'name',
|
||||
'value' => 'Appwrite',
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $secretVariable['headers']['status-code']);
|
||||
$this->assertNotEmpty($secretVariable['body']['$id']);
|
||||
$this->assertEquals('name', $secretVariable['body']['key']);
|
||||
$this->assertEquals('', $secretVariable['body']['value']);
|
||||
$this->assertEquals(true, $secretVariable['body']['secret']);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('astro'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Env variable is Appwrite", $response['body']);
|
||||
$this->assertStringNotContainsString("Variable not found", $response['body']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testListSites(): void
|
||||
{
|
||||
/**
|
||||
|
|
@ -308,11 +566,6 @@ class SitesCustomServerTest extends Scope
|
|||
'installCommand' => $nextjsFramework['installCommand'],
|
||||
'outputDirectory' => $nextjsFramework['outputDirectory'],
|
||||
'providerRootDirectory' => $nextjsFramework['providerRootDirectory'],
|
||||
'templateOwner' => $starterTemplate['body']['providerOwner'],
|
||||
'templateRepository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'templateRootDirectory' => $nextjsFramework['providerRootDirectory'],
|
||||
'templateVersion' => $starterTemplate['body']['providerVersion'],
|
||||
'providerBranch' => 'main',
|
||||
]
|
||||
);
|
||||
|
||||
|
|
@ -321,6 +574,20 @@ class SitesCustomServerTest extends Scope
|
|||
|
||||
$siteId = $site['body']['$id'] ?? '';
|
||||
|
||||
$deployment = $this->createTemplateDeployment(
|
||||
$siteId,
|
||||
[
|
||||
'owner' => $starterTemplate['body']['providerOwner'],
|
||||
'repository' => $starterTemplate['body']['providerRepositoryId'],
|
||||
'rootDirectory' => $nextjsFramework['providerRootDirectory'],
|
||||
'version' => $starterTemplate['body']['providerVersion'],
|
||||
'activate' => true,
|
||||
]
|
||||
);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$deployments = $this->listDeployments($siteId);
|
||||
|
||||
$this->assertEquals(200, $deployments['headers']['status-code']);
|
||||
|
|
@ -939,5 +1206,278 @@ class SitesCustomServerTest extends Scope
|
|||
$this->assertArrayHasKey('adapters', $framework);
|
||||
}
|
||||
|
||||
public function testSiteTemplate(): void
|
||||
{
|
||||
$template = $this->getTemplate('astro-starter');
|
||||
$this->assertEquals(200, $template['headers']['status-code']);
|
||||
|
||||
$template = $template['body'];
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Template site',
|
||||
'framework' => $template['frameworks'][0]['key'],
|
||||
'adapter' => $template['frameworks'][0]['adapter'],
|
||||
'buildRuntime' => $template['frameworks'][0]['buildRuntime'],
|
||||
'outputDirectory' => $template['frameworks'][0]['outputDirectory'],
|
||||
'buildCommand' => $template['frameworks'][0]['buildCommand'],
|
||||
'installCommand' => $template['frameworks'][0]['installCommand'],
|
||||
'fallbackFile' => $template['frameworks'][0]['fallbackFile'],
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$deployment = $this->createTemplateDeployment($siteId, [
|
||||
'repository' => $template['providerRepositoryId'],
|
||||
'owner' => $template['providerOwner'],
|
||||
'rootDirectory' => $template['frameworks'][0]['providerRootDirectory'],
|
||||
'version' => $template['providerVersion'],
|
||||
'activate' => true
|
||||
]);
|
||||
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
$this->assertNotEmpty($deployment['body']['$id']);
|
||||
|
||||
$this->assertEventually(function () use ($siteId) {
|
||||
$site = $this->getSite($siteId);
|
||||
$this->assertNotEmpty($site['body']['deploymentId']);
|
||||
}, 50000, 500);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("Hello, Astronaut!", $response['body']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/about', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Astro Blog", $response['body']);
|
||||
$this->assertStringContainsString("About Me", $response['body']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testSiteDomainReclaiming(): void
|
||||
{
|
||||
$subdomain = 'startup' . \uniqid();
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Startup site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
'subdomain' => $subdomain
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('static'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringNotContainsString("This domain is not connected to any Appwrite resource yet", $response['body']);
|
||||
|
||||
$site = $this->createSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Startup 2 site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
'subdomain' => $subdomain
|
||||
]);
|
||||
|
||||
$this->assertEquals(400, $site['headers']['status-code']);
|
||||
$this->assertStringContainsString("Subdomain already exists.", $site['body']['message']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
|
||||
$this->assertEventually(function () use ($domain) {
|
||||
$rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('domain', [$domain])->toString(),
|
||||
],
|
||||
]);
|
||||
|
||||
$this->assertEquals(200, $rules['headers']['status-code']);
|
||||
$this->assertEquals(0, $rules['body']['total']);
|
||||
}, 50000, 500);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(401, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("This domain is not connected to any Appwrite resource yet", $response['body']);
|
||||
|
||||
$site = $this->createSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Startup 2 site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
'subdomain' => $subdomain
|
||||
]);
|
||||
|
||||
$this->assertEquals(201, $site['headers']['status-code']);
|
||||
|
||||
$this->cleanupSite($site['body']['$id']);
|
||||
}
|
||||
|
||||
public function testSitePreviewBranding(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'A site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('static'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
$previewDomain = $this->getDeploymentDomain($deploymentId);
|
||||
|
||||
$this->assertNotEmpty($domain);
|
||||
$this->assertNotEmpty($previewDomain);
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Hello Appwrite", $response['body']);
|
||||
$this->assertStringNotContainsString("Preview by", $response['body']);
|
||||
|
||||
$contentLength = $response['headers']['content-length'];
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $previewDomain);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Hello Appwrite", $response['body']);
|
||||
$this->assertStringContainsString("Preview by", $response['body']);
|
||||
$this->assertGreaterThan($contentLength, $response['headers']['content-length']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testSiteCors(): void
|
||||
{
|
||||
// Create rule together with site
|
||||
$subdomain = 'startup' . \uniqid();
|
||||
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Startup site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => '',
|
||||
'installCommand' => '',
|
||||
'fallbackFile' => '',
|
||||
'subdomain' => $subdomain
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$domain = $this->getSiteDomain($siteId);
|
||||
|
||||
$this->assertNotEmpty($domain);
|
||||
|
||||
$url = 'http://' . $domain;
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'referer' => $url,
|
||||
'origin' => $url
|
||||
]));
|
||||
|
||||
$this->assertEquals($url, $response['headers']['access-control-allow-origin']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => 'unknown',
|
||||
'referer' => $url,
|
||||
'origin' => $url
|
||||
]));
|
||||
|
||||
$this->assertNotEquals($url, $response['headers']['access-control-allow-origin']);
|
||||
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
|
||||
|
||||
$response = $this->client->call(Client::METHOD_GET, '/account', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
'referer' => 'http://unknown.com',
|
||||
'origin' => 'http://unknown.com'
|
||||
]));
|
||||
|
||||
$this->assertNotEquals($url, $response['headers']['access-control-allow-origin']);
|
||||
$this->assertEquals('http://localhost', $response['headers']['access-control-allow-origin']);
|
||||
}
|
||||
|
||||
// TODO: Add tests for deletion of resources when site is deleted
|
||||
}
|
||||
|
|
|
|||
10
tests/resources/sites/astro/astro.config.mjs
Normal file
10
tests/resources/sites/astro/astro.config.mjs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { defineConfig } from 'astro/config';
|
||||
import node from '@astrojs/node';
|
||||
import 'dotenv/config';
|
||||
|
||||
export default defineConfig({
|
||||
output: 'server',
|
||||
adapter: node({
|
||||
mode: 'standalone'
|
||||
}),
|
||||
});
|
||||
16
tests/resources/sites/astro/package.json
Normal file
16
tests/resources/sites/astro/package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"name": "my-astro-app",
|
||||
"type": "module",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"dev": "astro dev",
|
||||
"build": "astro build",
|
||||
"preview": "astro preview",
|
||||
"astro": "astro"
|
||||
},
|
||||
"dependencies": {
|
||||
"@astrojs/node": "^9.0.2",
|
||||
"astro": "^5.2.5",
|
||||
"dotenv": "^16.4.7"
|
||||
}
|
||||
}
|
||||
15
tests/resources/sites/astro/src/pages/index.astro
Normal file
15
tests/resources/sites/astro/src/pages/index.astro
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
---
|
||||
const value = import.meta.env.name || 'Variable not found';
|
||||
---
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Astro SSR</title>
|
||||
</head>
|
||||
<body>
|
||||
<p>Env variable is {value}</p>
|
||||
</body>
|
||||
</html>
|
||||
5
tests/resources/sites/astro/tsconfig.json
Normal file
5
tests/resources/sites/astro/tsconfig.json
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
{
|
||||
"extends": "astro/tsconfigs/strict",
|
||||
"include": [".astro/types.d.ts", "**/*"],
|
||||
"exclude": ["dist"]
|
||||
}
|
||||
43
tests/unit/Transformation/TransformationTest.php
Normal file
43
tests/unit/Transformation/TransformationTest.php
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
<?php
|
||||
|
||||
namespace Tests\Unit\Transformation;
|
||||
|
||||
use Appwrite\Transformation\Adapter\Mock;
|
||||
use Appwrite\Transformation\Adapter\Preview;
|
||||
use Appwrite\Transformation\Transformation;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TransformationTest extends TestCase
|
||||
{
|
||||
public function testPreview(): void
|
||||
{
|
||||
$input = "Hello world";
|
||||
|
||||
$transformer = new Transformation([new Preview()]);
|
||||
$transformer->addAdapter(new Mock());
|
||||
|
||||
$transformer->setInput($input);
|
||||
$transformer->setTraits([]);
|
||||
|
||||
$this->assertFalse($transformer->transform());
|
||||
|
||||
$transformer->setTraits(['mock' => true]);
|
||||
$this->assertFalse($transformer->transform());
|
||||
|
||||
$transformer->setTraits(['mock' => true, 'content-type' => 'text/plain']);
|
||||
$this->assertFalse($transformer->transform());
|
||||
|
||||
$transformer->setTraits(['mock' => true, 'content-type' => 'tExT/HtML']);
|
||||
$this->assertFalse($transformer->transform());
|
||||
|
||||
$transformer->setTraits(['mock' => false, 'content-type' => 'text/plain, text/html; charset=utf-8']);
|
||||
$this->assertFalse($transformer->transform());
|
||||
|
||||
$transformer->setTraits(['mock' => true, 'content-type' => 'text/plain, text/html; charset=utf-8']);
|
||||
$this->assertTrue($transformer->transform());
|
||||
|
||||
$this->assertStringContainsString("Hello world", $transformer->getOutput());
|
||||
$this->assertStringContainsString("Preview by", $transformer->getOutput());
|
||||
$this->assertStringContainsString("Mock:", $transformer->getOutput());
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue