Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
224ee89fd0 | ||
|
|
0f8efefea7 | ||
|
|
3ba5a04951 | ||
|
|
89a0fb8c9b | ||
|
|
fb1a19747a | ||
|
|
8fbc517b17 | ||
|
|
51d2deca5f | ||
|
|
1bdbb8fce1 | ||
|
|
d87c67eecb | ||
|
|
8a2ec0dcd7 | ||
|
|
9c6551ff7b | ||
|
|
066474010b | ||
|
|
7bbf04e7ac | ||
|
|
dd8b718985 | ||
|
|
ef39f8c2e2 | ||
|
|
e0a54a2ea6 | ||
|
|
fbd0051118 | ||
|
|
85edfc6af8 | ||
|
|
cfe6b8d34b | ||
|
|
aedfe0352d | ||
|
|
9404e9225e | ||
|
|
e263d681d9 | ||
|
|
6b8df26f84 | ||
|
|
e8cc95323e | ||
|
|
7c1fb0d945 | ||
|
|
20f780a58b | ||
|
|
3d1ea6d330 | ||
|
|
834872870f | ||
|
|
1946fca9f6 | ||
|
|
6af57ca48e | ||
|
|
3c8e4da34d | ||
|
|
3b1f2afbd5 | ||
|
|
34ab21730d | ||
|
|
70fbf69c56 | ||
|
|
62be0e1d00 | ||
|
|
2c55f5aaa6 | ||
|
|
681b6031aa | ||
|
|
ab03b4e3ca | ||
|
|
2e0a6f9b9e | ||
|
|
572544bf55 | ||
|
|
583a18dcd7 | ||
|
|
3b9318c2fc | ||
|
|
e836df13ba | ||
|
|
023597842e | ||
|
|
c57ec57277 | ||
|
|
6978f6960e | ||
|
|
95ba881ab7 | ||
|
|
bd18edc197 | ||
|
|
65aa218aac | ||
|
|
df943c62ca | ||
|
|
e80bc2bb39 | ||
|
|
9a4220f2dd | ||
|
|
33ab1513d0 | ||
|
|
d422f693f7 |
@@ -1417,6 +1417,13 @@ return [
|
||||
'lengths' => [],
|
||||
'orders' => [Database::ORDER_ASC],
|
||||
],
|
||||
[
|
||||
'$id' => ID::custom('_key_roles'),
|
||||
'type' => Database::INDEX_KEY,
|
||||
'attributes' => ['roles'],
|
||||
'lengths' => [128],
|
||||
'orders' => [],
|
||||
],
|
||||
],
|
||||
],
|
||||
|
||||
|
||||
@@ -580,6 +580,11 @@ return [
|
||||
'description' => 'Build with the requested ID is already completed and cannot be canceled.',
|
||||
'code' => 400,
|
||||
],
|
||||
Exception::BUILD_FAILED => [
|
||||
'name' => Exception::BUILD_FAILED,
|
||||
'description' => 'Build with the requested ID failed. Please check the logs for more information.',
|
||||
'code' => 400,
|
||||
],
|
||||
|
||||
/** Deployments */
|
||||
Exception::DEPLOYMENT_NOT_FOUND => [
|
||||
@@ -832,7 +837,7 @@ return [
|
||||
Exception::RULE_VERIFICATION_FAILED => [
|
||||
'name' => Exception::RULE_VERIFICATION_FAILED,
|
||||
'description' => 'Domain verification failed. Please check if your DNS records are correct and try again.',
|
||||
'code' => 401,
|
||||
'code' => 400,
|
||||
'publish' => true
|
||||
],
|
||||
Exception::PROJECT_SMTP_CONFIG_INVALID => [
|
||||
|
||||
@@ -138,7 +138,7 @@ return [
|
||||
'vue' => [
|
||||
'key' => 'vue',
|
||||
'name' => 'Vue.js',
|
||||
'screenshotSleep' => 3000,
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
@@ -227,6 +227,23 @@ return [
|
||||
]
|
||||
]
|
||||
],
|
||||
'lynx' => [
|
||||
'key' => 'lynx',
|
||||
'name' => 'Lynx',
|
||||
'screenshotSleep' => 5000,
|
||||
'buildRuntime' => 'node-22',
|
||||
'runtimes' => getVersions($templateRuntimes['NODE']['versions'], 'node'),
|
||||
'adapters' => [
|
||||
'static' => [
|
||||
'key' => 'static',
|
||||
'buildCommand' => 'npm run build',
|
||||
'installCommand' => 'npm install',
|
||||
'outputDirectory' => './dist',
|
||||
'startCommand' => 'bash helpers/server.sh',
|
||||
'fallbackFile' => 'index.html'
|
||||
]
|
||||
]
|
||||
],
|
||||
'flutter' => [
|
||||
'key' => 'flutter',
|
||||
'name' => 'Flutter',
|
||||
|
||||
@@ -6848,7 +6848,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
|
||||
@@ -9362,7 +9362,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "functions.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -24718,6 +24718,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -24917,7 +24918,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "sites.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -25353,6 +25354,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -28893,7 +28895,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
@@ -30986,6 +30988,30 @@
|
||||
"x-example": "<USER_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"description": "Search term to filter your list results. Max length: 256 chars.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<SEARCH>",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -37040,6 +37066,11 @@
|
||||
"description": "Site Template Name.",
|
||||
"x-example": "Starter site"
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Short description of template",
|
||||
"x-example": "Minimal web app integrating with Appwrite."
|
||||
},
|
||||
"demoUrl": {
|
||||
"type": "string",
|
||||
"description": "URL hosting a template demo.",
|
||||
@@ -37103,6 +37134,7 @@
|
||||
"required": [
|
||||
"key",
|
||||
"name",
|
||||
"tagline",
|
||||
"demoUrl",
|
||||
"screenshotDark",
|
||||
"screenshotLight",
|
||||
@@ -40882,6 +40914,11 @@
|
||||
"description": "A domain to use for site URLs.",
|
||||
"x-example": "sites.localhost"
|
||||
},
|
||||
"_APP_DOMAIN_FUNCTIONS": {
|
||||
"type": "string",
|
||||
"description": "A domain to use for function URLs.",
|
||||
"x-example": "functions.localhost"
|
||||
},
|
||||
"_APP_OPTIONS_FORCE_HTTPS": {
|
||||
"type": "string",
|
||||
"description": "Defines if HTTPS is enforced for all requests.",
|
||||
@@ -40902,6 +40939,7 @@
|
||||
"_APP_DOMAIN_ENABLED",
|
||||
"_APP_ASSISTANT_ENABLED",
|
||||
"_APP_DOMAIN_SITES",
|
||||
"_APP_DOMAIN_FUNCTIONS",
|
||||
"_APP_OPTIONS_FORCE_HTTPS",
|
||||
"_APP_DOMAINS_NAMESERVERS"
|
||||
]
|
||||
|
||||
@@ -8449,7 +8449,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "functions.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -16816,6 +16816,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -17015,7 +17016,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "sites.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -17226,6 +17227,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -20513,7 +20515,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
@@ -22567,6 +22569,30 @@
|
||||
"x-example": "<USER_ID>"
|
||||
},
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": []
|
||||
},
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"description": "Search term to filter your list results. Max length: 256 chars.",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "string",
|
||||
"x-example": "<SEARCH>",
|
||||
"default": ""
|
||||
},
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -7060,7 +7060,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
|
||||
@@ -9530,7 +9530,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "functions.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -25221,6 +25221,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -25436,7 +25437,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "sites.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -25871,6 +25872,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -29426,7 +29428,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
@@ -31569,6 +31571,27 @@
|
||||
"type": "string",
|
||||
"x-example": "<USER_ID>",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"description": "Search term to filter your list results. Max length: 256 chars.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<SEARCH>",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -37623,6 +37646,11 @@
|
||||
"description": "Site Template Name.",
|
||||
"x-example": "Starter site"
|
||||
},
|
||||
"tagline": {
|
||||
"type": "string",
|
||||
"description": "Short description of template",
|
||||
"x-example": "Minimal web app integrating with Appwrite."
|
||||
},
|
||||
"demoUrl": {
|
||||
"type": "string",
|
||||
"description": "URL hosting a template demo.",
|
||||
@@ -37688,6 +37716,7 @@
|
||||
"required": [
|
||||
"key",
|
||||
"name",
|
||||
"tagline",
|
||||
"demoUrl",
|
||||
"screenshotDark",
|
||||
"screenshotLight",
|
||||
@@ -41544,6 +41573,11 @@
|
||||
"description": "A domain to use for site URLs.",
|
||||
"x-example": "sites.localhost"
|
||||
},
|
||||
"_APP_DOMAIN_FUNCTIONS": {
|
||||
"type": "string",
|
||||
"description": "A domain to use for function URLs.",
|
||||
"x-example": "functions.localhost"
|
||||
},
|
||||
"_APP_OPTIONS_FORCE_HTTPS": {
|
||||
"type": "string",
|
||||
"description": "Defines if HTTPS is enforced for all requests.",
|
||||
@@ -41564,6 +41598,7 @@
|
||||
"_APP_DOMAIN_ENABLED",
|
||||
"_APP_ASSISTANT_ENABLED",
|
||||
"_APP_DOMAIN_SITES",
|
||||
"_APP_DOMAIN_FUNCTIONS",
|
||||
"_APP_OPTIONS_FORCE_HTTPS",
|
||||
"_APP_DOMAINS_NAMESERVERS"
|
||||
]
|
||||
|
||||
@@ -8614,7 +8614,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "functions.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -17285,6 +17285,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -17500,7 +17501,7 @@
|
||||
"rate-limit": 0,
|
||||
"rate-time": 3600,
|
||||
"rate-key": "url:{url},ip:{ip}",
|
||||
"scope": "sites.read",
|
||||
"scope": "public",
|
||||
"platforms": [
|
||||
"server"
|
||||
],
|
||||
@@ -17714,6 +17715,7 @@
|
||||
"sveltekit",
|
||||
"astro",
|
||||
"remix",
|
||||
"lynx",
|
||||
"flutter",
|
||||
"vite",
|
||||
"other"
|
||||
@@ -21021,7 +21023,7 @@
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
@@ -23125,6 +23127,27 @@
|
||||
"type": "string",
|
||||
"x-example": "<USER_ID>",
|
||||
"in": "path"
|
||||
},
|
||||
{
|
||||
"name": "queries",
|
||||
"description": "Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https:\/\/appwrite.io\/docs\/queries). Maximum of 100 queries are allowed, each 4096 characters long. You may filter on the following attributes: userId, teamId, invited, joined, confirm, roles",
|
||||
"required": false,
|
||||
"type": "array",
|
||||
"collectionFormat": "multi",
|
||||
"items": {
|
||||
"type": "string"
|
||||
},
|
||||
"default": [],
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"name": "search",
|
||||
"description": "Search term to filter your list results. Max length: 256 chars.",
|
||||
"required": false,
|
||||
"type": "string",
|
||||
"x-example": "<SEARCH>",
|
||||
"default": "",
|
||||
"in": "query"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ return [
|
||||
'icon' => 'icon-lightning-bolt',
|
||||
'id' => 'starter',
|
||||
'name' => 'Starter function',
|
||||
'score' => 5,
|
||||
'tagline' =>
|
||||
'A simple function to get started. Edit this function to explore endless possibilities with Appwrite Functions.',
|
||||
'permissions' => ['any'],
|
||||
@@ -62,6 +63,7 @@ return [
|
||||
'icon' => 'icon-upstash',
|
||||
'id' => 'query-upstash-vector',
|
||||
'name' => 'Query Upstash Vector',
|
||||
'score' => 4,
|
||||
'tagline' => 'Vector database that stores text embeddings and context retrieval for LLMs',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -106,6 +108,7 @@ return [
|
||||
'icon' => 'icon-redis',
|
||||
'id' => 'query-redis-labs',
|
||||
'name' => 'Query Redis Labs',
|
||||
'score' => 4,
|
||||
'tagline' => 'Key-value database with advanced caching capabilities.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -149,6 +152,7 @@ return [
|
||||
'icon' => 'icon-neo4j',
|
||||
'id' => 'query-neo4j-auradb',
|
||||
'name' => 'Query Neo4j AuraDB',
|
||||
'score' => 4,
|
||||
'tagline' => 'Graph database with focus on relations between data.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -200,6 +204,7 @@ return [
|
||||
'icon' => 'icon-mongodb',
|
||||
'id' => 'query-mongo-atlas',
|
||||
'name' => 'Query MongoDB Atlas',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Realtime NoSQL document database with geospecial, graph, search, and vector suport.',
|
||||
'permissions' => ['any'],
|
||||
@@ -237,6 +242,7 @@ return [
|
||||
'icon' => 'icon-neon',
|
||||
'id' => 'query-neon-postgres',
|
||||
'name' => 'Query Neon Postgres',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Reliable SQL database with replication, point-in-time recovery, and pgvector support.',
|
||||
'permissions' => ['any'],
|
||||
@@ -305,6 +311,7 @@ return [
|
||||
'icon' => 'icon-open-ai',
|
||||
'id' => 'prompt-chatgpt',
|
||||
'name' => 'Prompt ChatGPT',
|
||||
'score' => 7,
|
||||
'tagline' => 'Ask questions and let OpenAI GPT-3.5-turbo answer.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -366,6 +373,7 @@ return [
|
||||
'icon' => 'icon-discord',
|
||||
'id' => 'discord-command-bot',
|
||||
'name' => 'Discord Command Bot',
|
||||
'score' => 6,
|
||||
'tagline' => 'Simple command using Discord Interactions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -429,6 +437,7 @@ return [
|
||||
'icon' => 'icon-perspective-api',
|
||||
'id' => 'analyze-with-perspectiveapi',
|
||||
'name' => 'Analyze with PerspectiveAPI',
|
||||
'score' => 5,
|
||||
'tagline' => 'Automate moderation by getting toxicity of messages.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -464,6 +473,7 @@ return [
|
||||
'icon' => 'icon-pangea',
|
||||
'id' => 'censor-with-redact',
|
||||
'name' => 'Censor with Redact',
|
||||
'score' => 5,
|
||||
'tagline' =>
|
||||
'Censor sensitive information from a provided text string using Redact API by Pangea.',
|
||||
'permissions' => ['any'],
|
||||
@@ -512,6 +522,7 @@ return [
|
||||
'icon' => 'icon-document',
|
||||
'id' => 'generate-pdf',
|
||||
'name' => 'Generate PDF',
|
||||
'score' => 7,
|
||||
'tagline' => 'Document containing sample invoice in PDF format.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -533,6 +544,7 @@ return [
|
||||
'icon' => 'icon-github',
|
||||
'id' => 'github-issue-bot',
|
||||
'name' => 'GitHub issue bot',
|
||||
'score' => 4,
|
||||
'tagline' =>
|
||||
'Automate the process of responding to newly opened issues in a GitHub repository.',
|
||||
'permissions' => ['any'],
|
||||
@@ -577,6 +589,7 @@ return [
|
||||
'icon' => 'icon-bookmark',
|
||||
'id' => 'url-shortener',
|
||||
'name' => 'URL shortener',
|
||||
'score' => 3,
|
||||
'tagline' => 'Generate URL with short ID and redirect to the original URL when visited.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -628,6 +641,7 @@ return [
|
||||
'icon' => 'icon-algolia',
|
||||
'id' => 'sync-with-algolia',
|
||||
'name' => 'Sync with Algolia',
|
||||
'score' => 4,
|
||||
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -709,6 +723,7 @@ return [
|
||||
'icon' => 'icon-meilisearch',
|
||||
'id' => 'sync-with-meilisearch',
|
||||
'name' => 'Sync with Meilisearch',
|
||||
'score' => 4,
|
||||
'tagline' => 'Intuitive search bar for any data in Appwrite Databases.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -802,6 +817,7 @@ return [
|
||||
'icon' => 'icon-vonage',
|
||||
'id' => 'whatsapp-with-vonage',
|
||||
'name' => 'WhatsApp with Vonage',
|
||||
'score' => 6,
|
||||
'tagline' => 'Simple bot to answer WhatsApp messages.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -888,6 +904,7 @@ return [
|
||||
'icon' => 'icon-bell',
|
||||
'id' => 'push-notification-with-fcm',
|
||||
'name' => 'Push notification with FCM',
|
||||
'score' => 4,
|
||||
'tagline' => 'Send push notifications to your users using Firebase Cloud Messaging (FCM).',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -944,6 +961,7 @@ return [
|
||||
'icon' => 'icon-mail',
|
||||
'id' => 'email-contact-form',
|
||||
'name' => 'Email contact form',
|
||||
'score' => 7,
|
||||
'tagline' => 'Sends an email with the contents of a HTML form.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1027,6 +1045,7 @@ return [
|
||||
'icon' => 'icon-stripe',
|
||||
'id' => 'subscriptions-with-stripe',
|
||||
'name' => 'Subscriptions with Stripe',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1068,6 +1087,7 @@ return [
|
||||
'icon' => 'icon-stripe',
|
||||
'id' => 'payments-with-stripe',
|
||||
'name' => 'Payments with Stripe',
|
||||
'score' => 8,
|
||||
'tagline' => 'Receive card payments and store paid orders.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1125,6 +1145,7 @@ return [
|
||||
'icon' => 'icon-chat',
|
||||
'id' => 'text-generation-with-huggingface',
|
||||
'name' => 'Text generation',
|
||||
'score' => 5,
|
||||
'tagline' => 'Generate text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1159,6 +1180,7 @@ return [
|
||||
'icon' => 'icon-translate',
|
||||
'id' => 'language-translation-with-huggingface',
|
||||
'name' => 'Language translation',
|
||||
'score' => 5,
|
||||
'tagline' => 'Translate text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1193,6 +1215,7 @@ return [
|
||||
'icon' => 'icon-eye',
|
||||
'id' => 'image-classification-with-huggingface',
|
||||
'name' => 'Image classification',
|
||||
'score' => 5,
|
||||
'tagline' => 'Classify images using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
@@ -1251,6 +1274,7 @@ return [
|
||||
'icon' => 'icon-eye',
|
||||
'id' => 'object-detection-with-huggingface',
|
||||
'name' => 'Object detection',
|
||||
'score' => 5,
|
||||
'tagline' => 'Detect objects in images using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
@@ -1309,6 +1333,7 @@ return [
|
||||
'icon' => 'icon-text',
|
||||
'id' => 'speech-recognition-with-huggingface',
|
||||
'name' => 'Speech recognition',
|
||||
'score' => 5,
|
||||
'tagline' => 'Transcribe audio to text using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['buckets.*.files.*.create'],
|
||||
@@ -1367,6 +1392,7 @@ return [
|
||||
'icon' => 'icon-chat',
|
||||
'id' => 'text-to-speech-with-huggingface',
|
||||
'name' => 'Text to speech',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => ['databases.*.collections.*.documents.*.create'],
|
||||
@@ -1425,6 +1451,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-replicate',
|
||||
'name' => 'Generate with Replicate',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text, audio and images using Replicate's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1460,6 +1487,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-together-ai',
|
||||
'name' => 'Generate with Together AI',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text and images using Together AI's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1502,6 +1530,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'chat-with-perplexity-ai',
|
||||
'name' => 'Chat with Perplexity AI',
|
||||
'score' => 5,
|
||||
'tagline' => 'Create a chatbot using the Perplexity AI API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1543,6 +1572,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-replicate',
|
||||
'name' => 'Generate with Replicate',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate text, audio and images using Replicate's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1578,6 +1608,7 @@ return [
|
||||
'icon' => 'icon-document-search',
|
||||
'id' => 'sync-with-pinecone',
|
||||
'name' => 'Sync with Pinecone',
|
||||
'score' => 4,
|
||||
'tagline' => "Sync your Appwrite database with Pinecone's vector database.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1641,6 +1672,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'rag-with-langchain',
|
||||
'name' => 'RAG with LangChain',
|
||||
'score' => 6,
|
||||
'tagline' => 'Generate text using a LangChain RAG model',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1704,6 +1736,7 @@ return [
|
||||
'icon' => 'icon-chat',
|
||||
'id' => 'speak-with-elevenlabs',
|
||||
'name' => 'Speak with ElevenLabs',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the ElevenLabs API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
@@ -1759,6 +1792,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'speak-with-lmnt',
|
||||
'name' => 'Speak with LMNT',
|
||||
'score' => 5,
|
||||
'tagline' => 'Convert text to speech using the LMNT API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
@@ -1800,6 +1834,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'chat-with-anyscale',
|
||||
'name' => 'Chat with AnyScale',
|
||||
'score' => 5,
|
||||
'tagline' => 'Create a chatbot using the AnyScale API.',
|
||||
'permissions' => ['any'],
|
||||
'cron' => '',
|
||||
@@ -1841,6 +1876,7 @@ return [
|
||||
'icon' => 'icon-music-note',
|
||||
'id' => 'music-generation-with-huggingface',
|
||||
'name' => 'Music generation',
|
||||
'score' => 4,
|
||||
'tagline' => 'Generate music from a text prompt using the Hugging Face inference API.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1883,6 +1919,7 @@ return [
|
||||
'icon' => 'icon-chip',
|
||||
'id' => 'generate-with-fal-ai',
|
||||
'name' => 'Generate with fal.ai',
|
||||
'score' => 5,
|
||||
'tagline' => "Generate images using fal.ai's API.",
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1918,6 +1955,7 @@ return [
|
||||
'icon' => 'icon-currency-dollar',
|
||||
'id' => 'subscriptions-with-lemon-squeezy',
|
||||
'name' => 'Subscriptions with Lemon Squeezy',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive recurring card payments and grant subscribers extra permissions.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
@@ -1973,6 +2011,7 @@ return [
|
||||
'icon' => 'icon-currency-dollar',
|
||||
'id' => 'payments-with-lemon-squeezy',
|
||||
'name' => 'Payments with Lemon Squeezy',
|
||||
'score' => 6,
|
||||
'tagline' => 'Receive card payments and store paid orders.',
|
||||
'permissions' => ['any'],
|
||||
'events' => [],
|
||||
|
||||
@@ -16,6 +16,16 @@ if (System::getEnv('_APP_ENV') === 'development') {
|
||||
|
||||
$url = $protocol . '://' . $hostname;
|
||||
|
||||
class UseCases
|
||||
{
|
||||
public const PORTFOLIO = 'portfolio';
|
||||
public const STARTER = 'starter';
|
||||
public const EVENTS = 'events';
|
||||
public const ECOMMERCE = 'ecommerce';
|
||||
public const DOCUMENTATION = 'documentation';
|
||||
public const BLOG = 'blog';
|
||||
}
|
||||
|
||||
const TEMPLATE_FRAMEWORKS = [
|
||||
'SVELTEKIT' => [
|
||||
'key' => 'sveltekit',
|
||||
@@ -77,15 +87,6 @@ const TEMPLATE_FRAMEWORKS = [
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => '',
|
||||
],
|
||||
'OTHER' => [
|
||||
'key' => 'other',
|
||||
'name' => 'Other',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
'VITE' => [
|
||||
'key' => 'vite',
|
||||
'name' => 'Vite',
|
||||
@@ -134,6 +135,16 @@ const TEMPLATE_FRAMEWORKS = [
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './',
|
||||
],
|
||||
'LYNX' => [
|
||||
'key' => 'lynx',
|
||||
'name' => 'Lynx',
|
||||
'installCommand' => 'npm install && cd web && npm install && cd ..',
|
||||
'buildCommand' => 'npm run build && cd web && npm run build && cd ..',
|
||||
'buildRuntime' => 'node-22',
|
||||
'adapter' => 'static',
|
||||
'outputDirectory' => './web/dist',
|
||||
'fallbackFile' => 'index.html',
|
||||
],
|
||||
];
|
||||
|
||||
function getFramework(string $frameworkEnum, array $overrides)
|
||||
@@ -143,10 +154,119 @@ function getFramework(string $frameworkEnum, array $overrides)
|
||||
}
|
||||
|
||||
return [
|
||||
[
|
||||
'key' => 'template-for-documentation',
|
||||
'name' => 'Documentation template',
|
||||
'tagline' => 'Modern site to store your knowledge with a clean design, full-text search, dark mode, and more.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-documentation-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-documentation-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('ASTRO', [
|
||||
'providerRootDirectory' => './',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'template-for-documentation',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.1.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'lynx-starter',
|
||||
'name' => 'Lynx Starter',
|
||||
'tagline' => 'Sample application built with Lynx, a cross-platform framework focused on performance.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/lynx-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/lynx-starter-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('LYNX', [
|
||||
'providerRootDirectory' => './lynx/starter',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'vitepress',
|
||||
'name' => 'Vitepress',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Vite.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/vitepress-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/vitepress-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('VITE', [
|
||||
'providerRootDirectory' => './vite/vitepress',
|
||||
'outputDirectory' => '404.html',
|
||||
'installCommand' => 'npm i vitepress && npm install',
|
||||
'buildCommand' => 'npm run docs:build',
|
||||
'outputDirectory' => './.vitepress/dist',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'vuepress',
|
||||
'name' => 'Vuepress',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Vue.',
|
||||
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/vuepress-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/vuepress-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('VUE', [
|
||||
'providerRootDirectory' => './vue/vuepress',
|
||||
'outputDirectory' => '404.html',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './src/.vuepress/dist',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'docusaurus',
|
||||
'name' => 'Docusaurus',
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by React.',
|
||||
'score' => 4, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/docusaurus-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/docusaurus-light.png',
|
||||
'frameworks' => [
|
||||
getFramework('REACT', [
|
||||
'providerRootDirectory' => './react/docusaurus',
|
||||
'outputDirectory' => '404.html',
|
||||
'installCommand' => 'npm install',
|
||||
'buildCommand' => 'npm run build',
|
||||
'outputDirectory' => './build',
|
||||
]),
|
||||
],
|
||||
'vcsProvider' => 'github',
|
||||
'providerRepositoryId' => 'templates-for-sites',
|
||||
'providerOwner' => 'appwrite',
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
[
|
||||
'key' => 'nxt-lnk',
|
||||
'name' => 'Nxt Lnk',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/nxt-lnk-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/nxt-lnk-light.png',
|
||||
'frameworks' => [
|
||||
@@ -160,11 +280,12 @@ return [
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'magic-portfolio',
|
||||
'name' => 'Magic Portfolio',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Complex personal website to showcase your projects, articles, and more.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/magic-portfolio-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/magic-portfolio-light.png',
|
||||
'frameworks' => [
|
||||
@@ -178,11 +299,12 @@ return [
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'littlelink',
|
||||
'name' => 'LittleLink',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/littlelink-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/littlelink-light.png',
|
||||
'frameworks' => [
|
||||
@@ -196,11 +318,12 @@ return [
|
||||
'providerVersion' => '0.3.*',
|
||||
'variables' => []
|
||||
],
|
||||
|
||||
[
|
||||
'key' => 'logspot',
|
||||
'name' => 'Logspot',
|
||||
'useCases' => ['blog'],
|
||||
'tagline' => 'Website to publish changelogs of your application.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/logspot-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/logspot-light.png',
|
||||
'frameworks' => [
|
||||
@@ -220,7 +343,9 @@ return [
|
||||
[
|
||||
'key' => 'astro-nano',
|
||||
'name' => 'Astro Nano',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Minimal personal website to showcase your projects, articles, and more.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-nano-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-nano-light.png',
|
||||
'frameworks' => [
|
||||
@@ -239,7 +364,9 @@ return [
|
||||
[
|
||||
'key' => 'astro-starlight',
|
||||
'name' => 'Astro Starlight',
|
||||
'useCases' => ['documentation'],
|
||||
'tagline' => 'Platform for documentation and knowledge sharing powered by Astro.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::DOCUMENTATION],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starlight-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starlight-light.png',
|
||||
'frameworks' => [
|
||||
@@ -258,7 +385,9 @@ return [
|
||||
[
|
||||
'key' => 'astro-sphere',
|
||||
'name' => 'Astro Sphere',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Modern personal website to showcase your projects, articles, and more.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-sphere-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-sphere-light.png',
|
||||
'frameworks' => [
|
||||
@@ -277,7 +406,9 @@ return [
|
||||
[
|
||||
'key' => 'astro-starlog',
|
||||
'name' => 'Astro Starlog',
|
||||
'useCases' => ['blog'],
|
||||
'tagline' => 'Platform for publishing written content and media powered by Astro.',
|
||||
'score' => 5, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starlog-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starlog-light.png',
|
||||
'frameworks' => [
|
||||
@@ -296,7 +427,9 @@ return [
|
||||
[
|
||||
'key' => 'onelink',
|
||||
'name' => 'Onelink',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Personal website for creators to merge all URLs to social profiles.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/onelink-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/onelink-light.png',
|
||||
'frameworks' => [
|
||||
@@ -316,7 +449,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-flutter',
|
||||
'name' => 'Flutter starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Flutter application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-flutter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-flutter-light.png',
|
||||
'frameworks' => [
|
||||
@@ -359,7 +494,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-js',
|
||||
'name' => 'JavaScript starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple JavaScript application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-js-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-js-light.png',
|
||||
'frameworks' => [
|
||||
@@ -401,7 +538,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-angular',
|
||||
'name' => 'Angular starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Angular application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-angular-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-angular-light.png',
|
||||
'frameworks' => [
|
||||
@@ -445,7 +584,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-svelte',
|
||||
'name' => 'Svelte starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Svelte application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-svelte-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-svelte-light.png',
|
||||
'frameworks' => [
|
||||
@@ -487,7 +628,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-react',
|
||||
'name' => 'React starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple React application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-light.png',
|
||||
'frameworks' => [
|
||||
@@ -529,7 +672,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-vue',
|
||||
'name' => 'Vue starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Vue application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-vue-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-vue-light.png',
|
||||
'frameworks' => [
|
||||
@@ -571,7 +716,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-react-native',
|
||||
'name' => 'React Native starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple React Native application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-react-native-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-react-native-light.png',
|
||||
'frameworks' => [
|
||||
@@ -614,7 +761,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-nextjs',
|
||||
'name' => 'Next.js starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Next.js application integrated with Appwrite SDK.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nextjs-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nextjs-light.png',
|
||||
'frameworks' => [
|
||||
@@ -656,7 +805,9 @@ return [
|
||||
[
|
||||
'key' => 'starter-for-nuxt',
|
||||
'name' => 'Nuxt starter',
|
||||
'useCases' => ['starter'],
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'tagline' => 'Simple Nuxt application integrated with Appwrite SDK.',
|
||||
'score' => 3, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'screenshotDark' => $url . '/images/sites/templates/starter-for-nuxt-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/starter-for-nuxt-light.png',
|
||||
'frameworks' => [
|
||||
@@ -698,7 +849,9 @@ return [
|
||||
[
|
||||
'key' => 'template-for-event',
|
||||
'name' => 'Event template',
|
||||
'useCases' => ['events'],
|
||||
'tagline' => 'Hackathon landing page with support for project submissions.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::EVENTS],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-event-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-event-light.png',
|
||||
'frameworks' => [
|
||||
@@ -734,7 +887,9 @@ return [
|
||||
[
|
||||
'key' => 'template-for-portfolio',
|
||||
'name' => 'Portfolio template',
|
||||
'useCases' => ['portfolio'],
|
||||
'tagline' => 'Simple personal website to showcase your projects, articles, and more.',
|
||||
'score' => 6, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::PORTFOLIO],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-portfolio-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-portfolio-light.png',
|
||||
'frameworks' => [
|
||||
@@ -751,7 +906,9 @@ return [
|
||||
[
|
||||
'key' => 'template-for-store',
|
||||
'name' => 'Store template',
|
||||
'useCases' => ['ecommerce'],
|
||||
'tagline' => 'E-commerce platform for selling products with Stripe integration.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::ECOMMERCE],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-store-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-store-light.png',
|
||||
'frameworks' => [
|
||||
@@ -793,7 +950,9 @@ return [
|
||||
[
|
||||
'key' => 'template-for-blog',
|
||||
'name' => 'Blog template',
|
||||
'useCases' => ['blog'],
|
||||
'tagline' => 'Platform for publishing written content and media.',
|
||||
'score' => 7, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::BLOG],
|
||||
'screenshotDark' => $url . '/images/sites/templates/template-for-blog-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/template-for-blog-light.png',
|
||||
'frameworks' => [
|
||||
@@ -810,7 +969,9 @@ return [
|
||||
[
|
||||
'key' => 'astro-starter',
|
||||
'name' => 'Astro starter',
|
||||
'useCases' => ['starter'],
|
||||
'tagline' => 'Sample application built with Astro, a content-driven web framework.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/astro-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/astro-starter-light.png',
|
||||
'frameworks' => [
|
||||
@@ -827,7 +988,9 @@ return [
|
||||
[
|
||||
'key' => 'remix-starter',
|
||||
'name' => 'Remix starter',
|
||||
'useCases' => ['starter'],
|
||||
'tagline' => 'Sample application built with Remix, a React meta-framework.',
|
||||
'score' => 1, // 0 to 10 based on looks of screenshot (avoid 1,2,3,8,9,10 if possible)
|
||||
'useCases' => [UseCases::STARTER],
|
||||
'screenshotDark' => $url . '/images/sites/templates/remix-starter-dark.png',
|
||||
'screenshotLight' => $url . '/images/sites/templates/remix-starter-light.png',
|
||||
'frameworks' => [
|
||||
|
||||
@@ -496,6 +496,7 @@ App::get('/v1/avatars/qr')
|
||||
'addQuietzone' => true,
|
||||
'quietzoneSize' => $margin,
|
||||
'outputType' => QRCode::OUTPUT_IMAGICK,
|
||||
'scale' => 15,
|
||||
]);
|
||||
|
||||
$qrcode = new QRCode($options);
|
||||
@@ -510,7 +511,7 @@ App::get('/v1/avatars/qr')
|
||||
$response
|
||||
->addHeader('Cache-Control', 'private, max-age=3888000') // 45 days
|
||||
->setContentType('image/png')
|
||||
->send($image->output('png', 9));
|
||||
->send($image->output('png', 90));
|
||||
});
|
||||
|
||||
App::get('/v1/avatars/initials')
|
||||
|
||||
@@ -62,6 +62,7 @@ App::get('/v1/console/variables')
|
||||
'_APP_DOMAIN_ENABLED' => $isDomainEnabled,
|
||||
'_APP_ASSISTANT_ENABLED' => $isAssistantEnabled,
|
||||
'_APP_DOMAIN_SITES' => System::getEnv('_APP_DOMAIN_SITES'),
|
||||
'_APP_DOMAIN_FUNCTIONS' => System::getEnv('_APP_DOMAIN_FUNCTIONS'),
|
||||
'_APP_OPTIONS_FORCE_HTTPS' => System::getEnv('_APP_OPTIONS_FORCE_HTTPS'),
|
||||
'_APP_DOMAINS_NAMESERVERS' => System::getEnv('_APP_DOMAINS_NAMESERVERS'),
|
||||
]);
|
||||
|
||||
@@ -21,6 +21,7 @@ use Appwrite\SDK\Method;
|
||||
use Appwrite\SDK\Response as SDKResponse;
|
||||
use Appwrite\Utopia\Database\Validator\CustomId;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Identities;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Targets;
|
||||
use Appwrite\Utopia\Database\Validator\Queries\Users;
|
||||
use Appwrite\Utopia\Request;
|
||||
@@ -799,9 +800,11 @@ App::get('/v1/users/:userId/memberships')
|
||||
]
|
||||
))
|
||||
->param('userId', '', new UID(), 'User ID.')
|
||||
->param('queries', [], new Memberships(), 'Array of query strings generated using the Query class provided by the SDK. [Learn more about queries](https://appwrite.io/docs/queries). Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' queries are allowed, each ' . APP_LIMIT_ARRAY_ELEMENT_SIZE . ' characters long. You may filter on the following attributes: ' . implode(', ', Memberships::ALLOWED_ATTRIBUTES), true)
|
||||
->param('search', '', new Text(256), 'Search term to filter your list results. Max length: 256 chars.', true)
|
||||
->inject('response')
|
||||
->inject('dbForProject')
|
||||
->action(function (string $userId, Response $response, Database $dbForProject) {
|
||||
->action(function (string $userId, array $queries, string $search, Response $response, Database $dbForProject) {
|
||||
|
||||
$user = $dbForProject->getDocument('users', $userId);
|
||||
|
||||
@@ -809,6 +812,19 @@ App::get('/v1/users/:userId/memberships')
|
||||
throw new Exception(Exception::USER_NOT_FOUND);
|
||||
}
|
||||
|
||||
try {
|
||||
$queries = Query::parseQueries($queries);
|
||||
} catch (QueryException $e) {
|
||||
throw new Exception(Exception::GENERAL_QUERY_INVALID, $e->getMessage());
|
||||
}
|
||||
|
||||
if (!empty($search)) {
|
||||
$queries[] = Query::search('search', $search);
|
||||
}
|
||||
|
||||
// Set internal queries
|
||||
$queries[] = Query::equal('userInternalId', [$user->getInternalId()]);
|
||||
|
||||
$memberships = array_map(function ($membership) use ($dbForProject, $user) {
|
||||
$team = $dbForProject->getDocument('teams', $membership->getAttribute('teamId'));
|
||||
|
||||
@@ -818,7 +834,7 @@ App::get('/v1/users/:userId/memberships')
|
||||
->setAttribute('userEmail', $user->getAttribute('email'));
|
||||
|
||||
return $membership;
|
||||
}, $user->getAttribute('memberships', []));
|
||||
}, $dbForProject->find('memberships', $queries));
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'memberships' => $memberships,
|
||||
|
||||
@@ -134,12 +134,12 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
|
||||
|
||||
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
|
||||
} else {
|
||||
$comment = new Comment();
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
|
||||
$latestCommentId = \strval($github->createComment($owner, $repositoryName, $providerPullRequestId, $comment->generateComment()));
|
||||
|
||||
if (!empty($latestCommentId)) {
|
||||
@@ -176,7 +176,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$latestCommentId = $comment->getAttribute('providerCommentId', '');
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $latestCommentId));
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '', '');
|
||||
$comment->addBuild($project, $resource, $resourceType, $commentStatus, $deploymentId, $action, '');
|
||||
|
||||
$latestCommentId = \strval($github->updateComment($owner, $repositoryName, $latestCommentId, $comment->generateComment()));
|
||||
}
|
||||
@@ -219,7 +219,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
$commands[] = $resource->getAttribute('commands', '');
|
||||
}
|
||||
|
||||
$deployment = $dbForProject->createDocument('deployments', new Document([
|
||||
$deployment = Authorization::skip(fn () => $dbForProject->createDocument('deployments', new Document([
|
||||
'$id' => $deploymentId,
|
||||
'$permissions' => [
|
||||
Permission::read(Role::any()),
|
||||
@@ -253,14 +253,14 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
|
||||
'providerBranch' => $providerBranch,
|
||||
'search' => implode(' ', [$deploymentId, $resource->getAttribute('entrypoint', '')]),
|
||||
'activate' => $activate,
|
||||
]));
|
||||
])));
|
||||
|
||||
$resource = $resource
|
||||
->setAttribute('latestDeploymentId', $deployment->getId())
|
||||
->setAttribute('latestDeploymentInternalId', $deployment->getInternalId())
|
||||
->setAttribute('latestDeploymentCreatedAt', $deployment->getCreatedAt())
|
||||
->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
Authorization::skip(fn () => $dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource));
|
||||
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$projectId = $project->getId();
|
||||
|
||||
@@ -269,8 +269,13 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
throw new AppwriteException(AppwriteException::FUNCTION_RUNTIME_UNSUPPORTED, 'Runtime "' . $resource->getAttribute('runtime', '') . '" is not supported');
|
||||
}
|
||||
|
||||
if ($deployment->getAttribute('status') !== 'ready') {
|
||||
throw new AppwriteException(AppwriteException::BUILD_NOT_READY);
|
||||
$allowAnyStatus = !\is_null($apiKey) && $apiKey->isDeploymentStatusIgnored();
|
||||
if (!$allowAnyStatus && $deployment->getAttribute('status') !== 'ready') {
|
||||
if ($deployment->getAttribute('status') === 'failed') {
|
||||
throw new AppwriteException(AppwriteException::BUILD_FAILED);
|
||||
} else {
|
||||
throw new AppwriteException(AppwriteException::BUILD_NOT_READY);
|
||||
}
|
||||
}
|
||||
|
||||
if ($type === 'function') {
|
||||
@@ -473,24 +478,28 @@ function router(App $utopia, Database $dbForPlatform, callable $getProjectDB, Sw
|
||||
);
|
||||
|
||||
// Branded 404 override
|
||||
$isResponseBranded = false;
|
||||
if ($executionResponse['statusCode'] === 404 && $deployment->getAttribute('adapter', '') === 'static') {
|
||||
$layout = new View(__DIR__ . '/../views/general/404.phtml');
|
||||
$executionResponse['body'] = $layout->render();
|
||||
$executionResponse['headers']['content-length'] = \strlen($executionResponse['body']);
|
||||
$isResponseBranded = true;
|
||||
}
|
||||
|
||||
// Branded banner for previews
|
||||
if (\is_null($apiKey) || $apiKey->isBannerDisabled() === false) {
|
||||
$transformation = new Transformation();
|
||||
$transformation->addAdapter(new Preview());
|
||||
$transformation->setInput($executionResponse['body']);
|
||||
$transformation->setTraits($executionResponse['headers']);
|
||||
if ($isPreview && $transformation->transform()) {
|
||||
$executionResponse['body'] = $transformation->getOutput();
|
||||
if (!$isResponseBranded) {
|
||||
if (\is_null($apiKey) || $apiKey->isBannerDisabled() === false) {
|
||||
$transformation = new Transformation();
|
||||
$transformation->addAdapter(new Preview());
|
||||
$transformation->setInput($executionResponse['body']);
|
||||
$transformation->setTraits($executionResponse['headers']);
|
||||
if ($isPreview && $transformation->transform()) {
|
||||
$executionResponse['body'] = $transformation->getOutput();
|
||||
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
foreach ($executionResponse['headers'] as $key => $value) {
|
||||
if (\strtolower($key) === 'content-length') {
|
||||
$executionResponse['headers'][$key] = \strlen($executionResponse['body']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
<title>404 Not Found</title>
|
||||
|
||||
<style>
|
||||
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
|
||||
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
@@ -36,7 +38,7 @@
|
||||
background: var(--color-overlay-on-neutral, rgba(0, 0, 0, 0.06));
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
@@ -47,7 +49,7 @@
|
||||
h1 {
|
||||
color: var(--color-fgColor-neutral-primary, #2D2D31);
|
||||
text-align: center;
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XXXL, 32px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
@@ -59,7 +61,7 @@
|
||||
|
||||
button {
|
||||
border-radius: var(--border-radius-S, 8px);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-S, 14px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
@@ -88,7 +90,7 @@
|
||||
}
|
||||
|
||||
.brand p {
|
||||
font-family: var(--font-family-monospace, "Aeonik Fono");
|
||||
font-family: var(--font-family-monospace, "Fira Code"), monospace;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
|
||||
@@ -709,16 +709,16 @@
|
||||
},
|
||||
{
|
||||
"name": "google/protobuf",
|
||||
"version": "v4.30.1",
|
||||
"version": "v4.30.2",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/protocolbuffers/protobuf-php.git",
|
||||
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24"
|
||||
"reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/f29ba8a30dfd940efb3a8a75dc44446539101f24",
|
||||
"reference": "f29ba8a30dfd940efb3a8a75dc44446539101f24",
|
||||
"url": "https://api.github.com/repos/protocolbuffers/protobuf-php/zipball/a4c4d8565b40b9f76debc9dfeb221412eacb8ced",
|
||||
"reference": "a4c4d8565b40b9f76debc9dfeb221412eacb8ced",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -747,9 +747,9 @@
|
||||
"proto"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.1"
|
||||
"source": "https://github.com/protocolbuffers/protobuf-php/tree/v4.30.2"
|
||||
},
|
||||
"time": "2025-03-13T21:08:17+00:00"
|
||||
"time": "2025-03-26T18:01:50+00:00"
|
||||
},
|
||||
{
|
||||
"name": "jean85/pretty-package-versions",
|
||||
@@ -4205,16 +4205,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/migration",
|
||||
"version": "0.8.1",
|
||||
"version": "0.8.4",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/migration.git",
|
||||
"reference": "36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab"
|
||||
"reference": "845fd04ccf5e0edb03c184b864e0596080a432b8"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab",
|
||||
"reference": "36ec7af2e6bf78de5d86e1b0a953fd7dcdf69dab",
|
||||
"url": "https://api.github.com/repos/utopia-php/migration/zipball/845fd04ccf5e0edb03c184b864e0596080a432b8",
|
||||
"reference": "845fd04ccf5e0edb03c184b864e0596080a432b8",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4222,7 +4222,7 @@
|
||||
"ext-curl": "*",
|
||||
"ext-openssl": "*",
|
||||
"php": ">=8.1",
|
||||
"utopia-php/database": "0.61.*",
|
||||
"utopia-php/database": "0.*.*",
|
||||
"utopia-php/dsn": "0.2.*",
|
||||
"utopia-php/framework": "0.33.*",
|
||||
"utopia-php/storage": "0.18.*"
|
||||
@@ -4255,9 +4255,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/migration/issues",
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.8.1"
|
||||
"source": "https://github.com/utopia-php/migration/tree/0.8.4"
|
||||
},
|
||||
"time": "2025-03-18T07:48:08+00:00"
|
||||
"time": "2025-03-28T02:08:22+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/mongo",
|
||||
@@ -4700,16 +4700,16 @@
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/swoole",
|
||||
"version": "0.8.2",
|
||||
"version": "0.8.3",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/utopia-php/swoole.git",
|
||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4"
|
||||
"reference": "1af73dd3e73987cf729c7db399054e4a70befd99"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
||||
"reference": "5fa9d42c608ad46a4ce42a6d2b2eae00592fccd4",
|
||||
"url": "https://api.github.com/repos/utopia-php/swoole/zipball/1af73dd3e73987cf729c7db399054e4a70befd99",
|
||||
"reference": "1af73dd3e73987cf729c7db399054e4a70befd99",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -4745,9 +4745,9 @@
|
||||
],
|
||||
"support": {
|
||||
"issues": "https://github.com/utopia-php/swoole/issues",
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.2"
|
||||
"source": "https://github.com/utopia-php/swoole/tree/0.8.3"
|
||||
},
|
||||
"time": "2024-02-01T14:54:12+00:00"
|
||||
"time": "2025-03-26T10:09:05+00:00"
|
||||
},
|
||||
{
|
||||
"name": "utopia-php/system",
|
||||
@@ -5089,16 +5089,16 @@
|
||||
"packages-dev": [
|
||||
{
|
||||
"name": "appwrite/sdk-generator",
|
||||
"version": "0.40.10",
|
||||
"version": "0.40.11",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/appwrite/sdk-generator.git",
|
||||
"reference": "054ac96285caf4f77879087b2416a5ddb8263051"
|
||||
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/054ac96285caf4f77879087b2416a5ddb8263051",
|
||||
"reference": "054ac96285caf4f77879087b2416a5ddb8263051",
|
||||
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
|
||||
"reference": "0ec5f4a60c15e33e208bc3444ba6148b1d0f0027",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -5134,9 +5134,9 @@
|
||||
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
|
||||
"support": {
|
||||
"issues": "https://github.com/appwrite/sdk-generator/issues",
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.10"
|
||||
"source": "https://github.com/appwrite/sdk-generator/tree/0.40.11"
|
||||
},
|
||||
"time": "2025-03-25T13:44:16+00:00"
|
||||
"time": "2025-03-26T10:53:16+00:00"
|
||||
},
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
@@ -7527,16 +7527,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/console",
|
||||
"version": "v7.2.1",
|
||||
"version": "v7.2.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/console.git",
|
||||
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3"
|
||||
"reference": "e51498ea18570c062e7df29d05a7003585b19b88"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
|
||||
"reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3",
|
||||
"url": "https://api.github.com/repos/symfony/console/zipball/e51498ea18570c062e7df29d05a7003585b19b88",
|
||||
"reference": "e51498ea18570c062e7df29d05a7003585b19b88",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -7600,7 +7600,7 @@
|
||||
"terminal"
|
||||
],
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/console/tree/v7.2.1"
|
||||
"source": "https://github.com/symfony/console/tree/v7.2.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -7616,7 +7616,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2024-12-11T03:49:26+00:00"
|
||||
"time": "2025-03-12T08:11:12+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/filesystem",
|
||||
@@ -8131,16 +8131,16 @@
|
||||
},
|
||||
{
|
||||
"name": "symfony/process",
|
||||
"version": "v7.2.4",
|
||||
"version": "v7.2.5",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/symfony/process.git",
|
||||
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf"
|
||||
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
|
||||
"reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf",
|
||||
"url": "https://api.github.com/repos/symfony/process/zipball/87b7c93e57df9d8e39a093d32587702380ff045d",
|
||||
"reference": "87b7c93e57df9d8e39a093d32587702380ff045d",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
@@ -8172,7 +8172,7 @@
|
||||
"description": "Executes commands in sub-processes",
|
||||
"homepage": "https://symfony.com",
|
||||
"support": {
|
||||
"source": "https://github.com/symfony/process/tree/v7.2.4"
|
||||
"source": "https://github.com/symfony/process/tree/v7.2.5"
|
||||
},
|
||||
"funding": [
|
||||
{
|
||||
@@ -8188,7 +8188,7 @@
|
||||
"type": "tidelift"
|
||||
}
|
||||
],
|
||||
"time": "2025-02-05T08:33:46+00:00"
|
||||
"time": "2025-03-13T12:21:46+00:00"
|
||||
},
|
||||
{
|
||||
"name": "symfony/string",
|
||||
|
||||
@@ -205,7 +205,7 @@ services:
|
||||
appwrite-console:
|
||||
<<: *x-logging
|
||||
container_name: appwrite-console
|
||||
image: appwrite/console:5.3.0-sites-rc.30
|
||||
image: appwrite/console:5.3.0-sites-rc.36
|
||||
restart: unless-stopped
|
||||
networks:
|
||||
- appwrite
|
||||
|
||||
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 81 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 323 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 43 KiB |
|
After Width: | Height: | Size: 42 KiB |
|
After Width: | Height: | Size: 71 KiB |
|
After Width: | Height: | Size: 71 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-outside-1_6367_338401" maskUnits="userSpaceOnUse" x="0" y="0" width="30" height="30" fill="black">
|
||||
<rect fill="white" width="30" height="30"/>
|
||||
<path d="M1 10.6C1 7.23969 1 5.55953 1.65396 4.27606C2.2292 3.14708 3.14708 2.2292 4.27606 1.65396C5.55953 1 7.23969 1 10.6 1H19.4C22.7603 1 24.4405 1 25.7239 1.65396C26.8529 2.2292 27.7708 3.14708 28.346 4.27606C29 5.55953 29 7.23969 29 10.6V19.4C29 22.7603 29 24.4405 28.346 25.7239C27.7708 26.8529 26.8529 27.7708 25.7239 28.346C24.4405 29 22.7603 29 19.4 29H10.6C7.23969 29 5.55953 29 4.27606 28.346C3.14708 27.7708 2.2292 26.8529 1.65396 25.7239C1 24.4405 1 22.7603 1 19.4V10.6Z"/>
|
||||
</mask>
|
||||
<path d="M1 10.6C1 7.23969 1 5.55953 1.65396 4.27606C2.2292 3.14708 3.14708 2.2292 4.27606 1.65396C5.55953 1 7.23969 1 10.6 1H19.4C22.7603 1 24.4405 1 25.7239 1.65396C26.8529 2.2292 27.7708 3.14708 28.346 4.27606C29 5.55953 29 7.23969 29 10.6V19.4C29 22.7603 29 24.4405 28.346 25.7239C27.7708 26.8529 26.8529 27.7708 25.7239 28.346C24.4405 29 22.7603 29 19.4 29H10.6C7.23969 29 5.55953 29 4.27606 28.346C3.14708 27.7708 2.2292 26.8529 1.65396 25.7239C1 24.4405 1 22.7603 1 19.4V10.6Z" fill="#1D1D21"/>
|
||||
<path d="M4.27606 28.346L4.73005 27.455L4.27606 28.346ZM1.65396 25.7239L0.762954 26.1779L1.65396 25.7239ZM28.346 25.7239L29.237 26.1779L28.346 25.7239ZM25.7239 28.346L26.1779 29.237L25.7239 28.346ZM28.346 4.27606L27.455 4.73005L28.346 4.27606ZM10.6 2H19.4V0H10.6V2ZM28 10.6V19.4H30V10.6H28ZM19.4 28H10.6V30H19.4V28ZM2 19.4V10.6H0V19.4H2ZM10.6 28C8.90334 28 7.69099 27.9992 6.74064 27.9216C5.80197 27.8449 5.20731 27.6982 4.73005 27.455L3.82207 29.237C4.62827 29.6478 5.51543 29.8281 6.57778 29.9149C7.62846 30.0008 8.93634 30 10.6 30V28ZM0 19.4C0 21.0637 -0.000777706 22.3715 0.0850662 23.4222C0.171864 24.4846 0.352173 25.3717 0.762954 26.1779L2.54497 25.27C2.30179 24.7927 2.15512 24.198 2.07842 23.2594C2.00078 22.309 2 21.0967 2 19.4H0ZM4.73005 27.455C3.78924 26.9757 3.02433 26.2108 2.54497 25.27L0.762954 26.1779C1.43407 27.4951 2.50493 28.5659 3.82207 29.237L4.73005 27.455ZM28 19.4C28 21.0967 27.9992 22.309 27.9216 23.2594C27.8449 24.198 27.6982 24.7927 27.455 25.27L29.237 26.1779C29.6478 25.3717 29.8281 24.4846 29.9149 23.4222C30.0008 22.3715 30 21.0637 30 19.4H28ZM19.4 30C21.0637 30 22.3715 30.0008 23.4222 29.9149C24.4846 29.8281 25.3717 29.6478 26.1779 29.237L25.27 27.455C24.7927 27.6982 24.198 27.8449 23.2594 27.9216C22.309 27.9992 21.0967 28 19.4 28V30ZM27.455 25.27C26.9757 26.2108 26.2108 26.9757 25.27 27.455L26.1779 29.237C27.4951 28.5659 28.5659 27.4951 29.237 26.1779L27.455 25.27ZM19.4 2C21.0967 2 22.309 2.00078 23.2594 2.07842C24.198 2.15512 24.7927 2.30179 25.27 2.54497L26.1779 0.762954C25.3717 0.352173 24.4846 0.171864 23.4222 0.0850662C22.3715 -0.000777706 21.0637 0 19.4 0V2ZM30 10.6C30 8.93634 30.0008 7.62846 29.9149 6.57778C29.8281 5.51543 29.6478 4.62827 29.237 3.82207L27.455 4.73005C27.6982 5.20731 27.8449 5.80197 27.9216 6.74064C27.9992 7.69099 28 8.90334 28 10.6H30ZM25.27 2.54497C26.2108 3.02433 26.9757 3.78924 27.455 4.73005L29.237 3.82207C28.5659 2.50493 27.4951 1.43407 26.1779 0.762954L25.27 2.54497ZM10.6 0C8.93634 0 7.62846 -0.000777706 6.57778 0.0850662C5.51543 0.171864 4.62827 0.352173 3.82207 0.762954L4.73005 2.54497C5.20731 2.30179 5.80197 2.15512 6.74064 2.07842C7.69099 2.00078 8.90334 2 10.6 2V0ZM2 10.6C2 8.90334 2.00078 7.69099 2.07842 6.74064C2.15512 5.80197 2.30179 5.20731 2.54497 4.73005L0.762954 3.82207C0.352173 4.62827 0.171864 5.51543 0.0850662 6.57778C-0.000777706 7.62846 0 8.93634 0 10.6H2ZM3.82207 0.762954C2.50493 1.43407 1.43407 2.50493 0.762954 3.82207L2.54497 4.73005C3.02433 3.78924 3.78924 3.02433 4.73005 2.54497L3.82207 0.762954Z" fill="#414146" mask="url(#path-1-outside-1_6367_338401)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 9C8 8.44772 8.44772 8 9 8H12C12.5523 8 13 8.44772 13 9V12C13 12.5523 12.5523 13 12 13H9C8.44772 13 8 12.5523 8 12V9ZM10 11V10H11V11H10Z" fill="#818186"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 18C8 17.4477 8.44772 17 9 17H12C12.5523 17 13 17.4477 13 18V21C13 21.5523 12.5523 22 12 22H9C8.44772 22 8 21.5523 8 21V18ZM10 20V19H11V20H10Z" fill="#818186"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 8C17.4477 8 17 8.44772 17 9V12C17 12.5523 17.4477 13 18 13H21C21.5523 13 22 12.5523 22 12V9C22 8.44772 21.5523 8 21 8H18ZM19 10V11H20V10H19Z" fill="#818186"/>
|
||||
<path d="M16 9C16 8.44772 15.5523 8 15 8C14.4477 8 14 8.44772 14 9V10C14 10.5523 14.4477 11 15 11C15.5523 11 16 10.5523 16 10V9Z" fill="#818186"/>
|
||||
<path d="M15 12C15.5523 12 16 12.4477 16 13V14H18C18.5523 14 19 14.4477 19 15C19 15.5523 18.5523 16 18 16H15C14.4477 16 14 15.5523 14 15V13C14 12.4477 14.4477 12 15 12Z" fill="#818186"/>
|
||||
<path d="M21 14C20.4477 14 20 14.4477 20 15C20 15.5523 20.4477 16 21 16C21.5523 16 22 15.5523 22 15C22 14.4477 21.5523 14 21 14Z" fill="#818186"/>
|
||||
<path d="M14 18C14 17.4477 14.4477 17 15 17H16C16.5523 17 17 17.4477 17 18C17 18.5523 16.5523 19 16 19V21C16 21.5523 15.5523 22 15 22C14.4477 22 14 21.5523 14 21V18Z" fill="#818186"/>
|
||||
<path d="M12 16C12.5523 16 13 15.5523 13 15C13 14.4477 12.5523 14 12 14H9C8.44772 14 8 14.4477 8 15C8 15.5523 8.44772 16 9 16H12Z" fill="#818186"/>
|
||||
<path d="M22 18C22 18.5523 21.5523 19 21 19H19C18.4477 19 18 18.5523 18 18C18 17.4477 18.4477 17 19 17H21C21.5523 17 22 17.4477 22 18Z" fill="#818186"/>
|
||||
<path d="M21 22C21.5523 22 22 21.5523 22 21C22 20.4477 21.5523 20 21 20H18C17.4477 20 17 20.4477 17 21C17 21.5523 17.4477 22 18 22H21Z" fill="#818186"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,18 @@
|
||||
<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<mask id="path-1-outside-1_6367_338394" maskUnits="userSpaceOnUse" x="0" y="0" width="30" height="30" fill="black">
|
||||
<rect fill="white" width="30" height="30"/>
|
||||
<path d="M1 10.6C1 7.23969 1 5.55953 1.65396 4.27606C2.2292 3.14708 3.14708 2.2292 4.27606 1.65396C5.55953 1 7.23969 1 10.6 1H19.4C22.7603 1 24.4405 1 25.7239 1.65396C26.8529 2.2292 27.7708 3.14708 28.346 4.27606C29 5.55953 29 7.23969 29 10.6V19.4C29 22.7603 29 24.4405 28.346 25.7239C27.7708 26.8529 26.8529 27.7708 25.7239 28.346C24.4405 29 22.7603 29 19.4 29H10.6C7.23969 29 5.55953 29 4.27606 28.346C3.14708 27.7708 2.2292 26.8529 1.65396 25.7239C1 24.4405 1 22.7603 1 19.4V10.6Z"/>
|
||||
</mask>
|
||||
<path d="M1 10.6C1 7.23969 1 5.55953 1.65396 4.27606C2.2292 3.14708 3.14708 2.2292 4.27606 1.65396C5.55953 1 7.23969 1 10.6 1H19.4C22.7603 1 24.4405 1 25.7239 1.65396C26.8529 2.2292 27.7708 3.14708 28.346 4.27606C29 5.55953 29 7.23969 29 10.6V19.4C29 22.7603 29 24.4405 28.346 25.7239C27.7708 26.8529 26.8529 27.7708 25.7239 28.346C24.4405 29 22.7603 29 19.4 29H10.6C7.23969 29 5.55953 29 4.27606 28.346C3.14708 27.7708 2.2292 26.8529 1.65396 25.7239C1 24.4405 1 22.7603 1 19.4V10.6Z" fill="white"/>
|
||||
<path d="M4.27606 28.346L4.73005 27.455L4.27606 28.346ZM1.65396 25.7239L0.762954 26.1779L1.65396 25.7239ZM28.346 25.7239L29.237 26.1779L28.346 25.7239ZM25.7239 28.346L26.1779 29.237L25.7239 28.346ZM28.346 4.27606L27.455 4.73005L28.346 4.27606ZM10.6 2H19.4V0H10.6V2ZM28 10.6V19.4H30V10.6H28ZM19.4 28H10.6V30H19.4V28ZM2 19.4V10.6H0V19.4H2ZM10.6 28C8.90334 28 7.69099 27.9992 6.74064 27.9216C5.80197 27.8449 5.20731 27.6982 4.73005 27.455L3.82207 29.237C4.62827 29.6478 5.51543 29.8281 6.57778 29.9149C7.62846 30.0008 8.93634 30 10.6 30V28ZM0 19.4C0 21.0637 -0.000777706 22.3715 0.0850662 23.4222C0.171864 24.4846 0.352173 25.3717 0.762954 26.1779L2.54497 25.27C2.30179 24.7927 2.15512 24.198 2.07842 23.2594C2.00078 22.309 2 21.0967 2 19.4H0ZM4.73005 27.455C3.78924 26.9757 3.02433 26.2108 2.54497 25.27L0.762954 26.1779C1.43407 27.4951 2.50493 28.5659 3.82207 29.237L4.73005 27.455ZM28 19.4C28 21.0967 27.9992 22.309 27.9216 23.2594C27.8449 24.198 27.6982 24.7927 27.455 25.27L29.237 26.1779C29.6478 25.3717 29.8281 24.4846 29.9149 23.4222C30.0008 22.3715 30 21.0637 30 19.4H28ZM19.4 30C21.0637 30 22.3715 30.0008 23.4222 29.9149C24.4846 29.8281 25.3717 29.6478 26.1779 29.237L25.27 27.455C24.7927 27.6982 24.198 27.8449 23.2594 27.9216C22.309 27.9992 21.0967 28 19.4 28V30ZM27.455 25.27C26.9757 26.2108 26.2108 26.9757 25.27 27.455L26.1779 29.237C27.4951 28.5659 28.5659 27.4951 29.237 26.1779L27.455 25.27ZM19.4 2C21.0967 2 22.309 2.00078 23.2594 2.07842C24.198 2.15512 24.7927 2.30179 25.27 2.54497L26.1779 0.762954C25.3717 0.352173 24.4846 0.171864 23.4222 0.0850662C22.3715 -0.000777706 21.0637 0 19.4 0V2ZM30 10.6C30 8.93634 30.0008 7.62846 29.9149 6.57778C29.8281 5.51543 29.6478 4.62827 29.237 3.82207L27.455 4.73005C27.6982 5.20731 27.8449 5.80197 27.9216 6.74064C27.9992 7.69099 28 8.90334 28 10.6H30ZM25.27 2.54497C26.2108 3.02433 26.9757 3.78924 27.455 4.73005L29.237 3.82207C28.5659 2.50493 27.4951 1.43407 26.1779 0.762954L25.27 2.54497ZM10.6 0C8.93634 0 7.62846 -0.000777706 6.57778 0.0850662C5.51543 0.171864 4.62827 0.352173 3.82207 0.762954L4.73005 2.54497C5.20731 2.30179 5.80197 2.15512 6.74064 2.07842C7.69099 2.00078 8.90334 2 10.6 2V0ZM2 10.6C2 8.90334 2.00078 7.69099 2.07842 6.74064C2.15512 5.80197 2.30179 5.20731 2.54497 4.73005L0.762954 3.82207C0.352173 4.62827 0.171864 5.51543 0.0850662 6.57778C-0.000777706 7.62846 0 8.93634 0 10.6H2ZM3.82207 0.762954C2.50493 1.43407 1.43407 2.50493 0.762954 3.82207L2.54497 4.73005C3.02433 3.78924 3.78924 3.02433 4.73005 2.54497L3.82207 0.762954Z" fill="#D8D8DB" mask="url(#path-1-outside-1_6367_338394)"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 9C8 8.44772 8.44772 8 9 8H12C12.5523 8 13 8.44772 13 9V12C13 12.5523 12.5523 13 12 13H9C8.44772 13 8 12.5523 8 12V9ZM10 11V10H11V11H10Z" fill="#97979B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 18C8 17.4477 8.44772 17 9 17H12C12.5523 17 13 17.4477 13 18V21C13 21.5523 12.5523 22 12 22H9C8.44772 22 8 21.5523 8 21V18ZM10 20V19H11V20H10Z" fill="#97979B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18 8C17.4477 8 17 8.44772 17 9V12C17 12.5523 17.4477 13 18 13H21C21.5523 13 22 12.5523 22 12V9C22 8.44772 21.5523 8 21 8H18ZM19 10V11H20V10H19Z" fill="#97979B"/>
|
||||
<path d="M16 9C16 8.44772 15.5523 8 15 8C14.4477 8 14 8.44772 14 9V10C14 10.5523 14.4477 11 15 11C15.5523 11 16 10.5523 16 10V9Z" fill="#97979B"/>
|
||||
<path d="M15 12C15.5523 12 16 12.4477 16 13V14H18C18.5523 14 19 14.4477 19 15C19 15.5523 18.5523 16 18 16H15C14.4477 16 14 15.5523 14 15V13C14 12.4477 14.4477 12 15 12Z" fill="#97979B"/>
|
||||
<path d="M21 14C20.4477 14 20 14.4477 20 15C20 15.5523 20.4477 16 21 16C21.5523 16 22 15.5523 22 15C22 14.4477 21.5523 14 21 14Z" fill="#97979B"/>
|
||||
<path d="M14 18C14 17.4477 14.4477 17 15 17H16C16.5523 17 17 17.4477 17 18C17 18.5523 16.5523 19 16 19V21C16 21.5523 15.5523 22 15 22C14.4477 22 14 21.5523 14 21V18Z" fill="#97979B"/>
|
||||
<path d="M12 16C12.5523 16 13 15.5523 13 15C13 14.4477 12.5523 14 12 14H9C8.44772 14 8 14.4477 8 15C8 15.5523 8.44772 16 9 16H12Z" fill="#97979B"/>
|
||||
<path d="M22 18C22 18.5523 21.5523 19 21 19H19C18.4477 19 18 18.5523 18 18C18 17.4477 18.4477 17 19 17H21C21.5523 17 22 17.4477 22 18Z" fill="#97979B"/>
|
||||
<path d="M21 22C21.5523 22 22 21.5523 22 21C22 20.4477 21.5523 20 21 20H18C17.4477 20 17 20.4477 17 21C17 21.5523 17.4477 22 18 22H21Z" fill="#97979B"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 5.4 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
After Width: | Height: | Size: 86 KiB |
|
After Width: | Height: | Size: 4.3 KiB |
|
After Width: | Height: | Size: 4.1 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.8 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
@@ -24,6 +24,7 @@ class Key
|
||||
protected bool $bannerDisabled = false,
|
||||
protected bool $projectCheckDisabled = false,
|
||||
protected bool $previewAuthDisabled = false,
|
||||
protected bool $deploymentStatusIgnored = false,
|
||||
) {
|
||||
}
|
||||
|
||||
@@ -79,6 +80,11 @@ class Key
|
||||
return $this->previewAuthDisabled;
|
||||
}
|
||||
|
||||
public function isDeploymentStatusIgnored(): bool
|
||||
{
|
||||
return $this->deploymentStatusIgnored;
|
||||
}
|
||||
|
||||
public function isProjectCheckDisabled(): bool
|
||||
{
|
||||
return $this->projectCheckDisabled;
|
||||
@@ -139,6 +145,7 @@ class Key
|
||||
$bannerDisabled = $payload['bannerDisabled'] ?? false;
|
||||
$projectCheckDisabled = $payload['projectCheckDisabled'] ?? false;
|
||||
$previewAuthDisabled = $payload['previewAuthDisabled'] ?? false;
|
||||
$deploymentStatusIgnored = $payload['deploymentStatusIgnored'] ?? false;
|
||||
$scopes = \array_merge($payload['scopes'] ?? [], $scopes);
|
||||
|
||||
if (!$projectCheckDisabled && $projectId !== $project->getId()) {
|
||||
@@ -156,7 +163,8 @@ class Key
|
||||
$hostnameOverride,
|
||||
$bannerDisabled,
|
||||
$projectCheckDisabled,
|
||||
$previewAuthDisabled
|
||||
$previewAuthDisabled,
|
||||
$deploymentStatusIgnored
|
||||
);
|
||||
case API_KEY_STANDARD:
|
||||
$key = $project->find(
|
||||
|
||||
@@ -174,6 +174,7 @@ class Exception extends \Exception
|
||||
public const BUILD_NOT_READY = 'build_not_ready';
|
||||
public const BUILD_IN_PROGRESS = 'build_in_progress';
|
||||
public const BUILD_ALREADY_COMPLETED = 'build_already_completed';
|
||||
public const BUILD_FAILED = 'build_failed';
|
||||
|
||||
/** Execution */
|
||||
public const EXECUTION_NOT_FOUND = 'execution_not_found';
|
||||
|
||||
@@ -93,6 +93,7 @@ abstract class Migration
|
||||
'1.6.0' => 'V21',
|
||||
'1.6.1' => 'V21',
|
||||
'1.6.2' => 'V22',
|
||||
'1.7.0' => 'V23',
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
<?php
|
||||
|
||||
namespace Appwrite\Migration\Version;
|
||||
|
||||
use Appwrite\Migration\Migration;
|
||||
use Exception;
|
||||
use Throwable;
|
||||
use Utopia\CLI\Console;
|
||||
use Utopia\Database\Database;
|
||||
|
||||
class V23 extends Migration
|
||||
{
|
||||
/**
|
||||
* @throws Throwable
|
||||
*/
|
||||
public function execute(): void
|
||||
{
|
||||
/**
|
||||
* Disable SubQueries for Performance.
|
||||
*/
|
||||
foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) {
|
||||
Database::addFilter(
|
||||
$name,
|
||||
fn () => null,
|
||||
fn () => []
|
||||
);
|
||||
}
|
||||
|
||||
Console::info('Migrating Collections');
|
||||
$this->migrateCollections();
|
||||
}
|
||||
|
||||
/**
|
||||
* Migrate Collections.
|
||||
*
|
||||
* @return void
|
||||
* @throws Exception|Throwable
|
||||
*/
|
||||
private function migrateCollections(): void
|
||||
{
|
||||
$internalProjectId = $this->project->getInternalId();
|
||||
$collectionType = match ($internalProjectId) {
|
||||
'console' => 'console',
|
||||
default => 'projects',
|
||||
};
|
||||
|
||||
$collections = $this->collections[$collectionType];
|
||||
foreach ($collections as $collection) {
|
||||
$id = $collection['$id'];
|
||||
|
||||
Console::log("Migrating Collection \"{$id}\"");
|
||||
|
||||
$this->projectDB->setNamespace("_$internalProjectId");
|
||||
|
||||
switch ($id) {
|
||||
case 'memberships':
|
||||
// Create roles index
|
||||
try {
|
||||
$this->createIndexFromCollection($this->projectDB, $id, '_key_roles');
|
||||
} catch (Throwable $th) {
|
||||
Console::warning("'_key_roles' from {$id}: {$th->getMessage()}");
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
usleep(50000);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,9 @@ class XList extends Base
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/functions/runtimes')
|
||||
->groups(['api', 'functions'])
|
||||
->groups(['api'])
|
||||
->desc('List runtimes')
|
||||
->label('scope', 'functions.read')
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'functions',
|
||||
|
||||
@@ -30,7 +30,7 @@ class XList extends Base
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/functions/templates')
|
||||
->desc('List templates')
|
||||
->groups(['api', 'functions'])
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_FUNCTIONS)
|
||||
->label('sdk', new Method(
|
||||
@@ -71,10 +71,14 @@ class XList extends Base
|
||||
});
|
||||
}
|
||||
|
||||
$responseTemplates = \array_slice($templates, $offset, $limit);
|
||||
\usort($templates, function ($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
$templates = \array_slice($templates, $offset, $limit);
|
||||
$response->dynamic(new Document([
|
||||
'templates' => $responseTemplates,
|
||||
'total' => \count($responseTemplates),
|
||||
'templates' => $templates,
|
||||
'total' => \count($templates),
|
||||
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,6 +41,8 @@ use Utopia\Storage\Device\Local;
|
||||
use Utopia\System\System;
|
||||
use Utopia\VCS\Adapter\Git\GitHub;
|
||||
|
||||
use function Swoole\Coroutine\batch;
|
||||
|
||||
class Builds extends Action
|
||||
{
|
||||
public static function getName(): string
|
||||
@@ -646,7 +648,11 @@ class Builds extends Action
|
||||
Co::join([
|
||||
Co\go(function () use ($executor, &$response, $project, $deployment, $source, $resource, $runtime, $vars, $command, $cpus, $memory, $timeout, &$err, $version) {
|
||||
try {
|
||||
$command = $version === 'v2' ? 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh' : 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh "' . \trim(\escapeshellarg($command), "\'") . '"';
|
||||
if ($version === 'v2') {
|
||||
$command = 'tar -zxf /tmp/code.tar.gz -C /usr/code && cd /usr/local/src/ && ./build.sh';
|
||||
} else {
|
||||
$command = 'tar -zxf /tmp/code.tar.gz -C /mnt/code && helpers/build.sh ' . \trim(\escapeshellarg($command));
|
||||
}
|
||||
|
||||
$response = $executor->createRuntime(
|
||||
deploymentId: $deployment->getId(),
|
||||
@@ -783,7 +789,6 @@ class Builds extends Action
|
||||
$deployment->setAttribute('buildStartAt', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
|
||||
$deployment->setAttribute('buildEndAt', $endTime);
|
||||
$deployment->setAttribute('buildDuration', \intval(\ceil($durationEnd - $durationStart)));
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$deployment->setAttribute('buildPath', $response['path']);
|
||||
$deployment->setAttribute('buildSize', $response['size']);
|
||||
$deployment->setAttribute('totalSize', $deployment->getAttribute('buildSize', 0) + $deployment->getAttribute('sourceSize', 0));
|
||||
@@ -792,25 +797,21 @@ class Builds extends Action
|
||||
foreach ($response['output'] as $log) {
|
||||
$logs .= $log['content'];
|
||||
}
|
||||
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][37m Screenshot capturing started. [0m\n";
|
||||
}
|
||||
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
|
||||
}
|
||||
|
||||
Console::success("Build id: $deploymentId created");
|
||||
|
||||
/** Screenshot site */
|
||||
if ($resource->getCollection() === 'sites') {
|
||||
try {
|
||||
$rule = Authorization::skip(fn () => $dbForPlatform->findOne('rules', [
|
||||
@@ -848,33 +849,53 @@ class Builds extends Action
|
||||
'bannerDisabled' => true,
|
||||
'projectCheckDisabled' => true,
|
||||
'previewAuthDisabled' => true,
|
||||
'deploymentStatusIgnored' => true
|
||||
]);
|
||||
|
||||
// TODO: @Meldiron if becomes too slow, do concurrently
|
||||
foreach ($configs as $key => $config) {
|
||||
$config['headers'] = \array_merge($config['headers'] ?? [], [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey
|
||||
]);
|
||||
$screenshotError = null;
|
||||
$screenshots = batch(\array_map(function ($key) use ($configs, $apiKey, $resource, $client, &$screenshotError) {
|
||||
return function () use ($key, $configs, $apiKey, $resource, $client, &$screenshotError) {
|
||||
try {
|
||||
$config = $configs[$key];
|
||||
|
||||
$config['sleep'] = 3000;
|
||||
$config['headers'] = \array_merge($config['headers'] ?? [], [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey
|
||||
]);
|
||||
$config['sleep'] = 3000;
|
||||
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
if (!is_null($framework)) {
|
||||
$config['sleep'] = $framework['screenshotSleep'];
|
||||
}
|
||||
$frameworks = Config::getParam('frameworks', []);
|
||||
$framework = $frameworks[$resource->getAttribute('framework', '')] ?? null;
|
||||
if (!is_null($framework)) {
|
||||
$config['sleep'] = $framework['screenshotSleep'];
|
||||
}
|
||||
|
||||
$response = $client->fetch(
|
||||
url: 'http://appwrite-browser:3000/v1/screenshots',
|
||||
method: 'POST',
|
||||
body: $config
|
||||
);
|
||||
$fetchResponse = $client->fetch(
|
||||
url: 'http://appwrite-browser:3000/v1/screenshots',
|
||||
method: 'POST',
|
||||
body: $config
|
||||
);
|
||||
|
||||
if ($response->getStatusCode() >= 400) {
|
||||
throw new \Exception($response->getBody());
|
||||
}
|
||||
if ($fetchResponse->getStatusCode() >= 400) {
|
||||
throw new \Exception($fetchResponse->getBody());
|
||||
}
|
||||
|
||||
$screenshot = $response->getBody();
|
||||
$screenshot = $fetchResponse->getBody();
|
||||
|
||||
return ['key' => $key, 'screenshot' => $screenshot];
|
||||
} catch (\Throwable $th) {
|
||||
$screenshotError = $th->getMessage();
|
||||
return;
|
||||
}
|
||||
};
|
||||
}, \array_keys($configs)));
|
||||
|
||||
if (!\is_null($screenshotError)) {
|
||||
throw new \Exception($screenshotError);
|
||||
}
|
||||
|
||||
foreach ($screenshots as $data) {
|
||||
$key = $data['key'];
|
||||
$screenshot = $data['screenshot'];
|
||||
|
||||
$fileId = ID::unique();
|
||||
$fileName = $fileId . '.png';
|
||||
@@ -911,12 +932,17 @@ class Builds extends Action
|
||||
'search' => implode(' ', [$fileId, $fileName]),
|
||||
'metadata' => ['content_type' => $deviceForFiles->getFileMimeType($path)],
|
||||
]);
|
||||
$file = Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getInternalId(), $file));
|
||||
|
||||
Authorization::skip(fn () => $dbForPlatform->createDocument('bucket_' . $bucket->getInternalId(), $file));
|
||||
|
||||
$deployment->setAttribute($key, $fileId);
|
||||
}
|
||||
|
||||
$dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][37m Screenshot capturing finished. [0m\n";
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
@@ -925,9 +951,39 @@ class Builds extends Action
|
||||
Console::warning("Screenshot failed to generate:");
|
||||
Console::warning($th->getMessage());
|
||||
Console::warning($th->getTraceAsString());
|
||||
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][33m Screenshot capturing failed. Deployment will continue. [0m\n";
|
||||
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
|
||||
}
|
||||
}
|
||||
|
||||
$logs = $deployment->getAttribute('buildLogs', '');
|
||||
$date = \date('H:i:s');
|
||||
$logs .= "[90m[$date] [90m[[0mappwrite[90m][32m Deployment finished. [0m\n";
|
||||
$deployment->setAttribute('buildLogs', $logs);
|
||||
|
||||
/** Update the status */
|
||||
$deployment->setAttribute('status', 'ready');
|
||||
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment);
|
||||
|
||||
if ($deployment->getInternalId() === $resource->getAttribute('latestDeploymentInternalId', '')) {
|
||||
$resource = $resource->setAttribute('latestDeploymentStatus', $deployment->getAttribute('status', ''));
|
||||
$dbForProject->updateDocument($resource->getCollection(), $resource->getId(), $resource);
|
||||
}
|
||||
|
||||
$queueForRealtime
|
||||
->setPayload($deployment->getArrayCopy())
|
||||
->trigger();
|
||||
|
||||
if ($isVcsEnabled) {
|
||||
$this->runGitAction('ready', $github, $providerCommitHash, $owner, $repositoryName, $project, $resource, $deployment->getId(), $dbForProject, $dbForPlatform);
|
||||
}
|
||||
|
||||
Console::success("Build id: $deploymentId created");
|
||||
|
||||
/** Set auto deploy */
|
||||
if ($deployment->getAttribute('activate') === true) {
|
||||
$resource->setAttribute('live', true);
|
||||
@@ -1066,6 +1122,12 @@ class Builds extends Action
|
||||
Authorization::skip(fn () => $dbForPlatform->updateDocument('schedules', $schedule->getId(), $schedule));
|
||||
}
|
||||
} catch (\Throwable $th) {
|
||||
Console::warning('Build failed:');
|
||||
Console::error($th->getMessage());
|
||||
Console::error($th->getFile());
|
||||
Console::error($th->getLine());
|
||||
Console::error($th->getTraceAsString());
|
||||
|
||||
if ($dbForProject->getDocument('deployments', $deploymentId)->getAttribute('status') === 'canceled') {
|
||||
Console::info('Build has been canceled');
|
||||
return;
|
||||
@@ -1338,15 +1400,9 @@ class Builds extends Action
|
||||
default => throw new \Exception('Invalid resource type')
|
||||
};
|
||||
|
||||
$previweQrCode = match($resource->getCollection()) {
|
||||
'functions' => '',
|
||||
'sites' => 'https://cloud.appwrite.io/v1/avatars/qr?text=' . $previewUrl,
|
||||
default => throw new \Exception('Invalid resource type')
|
||||
};
|
||||
|
||||
$comment = new Comment();
|
||||
$comment->parseComment($github->getComment($owner, $repositoryName, $commentId));
|
||||
$comment->addBuild($project, $resource, $resourceType, $status, $deployment->getId(), ['type' => 'logs'], $previewUrl, $previweQrCode);
|
||||
$comment->addBuild($project, $resource, $resourceType, $status, $deployment->getId(), ['type' => 'logs'], $previewUrl);
|
||||
$github->updateComment($owner, $repositoryName, $commentId, $comment->generateComment());
|
||||
} finally {
|
||||
$dbForPlatform->deleteDocument('vcsCommentLocks', $commentId);
|
||||
|
||||
@@ -39,7 +39,7 @@ class Create extends Base
|
||||
$this
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_POST)
|
||||
->setHttpPath('/v1/sites/:siteId/deployments/template')
|
||||
->desc('Create deployment')
|
||||
->desc('Create template deployment')
|
||||
->groups(['api', 'sites'])
|
||||
->label('scope', 'sites.write')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
|
||||
@@ -27,8 +27,8 @@ class XList extends Base
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/sites/frameworks')
|
||||
->desc('List frameworks')
|
||||
->groups(['api', 'sites'])
|
||||
->label('scope', 'sites.read')
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
->label('sdk', new Method(
|
||||
namespace: 'sites',
|
||||
|
||||
@@ -29,7 +29,7 @@ class Get extends Base
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/sites/templates/:templateId')
|
||||
->desc('Get site template')
|
||||
->groups(['api', 'sites'])
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
->label('sdk', new Method(
|
||||
|
||||
@@ -30,7 +30,7 @@ class XList extends Base
|
||||
->setHttpMethod(Action::HTTP_REQUEST_METHOD_GET)
|
||||
->setHttpPath('/v1/sites/templates')
|
||||
->desc('List templates')
|
||||
->groups(['api', 'sites'])
|
||||
->groups(['api'])
|
||||
->label('scope', 'public')
|
||||
->label('resourceType', RESOURCE_TYPE_SITES)
|
||||
->label('sdk', new Method(
|
||||
@@ -76,10 +76,15 @@ class XList extends Base
|
||||
});
|
||||
}
|
||||
|
||||
$responseTemplates = \array_slice($templates, $offset, $limit);
|
||||
\usort($templates, function ($a, $b) {
|
||||
return $b['score'] <=> $a['score'];
|
||||
});
|
||||
|
||||
$templates = \array_slice($templates, $offset, $limit);
|
||||
|
||||
$response->dynamic(new Document([
|
||||
'templates' => $responseTemplates,
|
||||
'total' => \count($responseTemplates),
|
||||
'templates' => $templates,
|
||||
'total' => \count($templates),
|
||||
]), Response::MODEL_TEMPLATE_SITE_LIST);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -831,7 +831,6 @@ class Deletes extends Action
|
||||
$this->deleteBuildFiles($deviceForBuilds, $document);
|
||||
$this->deleteDeploymentFiles($deviceForSites, $document);
|
||||
$this->deleteDeploymentScreenshots($deviceForFiles, $dbForPlatform, $document);
|
||||
$this->deleteDeploymentRules($dbForPlatform, $document, $project, $certificates);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -846,20 +845,6 @@ class Deletes extends Action
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete rules for all deployments of the site
|
||||
*/
|
||||
foreach ($deploymentIds as $deploymentId) {
|
||||
Console::info("Deleting rules for site " . $siteId . "'s deployment " . $deploymentId);
|
||||
$this->deleteByGroup('rules', [
|
||||
Query::equal('type', ['deployment']),
|
||||
Query::equal('value', [$deploymentId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
|
||||
$this->deleteRule($dbForPlatform, $document, $certificates);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete VCS Repositories and VCS Comments
|
||||
*/
|
||||
@@ -900,7 +885,7 @@ class Deletes extends Action
|
||||
Console::info("Deleting rules for function " . $functionId);
|
||||
$this->deleteByGroup('rules', [
|
||||
Query::equal('type', ['deployment']),
|
||||
Query::equal('deploymentResourceType', ['site']),
|
||||
Query::equal('deploymentResourceType', ['function']),
|
||||
Query::equal('deploymentResourceInternalId', [$functionInternalId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
], $dbForPlatform, function (Document $document) use ($project, $dbForPlatform, $certificates) {
|
||||
@@ -928,7 +913,6 @@ class Deletes extends Action
|
||||
$deploymentInternalIds[] = $document->getInternalId();
|
||||
$this->deleteDeploymentFiles($deviceForFunctions, $document);
|
||||
$this->deleteBuildFiles($deviceForBuilds, $document);
|
||||
$this->deleteDeploymentRules($dbForPlatform, $document, $project, $certificates);
|
||||
});
|
||||
|
||||
/**
|
||||
@@ -963,18 +947,6 @@ class Deletes extends Action
|
||||
$this->deleteRuntimes($getProjectDB, $document, $project);
|
||||
}
|
||||
|
||||
private function deleteDeploymentRules(Database $dbForPlatform, Document $deployment, Document $project, CertificatesAdapter $certificates): void
|
||||
{
|
||||
Console::info("Deleting rules for site " . $deployment->getId());
|
||||
$this->deleteByGroup('rules', [
|
||||
Query::equal('type', ['deployment']),
|
||||
Query::equal('deploymentInternalId', [$deployment->getInternalId()]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
|
||||
$this->deleteRule($dbForPlatform, $document, $certificates);
|
||||
});
|
||||
}
|
||||
|
||||
private function deleteDeploymentScreenshots(Device $deviceForFiles, Database $dbForPlatform, Document $deployment): void
|
||||
{
|
||||
$screenshotIds = [];
|
||||
@@ -1125,9 +1097,9 @@ class Deletes extends Action
|
||||
*/
|
||||
Console::info("Deleting rules for deployment " . $deploymentId);
|
||||
$this->deleteByGroup('rules', [
|
||||
Query::equal('trigger', ['manual']),
|
||||
Query::equal('trigger', ['deployment']),
|
||||
Query::equal('type', ['deployment']),
|
||||
Query::equal('deploymentResourceInternalId', [$deploymentInternalId]),
|
||||
Query::equal('deploymentInternalId', [$deploymentInternalId]),
|
||||
Query::equal('projectInternalId', [$project->getInternalId()])
|
||||
], $dbForPlatform, function (Document $document) use ($dbForPlatform, $certificates) {
|
||||
$this->deleteRule($dbForPlatform, $document, $certificates);
|
||||
|
||||
@@ -33,7 +33,15 @@ class Preview extends Adapter
|
||||
|
||||
$banner = <<<EOT
|
||||
<style>
|
||||
@import url(https://fonts.bunny.net/css?family=fira-code:400|inter:400);
|
||||
|
||||
#appwrite-preview {
|
||||
min-width: auto;
|
||||
min-height: auto;
|
||||
max-width: none;
|
||||
max-height: none;
|
||||
width: auto;
|
||||
height: auto;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
position: fixed;
|
||||
@@ -80,7 +88,7 @@ class Preview extends Adapter
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
color: var(--color-fgColor-neutral-secondary, #56565C);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 500;
|
||||
@@ -97,7 +105,7 @@ class Preview extends Adapter
|
||||
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-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
@@ -120,7 +128,7 @@ class Preview extends Adapter
|
||||
|
||||
#appwrite-preview-text {
|
||||
color: var(--color-fgColor-neutral-secondary, #C3C3C6);
|
||||
font-family: var(--font-family-sansSerif, Inter);
|
||||
font-family: var(--font-family-sansSerif, Inter), sans-serif;
|
||||
font-size: var(--font-size-XS, 12px);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,12 +9,12 @@ class Memberships extends Base
|
||||
'teamId',
|
||||
'invited',
|
||||
'joined',
|
||||
'confirm'
|
||||
'confirm',
|
||||
'roles',
|
||||
];
|
||||
|
||||
/**
|
||||
* Expression constructor
|
||||
*
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
|
||||
@@ -58,6 +58,12 @@ class ConsoleVariables extends Model
|
||||
'default' => '',
|
||||
'example' => 'sites.localhost',
|
||||
])
|
||||
->addRule('_APP_DOMAIN_FUNCTIONS', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'A domain to use for function URLs.',
|
||||
'default' => '',
|
||||
'example' => 'functions.localhost',
|
||||
])
|
||||
->addRule(
|
||||
'_APP_OPTIONS_FORCE_HTTPS',
|
||||
[
|
||||
|
||||
@@ -22,6 +22,12 @@ class TemplateSite extends Model
|
||||
'default' => '',
|
||||
'example' => 'Starter site',
|
||||
])
|
||||
->addRule('tagline', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'Short description of template',
|
||||
'default' => '',
|
||||
'example' => 'Minimal web app integrating with Appwrite.',
|
||||
])
|
||||
->addRule('demoUrl', [
|
||||
'type' => self::TYPE_STRING,
|
||||
'description' => 'URL hosting a template demo.',
|
||||
|
||||
@@ -9,10 +9,11 @@ use Utopia\System\System;
|
||||
|
||||
class Comment
|
||||
{
|
||||
// TODO: Add more tips
|
||||
protected array $tips = [
|
||||
'Appwrite has a Discord community with over 16 000 members. [Come join us!](https://appwrite.io/discord)',
|
||||
'You can use [Avatars API](https://appwrite.io/docs/client/avatars?sdk=web-default#avatarsGetQR) to generate QR code for any text or URLs',
|
||||
'[Cursor pagination](https://appwrite.io/docs/pagination#cursor-pagination) performs better than offset pagination when loading further pages',
|
||||
'Appwrite has a Discord community with over 16 000 members.',
|
||||
'You can use Avatars API to generate QR code for any text or URLs.',
|
||||
'Cursor pagination performs better than offset pagination when loading further pages.',
|
||||
];
|
||||
|
||||
protected string $statePrefix = '[appwrite]: #';
|
||||
@@ -27,7 +28,7 @@ class Comment
|
||||
return \count($this->builds) === 0;
|
||||
}
|
||||
|
||||
public function addBuild(Document $project, Document $resource, string $resourceType, string $buildStatus, string $deploymentId, array $action, string $previewUrl, string $previewQrCode): void
|
||||
public function addBuild(Document $project, Document $resource, string $resourceType, string $buildStatus, string $deploymentId, array $action, string $previewUrl): void
|
||||
{
|
||||
// Unique index
|
||||
$id = $project->getId() . '_' . $resource->getId();
|
||||
@@ -41,7 +42,6 @@ class Comment
|
||||
'buildStatus' => $buildStatus,
|
||||
'deploymentId' => $deploymentId,
|
||||
'action' => $action,
|
||||
'previewQrCode' => $previewQrCode,
|
||||
'previewUrl' => $previewUrl,
|
||||
];
|
||||
}
|
||||
@@ -70,7 +70,6 @@ class Comment
|
||||
'deploymentId' => $build['deploymentId'],
|
||||
'action' => $build['action'],
|
||||
'previewUrl' => $build['previewUrl'],
|
||||
'previewQrCode' => $build['previewQrCode']
|
||||
];
|
||||
} elseif ($build['resourceType'] === 'function') {
|
||||
$projects[$build['projectId']]['function'][$build['resourceId']] = [
|
||||
@@ -82,32 +81,36 @@ class Comment
|
||||
}
|
||||
}
|
||||
|
||||
$i = 0;
|
||||
foreach ($projects as $projectId => $project) {
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
$text .= "Project name: **{$project['name']}** \nProject ID: `{$projectId}`\n\n";
|
||||
$text .= "## {$project['name']}\n\n";
|
||||
$text .= "Project ID: `{$projectId}`\n\n";
|
||||
|
||||
$isOpen = $i === 0;
|
||||
|
||||
if (\count($project['site']) > 0) {
|
||||
$text .= "<details" . ($isOpen ? ' open' : '') . ">\n";
|
||||
$text .= "<summary>Sites (" . \count($project['site']) . ")</summary>\n\n";
|
||||
$text .= "<br>\n\n";
|
||||
|
||||
$text .= "| Site | ID | Status | Previews | Action |\n";
|
||||
$text .= "| Site | Status | Logs | Preview | QR\n";
|
||||
$text .= "| :- | :- | :- | :- | :- |\n";
|
||||
|
||||
foreach ($project['site'] as $siteId => $site) {
|
||||
$generateImage = function (string $status) use ($protocol, $hostname) {
|
||||
$extention = $status === 'building' ? 'gif' : 'png';
|
||||
$imagesUrl = $protocol . '://' . $hostname . '/console/images/vcs/';
|
||||
$imageUrl = '<picture><source media="(prefers-color-scheme: dark)" srcset="' . $imagesUrl . 'status-' . $status . '-dark.' . $extention . '"><img alt="' . $status . '" height="25" align="center" src="' . $imagesUrl . 'status-' . $status . '-light.' . $extention . '"></picture>';
|
||||
$extension = $site['status'] === 'building' ? 'gif' : 'png';
|
||||
|
||||
return $imageUrl;
|
||||
};
|
||||
$pathLight = '/images/vcs/status-' . $site['status'] . '-light.' . $extension;
|
||||
$pathDark = '/images/vcs/status-' . $site['status'] . '-dark.' . $extension;
|
||||
|
||||
$status = match ($site['status']) {
|
||||
'waiting' => $generateImage('waiting') . ' Waiting to build',
|
||||
'processing' => $generateImage('processing') . ' Processing',
|
||||
'building' => $generateImage('building') . ' Building',
|
||||
'ready' => $generateImage('ready') . ' Ready',
|
||||
'failed' => $generateImage('failed') . ' Failed',
|
||||
'waiting' => $this->generatImage($pathLight, $pathDark, 'Queued', 85) . ' _Queued_',
|
||||
'processing' => $this->generatImage($pathLight, $pathDark, 'Processing', 85) . ' _Processing_',
|
||||
'building' => $this->generatImage($pathLight, $pathDark, 'Building', 85) . ' _Building_',
|
||||
'ready' => $this->generatImage($pathLight, $pathDark, 'Ready', 85) . ' _Ready_',
|
||||
'failed' => $this->generatImage($pathLight, $pathDark, 'Failed', 85) . ' _Failed_',
|
||||
};
|
||||
|
||||
if ($site['action']['type'] === 'logs') {
|
||||
@@ -116,33 +119,44 @@ class Comment
|
||||
$action = '[Authorize](' . $site['action']['url'] . ')';
|
||||
}
|
||||
|
||||
$previews = '[Preview URL](' . $site['previewUrl'] . ') [QR Code](' . $site['previewQrCode'] . ')';
|
||||
$qrImagePathLight = '/images/vcs/qr-light.svg';
|
||||
$qrImagePathDark = '/images/vcs/qr-dark.svg';
|
||||
|
||||
$text .= "| {$site['name']} | `{$siteId}` | {$status} | {$previews} | {$action} |\n";
|
||||
$consoleUrl = $protocol . '://' . $hostname . '/v1/avatars/qr?text=' . \urlencode($site['previewUrl']);
|
||||
$qr = '[' . $this->generatImage($qrImagePathLight, $qrImagePathDark, 'QR Code', 28) . '](' . $consoleUrl . ')';
|
||||
|
||||
$preview = '[Preview URL](' . $site['previewUrl'] . ')';
|
||||
|
||||
$text .= "| **{$site['name']}**<br>`$siteId`";
|
||||
$text .= "| {$status}";
|
||||
$text .= "| {$action}";
|
||||
$text .= "| {$preview}";
|
||||
$text .= "| {$qr}";
|
||||
$text .= "|\n";
|
||||
}
|
||||
|
||||
$text .= "\n\n";
|
||||
$text .= "\n</details>\n\n";
|
||||
}
|
||||
|
||||
if (\count($project['function']) > 0) {
|
||||
|
||||
$text .= "| Function | ID | Status | Action |\n";
|
||||
$text .= "<details" . ($isOpen ? ' open' : '') . ">\n";
|
||||
$text .= "<summary>Functions (" . \count($project['function']) . ")</summary>\n\n";
|
||||
$text .= "<br>\n\n";
|
||||
$text .= "| Function | ID | Status | Logs |\n";
|
||||
$text .= "| :- | :- | :- | :- |\n";
|
||||
|
||||
foreach ($project['function'] as $functionId => $function) {
|
||||
$generateImage = function (string $status) use ($protocol, $hostname) {
|
||||
$extention = $status === 'building' ? 'gif' : 'png';
|
||||
$imagesUrl = $protocol . '://' . $hostname . '/console/images/vcs/';
|
||||
$imageUrl = '<picture><source media="(prefers-color-scheme: dark)" srcset="' . $imagesUrl . 'status-' . $status . '-dark.' . $extention . '"><img alt="' . $status . '" height="25" align="center" src="' . $imagesUrl . 'status-' . $status . '-light.' . $extention . '"></picture>';
|
||||
return $imageUrl;
|
||||
};
|
||||
$extension = $site['status'] === 'building' ? 'gif' : 'png';
|
||||
|
||||
$status = match ($function['status']) {
|
||||
'waiting' => $generateImage('waiting') . ' Waiting to build',
|
||||
'processing' => $generateImage('processing') . ' Processing',
|
||||
'building' => $generateImage('building') . ' Building',
|
||||
'ready' => $generateImage('ready') . ' Ready',
|
||||
'failed' => $generateImage('failed') . ' Failed',
|
||||
$pathLight = '/images/vcs/status-' . $site['status'] . '-light.' . $extension;
|
||||
$pathDark = '/images/vcs/status-' . $site['status'] . '-dark.' . $extension;
|
||||
|
||||
$status = match ($site['status']) {
|
||||
'waiting' => $this->generatImage($pathLight, $pathDark, 'Queued', 85) . ' _Queued_',
|
||||
'processing' => $this->generatImage($pathLight, $pathDark, 'Processing', 85) . ' _Processing_',
|
||||
'building' => $this->generatImage($pathLight, $pathDark, 'Building', 85) . ' _Building_',
|
||||
'ready' => $this->generatImage($pathLight, $pathDark, 'Ready', 85) . ' _Ready_',
|
||||
'failed' => $this->generatImage($pathLight, $pathDark, 'Failed', 85) . ' _Failed_',
|
||||
};
|
||||
|
||||
if ($function['action']['type'] === 'logs') {
|
||||
@@ -151,22 +165,45 @@ class Comment
|
||||
$action = '[Authorize](' . $function['action']['url'] . ')';
|
||||
}
|
||||
|
||||
$text .= "| {$function['name']} | `{$functionId}` | {$status} | {$action} |\n";
|
||||
$text .= "| **{$function['name']}**<br>`$functionId`";
|
||||
$text .= "| {$status}";
|
||||
$text .= "| {$action}";
|
||||
$text .= "|\n";
|
||||
}
|
||||
|
||||
$text .= "\n\n";
|
||||
$text .= "</details>\n\n";
|
||||
}
|
||||
|
||||
$text .= "</details>\n\n";
|
||||
|
||||
$isLast = $i === \count($projects) - 1;
|
||||
|
||||
if (\count($projects) > 1 && $isLast) {
|
||||
$text .= "---\n\n";
|
||||
}
|
||||
|
||||
$i++;
|
||||
}
|
||||
|
||||
$text .= "Only deployments on the production branch are activated automatically. Learn more about Appwrite [Functions](https://appwrite.io/docs/functions) and [Sites](https://appwrite.io/docs/sites).\n\n";
|
||||
|
||||
$tip = $this->tips[array_rand($this->tips)];
|
||||
$text .= "> **💡 Did you know?** \n " . $tip . "\n\n";
|
||||
$text .= "\n<br>\n\n> [!NOTE]\n> $tip\n\n";
|
||||
|
||||
return $text;
|
||||
}
|
||||
|
||||
public function generatImage(string $pathLight, string $pathDark, string $alt, int $width): string
|
||||
{
|
||||
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
|
||||
$hostname = System::getEnv('_APP_DOMAIN');
|
||||
|
||||
$imageLight = $protocol . '://' . $hostname . $pathLight;
|
||||
$imageDark = $protocol . '://' . $hostname . $pathDark;
|
||||
|
||||
$imageUrl = '<picture><source media="(prefers-color-scheme: dark)" srcset="' . $imageDark . '"><img alt="' . $alt . '" width="' . $width . '" align="center" src="' . $imageLight . '"></picture>';
|
||||
|
||||
return $imageUrl;
|
||||
}
|
||||
|
||||
public function parseComment(string $comment): self
|
||||
{
|
||||
$state = \explode("\n", $comment)[0] ?? '';
|
||||
|
||||
@@ -24,7 +24,7 @@ class ConsoleConsoleClientTest extends Scope
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertCount(10, $response['body']);
|
||||
$this->assertCount(11, $response['body']);
|
||||
$this->assertIsString($response['body']['_APP_DOMAIN_TARGET']);
|
||||
$this->assertIsInt($response['body']['_APP_STORAGE_LIMIT']);
|
||||
$this->assertIsInt($response['body']['_APP_COMPUTE_SIZE_LIMIT']);
|
||||
@@ -33,6 +33,7 @@ class ConsoleConsoleClientTest extends Scope
|
||||
$this->assertIsBool($response['body']['_APP_VCS_ENABLED']);
|
||||
$this->assertIsBool($response['body']['_APP_ASSISTANT_ENABLED']);
|
||||
$this->assertIsString($response['body']['_APP_DOMAIN_SITES']);
|
||||
$this->assertIsString($response['body']['_APP_DOMAIN_FUNCTIONS']);
|
||||
$this->assertIsString($response['body']['_APP_OPTIONS_FORCE_HTTPS']);
|
||||
$this->assertIsString($response['body']['_APP_DOMAINS_NAMESERVERS']);
|
||||
// When adding new keys, dont forget to update count a few lines above
|
||||
|
||||
@@ -321,7 +321,16 @@ class FunctionsCustomClientTest extends Scope
|
||||
$this->assertContains($template['useCases'][0], ['starter', 'ai']);
|
||||
}
|
||||
$this->assertArrayHasKey('runtimes', $templates['body']['templates'][0]);
|
||||
$this->assertContains('bun-1.0', array_column($templates['body']['templates'][0]['runtimes'], 'name'));
|
||||
|
||||
foreach ($templates['body']['templates'] as $template) {
|
||||
$this->assertThat(
|
||||
\array_column($template['runtimes'], 'name'),
|
||||
$this->logicalOr(
|
||||
$this->containsEqual('bun-1.0'),
|
||||
$this->containsEqual('dart-2.16'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
// List templates with pagination and filters
|
||||
$templates = $this->client->call(Client::METHOD_GET, '/functions/templates', array_merge([
|
||||
|
||||
@@ -1514,7 +1514,7 @@ class FunctionsCustomServerTest extends Scope
|
||||
$runtimes = $this->client->call(Client::METHOD_GET, '/functions/runtimes', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $runtimes['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $runtimes['body']['total']);
|
||||
|
||||
@@ -134,7 +134,7 @@ class AvatarsTest extends Scope
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $graphQLPayload);
|
||||
|
||||
$this->assertEquals(29428, \strlen($qrCode['body']));
|
||||
$this->assertEquals(9169, \strlen($qrCode['body']));
|
||||
|
||||
return $qrCode['body'];
|
||||
}
|
||||
|
||||
@@ -252,7 +252,7 @@ class FunctionsServerTest extends Scope
|
||||
$runtimes = $this->client->call(Client::METHOD_POST, '/graphql', \array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $projectId,
|
||||
], $this->getHeaders()), $gqlPayload);
|
||||
]), $gqlPayload);
|
||||
|
||||
$this->assertIsArray($runtimes['body']['data']);
|
||||
$this->assertArrayNotHasKey('errors', $runtimes['body']);
|
||||
|
||||
@@ -346,7 +346,7 @@ class ProxyCustomServerTest extends Scope
|
||||
$ruleId = $rule['body']['$id'];
|
||||
|
||||
$rule = $this->updateRuleVerification($ruleId);
|
||||
$this->assertEquals(401, $rule['headers']['status-code']);
|
||||
$this->assertEquals(400, $rule['headers']['status-code']);
|
||||
|
||||
$this->cleanupRule($ruleId);
|
||||
}
|
||||
|
||||
@@ -614,19 +614,26 @@ class RealtimeConsoleClientTest extends Scope
|
||||
|
||||
$previousBuildLogs = $response['data']['payload']['buildLogs'];
|
||||
|
||||
$this->assertThat(
|
||||
$response['data']['payload']['status'],
|
||||
$this->logicalOr(
|
||||
$this->equalTo('building'),
|
||||
$this->equalTo('ready'),
|
||||
),
|
||||
);
|
||||
$this->assertEquals('building', $response['data']['payload']['status']);
|
||||
|
||||
if ($response['data']['payload']['status'] === 'ready') {
|
||||
if (!empty($response['data']['payload']['buildEndAt'])) {
|
||||
$this->assertNotEmpty($response['data']['payload']['buildEndAt']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildStartAt']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildDuration']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildPath']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildSize']);
|
||||
$this->assertNotEmpty($response['data']['payload']['totalSize']);
|
||||
$this->assertNotEmpty($response['data']['payload']['buildLogs']);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$response = json_decode($client->receive(), true);
|
||||
$this->assertContains("functions.{$functionId}.deployments.{$deploymentId}.update", $response['data']['events']);
|
||||
$this->assertContains('console', $response['data']['channels']);
|
||||
$this->assertContains("projects.{$projectId}", $response['data']['channels']);
|
||||
$this->assertEquals("ready", $response['data']['payload']['status']);
|
||||
|
||||
$client->close();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +37,7 @@ class SitesCustomClientTest extends Scope
|
||||
$this->assertArrayHasKey('variables', $template);
|
||||
$this->assertArrayHasKey('screenshotDark', $template);
|
||||
$this->assertArrayHasKey('screenshotLight', $template);
|
||||
$this->assertArrayHasKey('tagline', $template);
|
||||
}
|
||||
|
||||
// List templates with pagination
|
||||
@@ -126,6 +127,7 @@ class SitesCustomClientTest extends Scope
|
||||
$this->assertEquals('React starter', $template['body']['name']);
|
||||
$this->assertEquals(['starter'], $template['body']['useCases']);
|
||||
$this->assertEquals('github', $template['body']['vcsProvider']);
|
||||
$this->assertEquals('Simple React application integrated with Appwrite SDK.', $template['body']['tagline']);
|
||||
$this->assertIsArray($template['body']['frameworks']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-dark.png', $template['body']['screenshotDark']);
|
||||
$this->assertEquals('http://localhost/images/sites/templates/starter-for-react-light.png', $template['body']['screenshotLight']);
|
||||
|
||||
@@ -1377,7 +1377,7 @@ class SitesCustomServerTest extends Scope
|
||||
$frameworks = $this->client->call(Client::METHOD_GET, '/sites/frameworks', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
]));
|
||||
|
||||
$this->assertEquals(200, $frameworks['headers']['status-code']);
|
||||
$this->assertGreaterThan(0, $frameworks['body']['total']);
|
||||
@@ -1736,6 +1736,14 @@ class SitesCustomServerTest extends Scope
|
||||
$this->assertStringContainsString("Preview by", $response['body']);
|
||||
$this->assertGreaterThan($contentLength, $response['headers']['content-length']);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/non-existing-path', followRedirects: false, headers: [
|
||||
'x-appwrite-key' => API_KEY_DYNAMIC . '_' . $apiKey,
|
||||
]);
|
||||
$this->assertEquals(404, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString("Page not found", $response['body']);
|
||||
$this->assertStringNotContainsString("Preview by", $response['body']);
|
||||
$this->assertGreaterThan($contentLength, $response['headers']['content-length']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
@@ -2393,4 +2401,106 @@ class SitesCustomServerTest extends Scope
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testDomainForFailedDeloyment(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Test Site',
|
||||
'framework' => 'astro',
|
||||
'buildRuntime' => 'node-22',
|
||||
'buildCommand' => 'cd random'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$this->assertNotEmpty($domain);
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
|
||||
$deployment = $this->createDeployment($siteId, [
|
||||
'code' => $this->packageSite('astro'),
|
||||
'activate' => true
|
||||
]);
|
||||
$this->assertEquals(202, $deployment['headers']['status-code']);
|
||||
|
||||
$deploymentId = $deployment['body']['$id'];
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$this->assertEventually(function () use ($siteId, $deploymentId) {
|
||||
$deployment = $this->getDeployment($siteId, $deploymentId);
|
||||
$this->assertEquals('failed', $deployment['body']['status'], json_encode($deployment['body'], JSON_PRETTY_PRINT));
|
||||
}, 100000, 500);
|
||||
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
$this->assertStringContainsString('build_failed', $response['body']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testPermanentRedirect(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'Sub project site',
|
||||
'framework' => 'other',
|
||||
'buildRuntime' => 'node-22',
|
||||
'outputDirectory' => './'
|
||||
]);
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$domain = $this->setupSiteDomain($siteId);
|
||||
$this->assertNotEmpty($domain);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('sub-directories'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$proxyClient = new Client();
|
||||
$proxyClient->setEndpoint('http://' . $domain);
|
||||
$response = $proxyClient->call(Client::METHOD_GET, '/');
|
||||
$this->assertEquals(200, $response['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory index', $response['body']);
|
||||
$response1 = $proxyClient->call(Client::METHOD_GET, '/project1');
|
||||
$this->assertEquals(200, $response1['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory project1', $response1['body']);
|
||||
$response2 = $proxyClient->call(Client::METHOD_GET, '/project1/');
|
||||
$this->assertEquals(200, $response2['headers']['status-code']);
|
||||
$this->assertStringContainsString('Sub-directory project1', $response2['body']);
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
|
||||
public function testDeploymentCommandEscaping(): void
|
||||
{
|
||||
$siteId = $this->setupSite([
|
||||
'siteId' => ID::unique(),
|
||||
'name' => 'A site',
|
||||
'framework' => 'other',
|
||||
'adapter' => 'static',
|
||||
'buildRuntime' => 'static-1',
|
||||
'outputDirectory' => './',
|
||||
'buildCommand' => "echo 'Hello two'",
|
||||
'installCommand' => 'echo "Hello one"',
|
||||
'fallbackFile' => '',
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($siteId);
|
||||
|
||||
$deploymentId = $this->setupDeployment($siteId, [
|
||||
'code' => $this->packageSite('static'),
|
||||
'activate' => 'true'
|
||||
]);
|
||||
|
||||
$this->assertNotEmpty($deploymentId);
|
||||
|
||||
$deployment = $this->getDeployment($siteId, $deploymentId);
|
||||
$this->assertEquals(200, $deployment['headers']['status-code']);
|
||||
$this->assertStringContainsString('Hello one', $deployment['body']['buildLogs']);
|
||||
$this->assertStringContainsString('Hello two', $deployment['body']['buildLogs']);
|
||||
|
||||
$this->cleanupSite($siteId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -801,6 +801,97 @@ trait UsersBase
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetUser
|
||||
*/
|
||||
public function testListUserMemberships(array $data): array
|
||||
{
|
||||
/**
|
||||
* Test for SUCCESS
|
||||
*/
|
||||
|
||||
// create a new team
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'name' => 'Test Team',
|
||||
]);
|
||||
|
||||
// create a new membership
|
||||
$membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team['body']['$id'] . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => $data['userId'],
|
||||
'roles' => ['new-role'],
|
||||
]);
|
||||
|
||||
// list the memberships
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()));
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals($response['body']['memberships'][0]['$id'], $membership['body']['$id']);
|
||||
$this->assertEquals($response['body']['memberships'][0]['roles'], ['new-role']);
|
||||
$this->assertEquals($response['body']['total'], 1);
|
||||
|
||||
// create another membership with a new role
|
||||
$team = $this->client->call(Client::METHOD_POST, '/teams', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'teamId' => 'unique()',
|
||||
'name' => 'Test Team 2',
|
||||
]);
|
||||
|
||||
$membership = $this->client->call(Client::METHOD_POST, '/teams/' . $team['body']['$id'] . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'userId' => $data['userId'],
|
||||
'roles' => ['new-role-2'],
|
||||
]);
|
||||
|
||||
// list out memberships and query by role
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::contains('roles', ['new-role-2'])->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['headers']['status-code'], 200);
|
||||
$this->assertEquals($response['body']['memberships'][0]['$id'], $membership['body']['$id']);
|
||||
$this->assertEquals($response['body']['memberships'][0]['roles'], ['new-role-2']);
|
||||
$this->assertEquals($response['body']['total'], 1);
|
||||
|
||||
/**
|
||||
* Test for FAILURE
|
||||
*/
|
||||
|
||||
// query using equal on array field
|
||||
$response = $this->client->call(Client::METHOD_GET, '/users/' . $data['userId'] . '/memberships', array_merge([
|
||||
'content-type' => 'application/json',
|
||||
'x-appwrite-project' => $this->getProject()['$id'],
|
||||
], $this->getHeaders()), [
|
||||
'queries' => [
|
||||
Query::equal('roles', ['new-role-2'])->toString()
|
||||
]
|
||||
]);
|
||||
|
||||
$this->assertEquals($response['body']['code'], 400);
|
||||
$this->assertEquals($response['body']['message'], 'Invalid `queries` param: Invalid query: Cannot query equal on attribute "roles" because it is an array.');
|
||||
$this->assertEquals($response['body']['type'], 'general_argument_invalid');
|
||||
|
||||
return $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* @depends testGetUser
|
||||
*/
|
||||
|
||||
|
Before Width: | Height: | Size: 28 KiB After Width: | Height: | Size: 8.9 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 4.9 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 7.4 KiB |
@@ -0,0 +1,5 @@
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Sub-directory index</p>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,5 @@
|
||||
<html lang="en">
|
||||
<body>
|
||||
<p>Sub-directory project1</p>
|
||||
</body>
|
||||
</html>
|
||||