diff --git a/app/config/specs/open-api3-latest-client.json b/app/config/specs/open-api3-latest-client.json index a626c47bd0..21a66361e5 100644 --- a/app/config/specs/open-api3-latest-client.json +++ b/app/config/specs/open-api3-latest-client.json @@ -5033,7 +5033,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -5084,7 +5084,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -5543,7 +5543,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -5625,7 +5625,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/open-api3-latest-console.json b/app/config/specs/open-api3-latest-console.json index 954f3fcef7..60795ac276 100644 --- a/app/config/specs/open-api3-latest-console.json +++ b/app/config/specs/open-api3-latest-console.json @@ -4293,7 +4293,7 @@ }, "x-appwrite": { "method": "chat", - "weight": 328, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -4337,6 +4337,73 @@ } } }, + "\/console\/resources": { + "get": { + "summary": "Check resource ID availability", + "operationId": "consoleGetResource", + "tags": [ + "console" + ], + "description": "", + "responses": { + "204": { + "description": "No content" + } + }, + "x-appwrite": { + "method": "getResource", + "weight": 419, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "console\/get-resource.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCheck if a resource ID is available.", + "rate-limit": 10, + "rate-time": 60, + "rate-key": "userId:{userId}, url:{url}", + "scope": "rules.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "value", + "description": "Resource value.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "query" + }, + { + "name": "type", + "description": "Resource type.", + "required": true, + "schema": { + "type": "string", + "x-example": "rules", + "enum": [ + "rules" + ], + "x-enum-name": null, + "x-enum-keys": [] + }, + "in": "query" + } + ] + } + }, "\/console\/variables": { "get": { "summary": "Get variables", @@ -4359,7 +4426,7 @@ }, "x-appwrite": { "method": "variables", - "weight": 327, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -8986,7 +9053,7 @@ }, "x-appwrite": { "method": "list", - "weight": 389, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9058,7 +9125,7 @@ }, "x-appwrite": { "method": "create", - "weight": 387, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9248,26 +9315,6 @@ "description": "Path to function code in the linked repo.", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to function code in the template repo.", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "x-example": "" - }, "specification": { "type": "string", "description": "Runtime specification for the function and builds.", @@ -9307,7 +9354,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 390, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9691,7 +9738,7 @@ }, "x-appwrite": { "method": "update", - "weight": 388, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -10059,7 +10106,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 391, + "weight": 389, "cookies": false, "type": "upload", "deprecated": false, @@ -10132,6 +10179,108 @@ } } }, + "\/functions\/{functionId}\/deployments\/template": { + "post": { + "summary": "Create template deployment", + "operationId": "functionsCreateTemplateDeployment", + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 390, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to function code in the template repo.", + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the function template.", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + } + } + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -10955,7 +11104,7 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "x-example": false } }, @@ -10991,7 +11140,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -11042,7 +11191,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -12774,7 +12923,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 379, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -12849,7 +12998,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 376, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -12992,7 +13141,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 383, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -13137,7 +13286,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 378, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -13310,7 +13459,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 385, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13487,7 +13636,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 377, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -13595,7 +13744,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 384, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -13706,7 +13855,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 382, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -13758,7 +13907,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 386, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13819,7 +13968,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 380, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -13893,7 +14042,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 381, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -13967,7 +14116,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 351, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14042,7 +14191,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 350, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -14146,7 +14295,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 363, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14253,7 +14402,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 349, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -14337,7 +14486,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 362, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14424,7 +14573,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 341, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -14538,7 +14687,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 354, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -14655,7 +14804,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 344, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -14749,7 +14898,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 357, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -14846,7 +14995,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 342, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -14950,7 +15099,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 355, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -15057,7 +15206,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 343, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15199,7 +15348,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 356, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15343,7 +15492,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 345, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15437,7 +15586,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 358, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -15534,7 +15683,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 346, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -15628,7 +15777,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 359, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -15725,7 +15874,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 347, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15819,7 +15968,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 360, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -15916,7 +16065,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 348, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -16010,7 +16159,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 361, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -16107,7 +16256,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 353, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16159,7 +16308,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 364, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -16220,7 +16369,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 352, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16294,7 +16443,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 373, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -16368,7 +16517,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 366, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -16441,7 +16590,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 365, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16523,7 +16672,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 368, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -16582,7 +16731,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 369, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -16658,7 +16807,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 370, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -16719,7 +16868,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 367, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -16793,7 +16942,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 372, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -16876,7 +17025,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16965,7 +17114,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 374, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -17027,7 +17176,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17101,7 +17250,7 @@ }, "x-appwrite": { "method": "list", - "weight": 333, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -17174,7 +17323,7 @@ }, "x-appwrite": { "method": "createAppwriteMigration", - "weight": 329, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -17261,7 +17410,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 335, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -17353,7 +17502,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 330, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -17428,7 +17577,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 336, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17499,7 +17648,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 332, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -17609,7 +17758,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 338, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -17741,7 +17890,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 331, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -17845,7 +17994,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 337, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -17968,7 +18117,7 @@ }, "x-appwrite": { "method": "get", - "weight": 334, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -18025,7 +18174,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 339, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -18075,7 +18224,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 340, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18307,7 +18456,7 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", "x-example": false } }, @@ -18450,6 +18599,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", + "x-example": false } }, "required": [ @@ -23344,7 +23498,7 @@ }, "x-appwrite": { "method": "listRules", - "weight": 311, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -23400,7 +23554,7 @@ "tags": [ "proxy" ], - "description": "Create a new proxy rule.", + "description": "", "responses": { "201": { "description": "Rule", @@ -23415,15 +23569,15 @@ }, "x-appwrite": { "method": "createRule", - "weight": 310, + "weight": 420, "cookies": false, "type": "", "deprecated": false, "demo": "proxy\/create-rule.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/proxy\/create-rule.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new proxy rule.", + "rate-limit": 10, + "rate-time": 60, + "rate-key": "userId:{userId}, url:{url}", "scope": "rules.write", "platforms": [ "console" @@ -23499,7 +23653,7 @@ }, "x-appwrite": { "method": "getRule", - "weight": 312, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -23549,7 +23703,7 @@ }, "x-appwrite": { "method": "deleteRule", - "weight": 313, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -23608,7 +23762,7 @@ }, "x-appwrite": { "method": "updateRuleVerification", - "weight": 314, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -23667,7 +23821,7 @@ }, "x-appwrite": { "method": "list", - "weight": 394, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -23736,7 +23890,7 @@ }, "x-appwrite": { "method": "create", - "weight": 392, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -23817,11 +23971,6 @@ "description": "Output Directory for site.", "x-example": "" }, - "subdomain": { - "type": "string", - "description": "Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.", - "x-example": "" - }, "buildRuntime": { "type": "string", "description": "Runtime to use during build step.", @@ -23928,26 +24077,6 @@ "description": "Path to site code in the linked repo.", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to site code in the template repo.", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "x-example": "" - }, "specification": { "type": "string", "description": "Framework specification for the site and builds.", @@ -23988,7 +24117,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 397, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -24261,7 +24390,7 @@ }, "x-appwrite": { "method": "get", - "weight": 393, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -24319,7 +24448,7 @@ }, "x-appwrite": { "method": "update", - "weight": 395, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -24542,7 +24671,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 396, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -24684,7 +24813,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 398, + "weight": 397, "cookies": false, "type": "upload", "deprecated": false, @@ -24762,6 +24891,108 @@ } } }, + "\/sites\/{siteId}\/deployments\/template": { + "post": { + "summary": "Create deployment", + "operationId": "sitesCreateTemplateDeployment", + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 398, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to site code in the template repo.", + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the site template.", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + } + } + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -25649,7 +25880,7 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "x-example": false } }, @@ -25814,6 +26045,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "x-example": false } }, "required": [ diff --git a/app/config/specs/open-api3-latest-server.json b/app/config/specs/open-api3-latest-server.json index eb740ba0da..f6a62e21b5 100644 --- a/app/config/specs/open-api3-latest-server.json +++ b/app/config/specs/open-api3-latest-server.json @@ -8138,7 +8138,7 @@ }, "x-appwrite": { "method": "list", - "weight": 389, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -8211,7 +8211,7 @@ }, "x-appwrite": { "method": "create", - "weight": 387, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -8402,26 +8402,6 @@ "description": "Path to function code in the linked repo.", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to function code in the template repo.", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "x-example": "" - }, "specification": { "type": "string", "description": "Runtime specification for the function and builds.", @@ -8461,7 +8441,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 390, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -8619,7 +8599,7 @@ }, "x-appwrite": { "method": "update", - "weight": 388, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -8990,7 +8970,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 391, + "weight": 389, "cookies": false, "type": "upload", "deprecated": false, @@ -9064,6 +9044,109 @@ } } }, + "\/functions\/{functionId}\/deployments\/template": { + "post": { + "summary": "Create template deployment", + "operationId": "functionsCreateTemplateDeployment", + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 390, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to function code in the template repo.", + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the function template.", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + } + } + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -9819,7 +9902,7 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", "x-example": false } }, @@ -9855,7 +9938,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -9908,7 +9991,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -11680,7 +11763,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 379, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -11756,7 +11839,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 376, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -11900,7 +11983,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 383, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -12046,7 +12129,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 378, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -12220,7 +12303,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 385, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -12398,7 +12481,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 377, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -12507,7 +12590,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 384, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -12619,7 +12702,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 382, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -12672,7 +12755,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 386, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -12734,7 +12817,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 380, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -12809,7 +12892,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 381, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -12884,7 +12967,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 351, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -12960,7 +13043,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 350, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -13065,7 +13148,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 363, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13173,7 +13256,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 349, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -13258,7 +13341,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 362, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -13346,7 +13429,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 341, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -13461,7 +13544,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 354, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -13579,7 +13662,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 344, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -13674,7 +13757,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 357, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -13772,7 +13855,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 342, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -13877,7 +13960,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 355, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -13985,7 +14068,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 343, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -14128,7 +14211,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 356, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14273,7 +14356,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 345, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14368,7 +14451,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 358, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14466,7 +14549,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 346, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -14561,7 +14644,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 359, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -14659,7 +14742,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 347, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -14754,7 +14837,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 360, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -14852,7 +14935,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 348, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -14947,7 +15030,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 361, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -15045,7 +15128,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 353, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15098,7 +15181,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 364, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -15160,7 +15243,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 352, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -15235,7 +15318,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 373, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -15310,7 +15393,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 366, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15384,7 +15467,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 365, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15467,7 +15550,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 368, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -15527,7 +15610,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 369, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -15604,7 +15687,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 370, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -15666,7 +15749,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 367, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -15741,7 +15824,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 372, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -15825,7 +15908,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -15916,7 +15999,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 374, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -15979,7 +16062,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16055,7 +16138,7 @@ }, "x-appwrite": { "method": "list", - "weight": 394, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -16125,7 +16208,7 @@ }, "x-appwrite": { "method": "create", - "weight": 392, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -16207,11 +16290,6 @@ "description": "Output Directory for site.", "x-example": "" }, - "subdomain": { - "type": "string", - "description": "Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.", - "x-example": "" - }, "buildRuntime": { "type": "string", "description": "Runtime to use during build step.", @@ -16318,26 +16396,6 @@ "description": "Path to site code in the linked repo.", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to site code in the template repo.", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "x-example": "" - }, "specification": { "type": "string", "description": "Framework specification for the site and builds.", @@ -16378,7 +16436,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 397, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -16427,7 +16485,7 @@ }, "x-appwrite": { "method": "get", - "weight": 393, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -16486,7 +16544,7 @@ }, "x-appwrite": { "method": "update", - "weight": 395, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -16710,7 +16768,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 396, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -16854,7 +16912,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 398, + "weight": 397, "cookies": false, "type": "upload", "deprecated": false, @@ -16933,6 +16991,109 @@ } } }, + "\/sites\/{siteId}\/deployments\/template": { + "post": { + "summary": "Create deployment", + "operationId": "sitesCreateTemplateDeployment", + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "content": { + "application\/json": { + "schema": { + "$ref": "#\/components\/schemas\/deployment" + } + } + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 398, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "schema": { + "type": "string", + "x-example": "" + }, + "in": "path" + } + ], + "requestBody": { + "content": { + "application\/json": { + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to site code in the template repo.", + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the site template.", + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + } + } + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -17751,7 +17912,7 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", "x-example": false } }, @@ -17918,6 +18079,11 @@ "type": "string", "description": "Variable value. Max length: 8192 chars.", "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "x-example": false } }, "required": [ diff --git a/app/config/specs/swagger2-latest-client.json b/app/config/specs/swagger2-latest-client.json index 1ee6677c92..923594c51d 100644 --- a/app/config/specs/swagger2-latest-client.json +++ b/app/config/specs/swagger2-latest-client.json @@ -5198,7 +5198,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -5271,7 +5271,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -5768,7 +5768,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -5852,7 +5852,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, diff --git a/app/config/specs/swagger2-latest-console.json b/app/config/specs/swagger2-latest-console.json index e1b2a4da74..94da70da74 100644 --- a/app/config/specs/swagger2-latest-console.json +++ b/app/config/specs/swagger2-latest-console.json @@ -4497,7 +4497,7 @@ }, "x-appwrite": { "method": "chat", - "weight": 328, + "weight": 326, "cookies": false, "type": "", "deprecated": false, @@ -4542,6 +4542,73 @@ ] } }, + "\/console\/resources": { + "get": { + "summary": "Check resource ID availability", + "operationId": "consoleGetResource", + "consumes": [ + "application\/json" + ], + "produces": [], + "tags": [ + "console" + ], + "description": "", + "responses": { + "204": { + "description": "No content" + } + }, + "x-appwrite": { + "method": "getResource", + "weight": 419, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "console\/get-resource.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCheck if a resource ID is available.", + "rate-limit": 10, + "rate-time": 60, + "rate-key": "userId:{userId}, url:{url}", + "scope": "rules.read", + "platforms": [ + "console" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [] + } + ], + "parameters": [ + { + "name": "value", + "description": "Resource value.", + "required": true, + "type": "string", + "x-example": "", + "in": "query" + }, + { + "name": "type", + "description": "Resource type.", + "required": true, + "type": "string", + "x-example": "rules", + "enum": [ + "rules" + ], + "x-enum-name": null, + "x-enum-keys": [], + "in": "query" + } + ] + } + }, "\/console\/variables": { "get": { "summary": "Get variables", @@ -4566,7 +4633,7 @@ }, "x-appwrite": { "method": "variables", - "weight": 327, + "weight": 325, "cookies": false, "type": "", "deprecated": false, @@ -9135,7 +9202,7 @@ }, "x-appwrite": { "method": "list", - "weight": 389, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -9206,7 +9273,7 @@ }, "x-appwrite": { "method": "create", - "weight": 387, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -9414,30 +9481,6 @@ "default": "", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "default": "", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "default": "", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to function code in the template repo.", - "default": "", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "default": "", - "x-example": "" - }, "specification": { "type": "string", "description": "Runtime specification for the function and builds.", @@ -9479,7 +9522,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 390, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -9863,7 +9906,7 @@ }, "x-appwrite": { "method": "update", - "weight": 388, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -10245,7 +10288,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 391, + "weight": 389, "cookies": false, "type": "upload", "deprecated": false, @@ -10312,6 +10355,111 @@ ] } }, + "\/functions\/{functionId}\/deployments\/template": { + "post": { + "summary": "Create template deployment", + "operationId": "functionsCreateTemplateDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 390, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "default": null, + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "default": null, + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to function code in the template repo.", + "default": null, + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the function template.", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + ] + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -11137,8 +11285,8 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", - "default": false, + "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", + "default": true, "x-example": false } }, @@ -11175,7 +11323,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -11248,7 +11396,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -13030,7 +13178,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 379, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -13104,7 +13252,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 376, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -13261,7 +13409,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 383, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -13415,7 +13563,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 378, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -13609,7 +13757,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 385, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -13802,7 +13950,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 377, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -13919,7 +14067,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 384, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -14034,7 +14182,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 382, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -14088,7 +14236,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 386, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -14149,7 +14297,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 380, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -14222,7 +14370,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 381, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -14295,7 +14443,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 351, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -14369,7 +14517,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 350, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -14483,7 +14631,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 363, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -14595,7 +14743,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 349, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -14685,7 +14833,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 362, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -14773,7 +14921,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 341, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -14899,7 +15047,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 354, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -15023,7 +15171,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 344, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -15125,7 +15273,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 357, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -15225,7 +15373,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 342, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -15339,7 +15487,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 355, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -15451,7 +15599,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 343, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -15609,7 +15757,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 356, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -15764,7 +15912,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 345, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -15866,7 +16014,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 358, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -15966,7 +16114,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 346, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -16068,7 +16216,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 359, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -16168,7 +16316,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 347, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -16270,7 +16418,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 360, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -16370,7 +16518,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 348, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -16472,7 +16620,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 361, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -16572,7 +16720,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 353, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -16626,7 +16774,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 364, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -16687,7 +16835,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 352, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -16760,7 +16908,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 373, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -16833,7 +16981,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 366, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -16905,7 +17053,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 365, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -16994,7 +17142,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 368, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -17053,7 +17201,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 369, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -17131,7 +17279,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 370, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -17192,7 +17340,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 367, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -17265,7 +17413,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 372, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -17345,7 +17493,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -17434,7 +17582,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 374, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -17496,7 +17644,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -17568,7 +17716,7 @@ }, "x-appwrite": { "method": "list", - "weight": 333, + "weight": 331, "cookies": false, "type": "", "deprecated": false, @@ -17640,7 +17788,7 @@ }, "x-appwrite": { "method": "createAppwriteMigration", - "weight": 329, + "weight": 327, "cookies": false, "type": "", "deprecated": false, @@ -17733,7 +17881,7 @@ }, "x-appwrite": { "method": "getAppwriteReport", - "weight": 335, + "weight": 333, "cookies": false, "type": "", "deprecated": false, @@ -17820,7 +17968,7 @@ }, "x-appwrite": { "method": "createFirebaseMigration", - "weight": 330, + "weight": 328, "cookies": false, "type": "", "deprecated": false, @@ -17899,7 +18047,7 @@ }, "x-appwrite": { "method": "getFirebaseReport", - "weight": 336, + "weight": 334, "cookies": false, "type": "", "deprecated": false, @@ -17969,7 +18117,7 @@ }, "x-appwrite": { "method": "createNHostMigration", - "weight": 332, + "weight": 330, "cookies": false, "type": "", "deprecated": false, @@ -18089,7 +18237,7 @@ }, "x-appwrite": { "method": "getNHostReport", - "weight": 338, + "weight": 336, "cookies": false, "type": "", "deprecated": false, @@ -18208,7 +18356,7 @@ }, "x-appwrite": { "method": "createSupabaseMigration", - "weight": 331, + "weight": 329, "cookies": false, "type": "", "deprecated": false, @@ -18321,7 +18469,7 @@ }, "x-appwrite": { "method": "getSupabaseReport", - "weight": 337, + "weight": 335, "cookies": false, "type": "", "deprecated": false, @@ -18433,7 +18581,7 @@ }, "x-appwrite": { "method": "get", - "weight": 334, + "weight": 332, "cookies": false, "type": "", "deprecated": false, @@ -18490,7 +18638,7 @@ }, "x-appwrite": { "method": "retry", - "weight": 339, + "weight": 337, "cookies": false, "type": "", "deprecated": false, @@ -18542,7 +18690,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 340, + "weight": 338, "cookies": false, "type": "", "deprecated": false, @@ -18775,8 +18923,8 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", - "default": false, + "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", + "default": true, "x-example": false } }, @@ -18919,6 +19067,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only projects can read them during build and runtime.", + "default": null, + "x-example": false } }, "required": [ @@ -23828,7 +23982,7 @@ }, "x-appwrite": { "method": "listRules", - "weight": 311, + "weight": 310, "cookies": false, "type": "", "deprecated": false, @@ -23887,7 +24041,7 @@ "tags": [ "proxy" ], - "description": "Create a new proxy rule.", + "description": "", "responses": { "201": { "description": "Rule", @@ -23898,15 +24052,15 @@ }, "x-appwrite": { "method": "createRule", - "weight": 310, + "weight": 420, "cookies": false, "type": "", "deprecated": false, "demo": "proxy\/create-rule.md", - "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/master\/docs\/references\/proxy\/create-rule.md", - "rate-limit": 0, - "rate-time": 3600, - "rate-key": "url:{url},ip:{ip}", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a new proxy rule.", + "rate-limit": 10, + "rate-time": 60, + "rate-key": "userId:{userId}, url:{url}", "scope": "rules.write", "platforms": [ "console" @@ -23987,7 +24141,7 @@ }, "x-appwrite": { "method": "getRule", - "weight": 312, + "weight": 311, "cookies": false, "type": "", "deprecated": false, @@ -24039,7 +24193,7 @@ }, "x-appwrite": { "method": "deleteRule", - "weight": 313, + "weight": 312, "cookies": false, "type": "", "deprecated": false, @@ -24098,7 +24252,7 @@ }, "x-appwrite": { "method": "updateRuleVerification", - "weight": 314, + "weight": 313, "cookies": false, "type": "", "deprecated": false, @@ -24157,7 +24311,7 @@ }, "x-appwrite": { "method": "list", - "weight": 394, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -24228,7 +24382,7 @@ }, "x-appwrite": { "method": "create", - "weight": 392, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -24318,12 +24472,6 @@ "default": "", "x-example": "" }, - "subdomain": { - "type": "string", - "description": "Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.", - "default": "", - "x-example": "" - }, "buildRuntime": { "type": "string", "description": "Runtime to use during build step.", @@ -24438,30 +24586,6 @@ "default": "", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "default": "", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "default": "", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to site code in the template repo.", - "default": "", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "default": "", - "x-example": "" - }, "specification": { "type": "string", "description": "Framework specification for the site and builds.", @@ -24504,7 +24628,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 397, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -24775,7 +24899,7 @@ }, "x-appwrite": { "method": "get", - "weight": 393, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -24833,7 +24957,7 @@ }, "x-appwrite": { "method": "update", - "weight": 395, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -25072,7 +25196,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 396, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -25211,7 +25335,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 398, + "weight": 397, "cookies": false, "type": "upload", "deprecated": false, @@ -25286,6 +25410,111 @@ ] } }, + "\/sites\/{siteId}\/deployments\/template": { + "post": { + "summary": "Create deployment", + "operationId": "sitesCreateTemplateDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 398, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "default": null, + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "default": null, + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to site code in the template repo.", + "default": null, + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the site template.", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + ] + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -26178,8 +26407,8 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", - "default": false, + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "default": true, "x-example": false } }, @@ -26340,6 +26569,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "default": null, + "x-example": false } }, "required": [ diff --git a/app/config/specs/swagger2-latest-server.json b/app/config/specs/swagger2-latest-server.json index 642c4b6a70..5b50b0ec33 100644 --- a/app/config/specs/swagger2-latest-server.json +++ b/app/config/specs/swagger2-latest-server.json @@ -8284,7 +8284,7 @@ }, "x-appwrite": { "method": "list", - "weight": 389, + "weight": 387, "cookies": false, "type": "", "deprecated": false, @@ -8356,7 +8356,7 @@ }, "x-appwrite": { "method": "create", - "weight": 387, + "weight": 385, "cookies": false, "type": "", "deprecated": false, @@ -8565,30 +8565,6 @@ "default": "", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "default": "", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "default": "", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to function code in the template repo.", - "default": "", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the function template.", - "default": "", - "x-example": "" - }, "specification": { "type": "string", "description": "Runtime specification for the function and builds.", @@ -8630,7 +8606,7 @@ }, "x-appwrite": { "method": "listRuntimes", - "weight": 390, + "weight": 388, "cookies": false, "type": "", "deprecated": false, @@ -8792,7 +8768,7 @@ }, "x-appwrite": { "method": "update", - "weight": 388, + "weight": 386, "cookies": false, "type": "", "deprecated": false, @@ -9177,7 +9153,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 391, + "weight": 389, "cookies": false, "type": "upload", "deprecated": false, @@ -9245,6 +9221,112 @@ ] } }, + "\/functions\/{functionId}\/deployments\/template": { + "post": { + "summary": "Create template deployment", + "operationId": "functionsCreateTemplateDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "functions" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 390, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "functions\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/functions#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "functions.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "functionId", + "description": "Function ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "default": null, + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "default": null, + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to function code in the template repo.", + "default": null, + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the function template.", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + ] + } + }, "\/functions\/{functionId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -10004,8 +10086,8 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", - "default": false, + "description": "Secret variables can be updated or deleted, but only functions can read them during build and runtime.", + "default": true, "x-example": false } }, @@ -10042,7 +10124,7 @@ }, "x-appwrite": { "method": "query", - "weight": 326, + "weight": 324, "cookies": false, "type": "graphql", "deprecated": false, @@ -10117,7 +10199,7 @@ }, "x-appwrite": { "method": "mutation", - "weight": 325, + "weight": 323, "cookies": false, "type": "graphql", "deprecated": false, @@ -11939,7 +12021,7 @@ }, "x-appwrite": { "method": "listMessages", - "weight": 379, + "weight": 377, "cookies": false, "type": "", "deprecated": false, @@ -12014,7 +12096,7 @@ }, "x-appwrite": { "method": "createEmail", - "weight": 376, + "weight": 374, "cookies": false, "type": "", "deprecated": false, @@ -12172,7 +12254,7 @@ }, "x-appwrite": { "method": "updateEmail", - "weight": 383, + "weight": 381, "cookies": false, "type": "", "deprecated": false, @@ -12327,7 +12409,7 @@ }, "x-appwrite": { "method": "createPush", - "weight": 378, + "weight": 376, "cookies": false, "type": "", "deprecated": false, @@ -12522,7 +12604,7 @@ }, "x-appwrite": { "method": "updatePush", - "weight": 385, + "weight": 383, "cookies": false, "type": "", "deprecated": false, @@ -12716,7 +12798,7 @@ }, "x-appwrite": { "method": "createSms", - "weight": 377, + "weight": 375, "cookies": false, "type": "", "deprecated": false, @@ -12834,7 +12916,7 @@ }, "x-appwrite": { "method": "updateSms", - "weight": 384, + "weight": 382, "cookies": false, "type": "", "deprecated": false, @@ -12950,7 +13032,7 @@ }, "x-appwrite": { "method": "getMessage", - "weight": 382, + "weight": 380, "cookies": false, "type": "", "deprecated": false, @@ -13005,7 +13087,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 386, + "weight": 384, "cookies": false, "type": "", "deprecated": false, @@ -13067,7 +13149,7 @@ }, "x-appwrite": { "method": "listMessageLogs", - "weight": 380, + "weight": 378, "cookies": false, "type": "", "deprecated": false, @@ -13141,7 +13223,7 @@ }, "x-appwrite": { "method": "listTargets", - "weight": 381, + "weight": 379, "cookies": false, "type": "", "deprecated": false, @@ -13215,7 +13297,7 @@ }, "x-appwrite": { "method": "listProviders", - "weight": 351, + "weight": 349, "cookies": false, "type": "", "deprecated": false, @@ -13290,7 +13372,7 @@ }, "x-appwrite": { "method": "createApnsProvider", - "weight": 350, + "weight": 348, "cookies": false, "type": "", "deprecated": false, @@ -13405,7 +13487,7 @@ }, "x-appwrite": { "method": "updateApnsProvider", - "weight": 363, + "weight": 361, "cookies": false, "type": "", "deprecated": false, @@ -13518,7 +13600,7 @@ }, "x-appwrite": { "method": "createFcmProvider", - "weight": 349, + "weight": 347, "cookies": false, "type": "", "deprecated": false, @@ -13609,7 +13691,7 @@ }, "x-appwrite": { "method": "updateFcmProvider", - "weight": 362, + "weight": 360, "cookies": false, "type": "", "deprecated": false, @@ -13698,7 +13780,7 @@ }, "x-appwrite": { "method": "createMailgunProvider", - "weight": 341, + "weight": 339, "cookies": false, "type": "", "deprecated": false, @@ -13825,7 +13907,7 @@ }, "x-appwrite": { "method": "updateMailgunProvider", - "weight": 354, + "weight": 352, "cookies": false, "type": "", "deprecated": false, @@ -13950,7 +14032,7 @@ }, "x-appwrite": { "method": "createMsg91Provider", - "weight": 344, + "weight": 342, "cookies": false, "type": "", "deprecated": false, @@ -14053,7 +14135,7 @@ }, "x-appwrite": { "method": "updateMsg91Provider", - "weight": 357, + "weight": 355, "cookies": false, "type": "", "deprecated": false, @@ -14154,7 +14236,7 @@ }, "x-appwrite": { "method": "createSendgridProvider", - "weight": 342, + "weight": 340, "cookies": false, "type": "", "deprecated": false, @@ -14269,7 +14351,7 @@ }, "x-appwrite": { "method": "updateSendgridProvider", - "weight": 355, + "weight": 353, "cookies": false, "type": "", "deprecated": false, @@ -14382,7 +14464,7 @@ }, "x-appwrite": { "method": "createSmtpProvider", - "weight": 343, + "weight": 341, "cookies": false, "type": "", "deprecated": false, @@ -14541,7 +14623,7 @@ }, "x-appwrite": { "method": "updateSmtpProvider", - "weight": 356, + "weight": 354, "cookies": false, "type": "", "deprecated": false, @@ -14697,7 +14779,7 @@ }, "x-appwrite": { "method": "createTelesignProvider", - "weight": 345, + "weight": 343, "cookies": false, "type": "", "deprecated": false, @@ -14800,7 +14882,7 @@ }, "x-appwrite": { "method": "updateTelesignProvider", - "weight": 358, + "weight": 356, "cookies": false, "type": "", "deprecated": false, @@ -14901,7 +14983,7 @@ }, "x-appwrite": { "method": "createTextmagicProvider", - "weight": 346, + "weight": 344, "cookies": false, "type": "", "deprecated": false, @@ -15004,7 +15086,7 @@ }, "x-appwrite": { "method": "updateTextmagicProvider", - "weight": 359, + "weight": 357, "cookies": false, "type": "", "deprecated": false, @@ -15105,7 +15187,7 @@ }, "x-appwrite": { "method": "createTwilioProvider", - "weight": 347, + "weight": 345, "cookies": false, "type": "", "deprecated": false, @@ -15208,7 +15290,7 @@ }, "x-appwrite": { "method": "updateTwilioProvider", - "weight": 360, + "weight": 358, "cookies": false, "type": "", "deprecated": false, @@ -15309,7 +15391,7 @@ }, "x-appwrite": { "method": "createVonageProvider", - "weight": 348, + "weight": 346, "cookies": false, "type": "", "deprecated": false, @@ -15412,7 +15494,7 @@ }, "x-appwrite": { "method": "updateVonageProvider", - "weight": 361, + "weight": 359, "cookies": false, "type": "", "deprecated": false, @@ -15513,7 +15595,7 @@ }, "x-appwrite": { "method": "getProvider", - "weight": 353, + "weight": 351, "cookies": false, "type": "", "deprecated": false, @@ -15568,7 +15650,7 @@ }, "x-appwrite": { "method": "deleteProvider", - "weight": 364, + "weight": 362, "cookies": false, "type": "", "deprecated": false, @@ -15630,7 +15712,7 @@ }, "x-appwrite": { "method": "listProviderLogs", - "weight": 352, + "weight": 350, "cookies": false, "type": "", "deprecated": false, @@ -15704,7 +15786,7 @@ }, "x-appwrite": { "method": "listSubscriberLogs", - "weight": 373, + "weight": 371, "cookies": false, "type": "", "deprecated": false, @@ -15778,7 +15860,7 @@ }, "x-appwrite": { "method": "listTopics", - "weight": 366, + "weight": 364, "cookies": false, "type": "", "deprecated": false, @@ -15851,7 +15933,7 @@ }, "x-appwrite": { "method": "createTopic", - "weight": 365, + "weight": 363, "cookies": false, "type": "", "deprecated": false, @@ -15941,7 +16023,7 @@ }, "x-appwrite": { "method": "getTopic", - "weight": 368, + "weight": 366, "cookies": false, "type": "", "deprecated": false, @@ -16001,7 +16083,7 @@ }, "x-appwrite": { "method": "updateTopic", - "weight": 369, + "weight": 367, "cookies": false, "type": "", "deprecated": false, @@ -16080,7 +16162,7 @@ }, "x-appwrite": { "method": "deleteTopic", - "weight": 370, + "weight": 368, "cookies": false, "type": "", "deprecated": false, @@ -16142,7 +16224,7 @@ }, "x-appwrite": { "method": "listTopicLogs", - "weight": 367, + "weight": 365, "cookies": false, "type": "", "deprecated": false, @@ -16216,7 +16298,7 @@ }, "x-appwrite": { "method": "listSubscribers", - "weight": 372, + "weight": 370, "cookies": false, "type": "", "deprecated": false, @@ -16297,7 +16379,7 @@ }, "x-appwrite": { "method": "createSubscriber", - "weight": 371, + "weight": 369, "cookies": false, "type": "", "deprecated": false, @@ -16388,7 +16470,7 @@ }, "x-appwrite": { "method": "getSubscriber", - "weight": 374, + "weight": 372, "cookies": false, "type": "", "deprecated": false, @@ -16451,7 +16533,7 @@ }, "x-appwrite": { "method": "deleteSubscriber", - "weight": 375, + "weight": 373, "cookies": false, "type": "", "deprecated": false, @@ -16525,7 +16607,7 @@ }, "x-appwrite": { "method": "list", - "weight": 394, + "weight": 393, "cookies": false, "type": "", "deprecated": false, @@ -16597,7 +16679,7 @@ }, "x-appwrite": { "method": "create", - "weight": 392, + "weight": 391, "cookies": false, "type": "", "deprecated": false, @@ -16688,12 +16770,6 @@ "default": "", "x-example": "" }, - "subdomain": { - "type": "string", - "description": "Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can't start with a special char. Max length is 36 chars.", - "default": "", - "x-example": "" - }, "buildRuntime": { "type": "string", "description": "Runtime to use during build step.", @@ -16808,30 +16884,6 @@ "default": "", "x-example": "" }, - "templateRepository": { - "type": "string", - "description": "Repository name of the template.", - "default": "", - "x-example": "" - }, - "templateOwner": { - "type": "string", - "description": "The name of the owner of the template.", - "default": "", - "x-example": "" - }, - "templateRootDirectory": { - "type": "string", - "description": "Path to site code in the template repo.", - "default": "", - "x-example": "" - }, - "templateVersion": { - "type": "string", - "description": "Version (tag) for the repo linked to the site template.", - "default": "", - "x-example": "" - }, "specification": { "type": "string", "description": "Framework specification for the site and builds.", @@ -16874,7 +16926,7 @@ }, "x-appwrite": { "method": "listFrameworks", - "weight": 397, + "weight": 396, "cookies": false, "type": "", "deprecated": false, @@ -16925,7 +16977,7 @@ }, "x-appwrite": { "method": "get", - "weight": 393, + "weight": 392, "cookies": false, "type": "", "deprecated": false, @@ -16984,7 +17036,7 @@ }, "x-appwrite": { "method": "update", - "weight": 395, + "weight": 394, "cookies": false, "type": "", "deprecated": false, @@ -17224,7 +17276,7 @@ }, "x-appwrite": { "method": "delete", - "weight": 396, + "weight": 395, "cookies": false, "type": "", "deprecated": false, @@ -17365,7 +17417,7 @@ }, "x-appwrite": { "method": "createDeployment", - "weight": 398, + "weight": 397, "cookies": false, "type": "upload", "deprecated": false, @@ -17441,6 +17493,112 @@ ] } }, + "\/sites\/{siteId}\/deployments\/template": { + "post": { + "summary": "Create deployment", + "operationId": "sitesCreateTemplateDeployment", + "consumes": [ + "application\/json" + ], + "produces": [ + "application\/json" + ], + "tags": [ + "sites" + ], + "description": "", + "responses": { + "202": { + "description": "Deployment", + "schema": { + "$ref": "#\/definitions\/deployment" + } + } + }, + "x-appwrite": { + "method": "createTemplateDeployment", + "weight": 398, + "cookies": false, + "type": "", + "deprecated": false, + "demo": "sites\/create-template-deployment.md", + "edit": "https:\/\/github.com\/appwrite\/appwrite\/edit\/masterCreate a deployment based on a template.\n\nUse this endpoint with combination of [listTemplates](https:\/\/appwrite.io\/docs\/server\/sites#listTemplates) to find the template details.", + "rate-limit": 0, + "rate-time": 3600, + "rate-key": "url:{url},ip:{ip}", + "scope": "sites.write", + "platforms": [ + "server" + ], + "packaging": false, + "auth": { + "Project": [], + "Key": [] + } + }, + "security": [ + { + "Project": [], + "Key": [] + } + ], + "parameters": [ + { + "name": "siteId", + "description": "Site ID.", + "required": true, + "type": "string", + "x-example": "", + "in": "path" + }, + { + "name": "payload", + "in": "body", + "schema": { + "type": "object", + "properties": { + "repository": { + "type": "string", + "description": "Repository name of the template.", + "default": null, + "x-example": "" + }, + "owner": { + "type": "string", + "description": "The name of the owner of the template.", + "default": null, + "x-example": "" + }, + "rootDirectory": { + "type": "string", + "description": "Path to site code in the template repo.", + "default": null, + "x-example": "" + }, + "version": { + "type": "string", + "description": "Version (tag) for the repo linked to the site template.", + "default": null, + "x-example": "" + }, + "activate": { + "type": "boolean", + "description": "Automatically activate the deployment when it is finished building.", + "default": false, + "x-example": false + } + }, + "required": [ + "repository", + "owner", + "rootDirectory", + "version" + ] + } + } + ] + } + }, "\/sites\/{siteId}\/deployments\/{deploymentId}": { "get": { "summary": "Get deployment", @@ -18266,8 +18424,8 @@ }, "secret": { "type": "boolean", - "description": "Is secret? Secret variables can only be updated or deleted, they cannot be read.", - "default": false, + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "default": true, "x-example": false } }, @@ -18430,6 +18588,12 @@ "description": "Variable value. Max length: 8192 chars.", "default": null, "x-example": "" + }, + "secret": { + "type": "boolean", + "description": "Secret variables can be updated or deleted, but only sites can read them during build and runtime.", + "default": null, + "x-example": false } }, "required": [ diff --git a/app/controllers/api/proxy.php b/app/controllers/api/proxy.php index e691077adf..e7aa3acb6c 100644 --- a/app/controllers/api/proxy.php +++ b/app/controllers/api/proxy.php @@ -15,176 +15,13 @@ use Utopia\App; use Utopia\Database\Database; use Utopia\Database\Document; use Utopia\Database\Exception\Query as QueryException; -use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Query\Cursor; use Utopia\Database\Validator\UID; use Utopia\Domains\Domain; use Utopia\Logger\Log; use Utopia\System\System; -use Utopia\Validator\Domain as ValidatorDomain; use Utopia\Validator\Text; -use Utopia\Validator\WhiteList; - -App::post('/v1/proxy/rules') - ->groups(['api', 'proxy']) - ->desc('Create rule') - ->label('scope', 'rules.write') - ->label('event', 'rules.[ruleId].create') - ->label('audits.event', 'rule.create') - ->label('audits.resource', 'rule/{response.$id}') - ->label('sdk', new Method( - namespace: 'proxy', - name: 'createRule', - description: '/docs/references/proxy/create-rule.md', - auth: [AuthType::ADMIN], - responses: [ - new SDKResponse( - code: Response::STATUS_CODE_CREATED, - model: Response::MODEL_PROXY_RULE, - ) - ] - )) - ->param('domain', null, new ValidatorDomain(), 'Domain name.') - ->param('resourceType', null, new WhiteList(['api', 'function', 'site']), 'Action definition for the rule. Possible values are "api", "function" and "site"') - ->param('resourceId', '', new UID(), 'ID of resource for the action type. If resourceType is "api", leave empty. If resourceType is "function", provide ID of the function.', true) - ->inject('response') - ->inject('project') - ->inject('queueForCertificates') - ->inject('queueForEvents') - ->inject('dbForPlatform') - ->inject('dbForProject') - ->action(function (string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) { - $mainDomain = System::getEnv('_APP_DOMAIN', ''); - if ($domain === $mainDomain) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your main domain to specific resource. Please use subdomain or a different domain.'); - } - - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - - if ( - ($functionsDomain !== '' && str_ends_with($domain, $functionsDomain)) || - ($sitesDomain !== '' && str_ends_with($domain, $sitesDomain)) - ) { - // TODO: Refactor later - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'You cannot assign your functions or sites domain or their subdomains to a specific resource. Please use a different domain.'); - } - - if ($domain === 'localhost' || $domain === APP_HOSTNAME_INTERNAL) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.'); - } - - // TODO: @christyjacob remove once we migrate the rules in 1.7.x - if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { - $document = $dbForPlatform->getDocument('rules', md5($domain)); - } else { - $document = $dbForPlatform->findOne('rules', [ - Query::equal('domain', [$domain]), - ]); - } - - - if (!$document->isEmpty()) { - if ($document->getAttribute('projectId') === $project->getId()) { - $resourceType = $document->getAttribute('resourceType'); - $resourceId = $document->getAttribute('resourceId'); - $message = "Domain already assigned to '{$resourceType}' service"; - if (!empty($resourceId)) { - $message .= " with ID '{$resourceId}'"; - } - - $message .= '.'; - } else { - $message = 'Domain already assigned to different project.'; - } - - throw new Exception(Exception::RULE_ALREADY_EXISTS, $message); - } - - $resourceInternalId = ''; - - switch ($resourceType) { - case 'function': - if (empty($resourceId)) { - throw new Exception(Exception::FUNCTION_NOT_FOUND); - } - - $function = $dbForProject->getDocument('functions', $resourceId); - - if ($function->isEmpty()) { - throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); - } - - $resourceInternalId = $function->getInternalId(); - break; - case 'site': - if (empty($resourceId)) { - throw new Exception(Exception::SITE_NOT_FOUND); - } - - $site = $dbForProject->getDocument('sites', $resourceId); - - if ($site->isEmpty()) { - throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); - } - - $resourceInternalId = $site->getInternalId(); - break; - } - - try { - $domain = new Domain($domain); - } catch (\Throwable) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.'); - } - - // TODO: @christyjacob remove once we migrate the rules in 1.7.x - $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique(); - - $rule = new Document([ - '$id' => $ruleId, - 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), - 'domain' => $domain->get(), - 'resourceType' => $resourceType, - 'resourceId' => $resourceId, - 'resourceInternalId' => $resourceInternalId, - 'certificateId' => '', - ]); - - $status = 'created'; - - if (\str_ends_with($domain->get(), $functionsDomain) || \str_ends_with($domain->get(), $sitesDomain)) { - $status = 'verified'; - } - - if ($status === 'created') { - $target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', '')); - $validator = new CNAME($target->get()); // Verify Domain with DNS records - - if ($validator->isValid($domain->get())) { - $status = 'verifying'; - - $queueForCertificates - ->setDomain(new Document([ - 'domain' => $rule->getAttribute('domain') - ])) - ->trigger(); - } - } - - $rule->setAttribute('status', $status); - $rule = $dbForPlatform->createDocument('rules', $rule); - - $queueForEvents->setParam('ruleId', $rule->getId()); - - $rule->setAttribute('logs', ''); - - $response - ->setStatusCode(Response::STATUS_CODE_CREATED) - ->dynamic($rule, Response::MODEL_PROXY_RULE); - }); App::get('/v1/proxy/rules') ->groups(['api', 'proxy']) @@ -411,34 +248,3 @@ App::patch('/v1/proxy/rules/:ruleId/verification') $response->dynamic($rule, Response::MODEL_PROXY_RULE); }); - -App::get('/v1/proxy/subdomains') - ->desc('Check if subdomain is available') - ->groups(['api', 'proxy']) - ->label('scope', 'rules.read') - ->label('sdk.auth', [APP_AUTH_TYPE_ADMIN]) - ->label('sdk.namespace', 'proxy') - ->label('sdk.method', 'checkSubdomain') - ->label('sdk.description', '/docs/references/proxy/check-subdomain.md') - ->label('sdk.response.code', Response::STATUS_CODE_OK) - ->label('sdk.response.type', Response::CONTENT_TYPE_JSON) - ->label('sdk.response.model', Response::MODEL_NONE) - ->param('resourceType', null, new WhiteList(['function', 'site']), 'Action definition for the rule. Possible values are "function" and "site"') - ->param('subdomain', '', new Text(256), 'Subdomain name.') - ->inject('response') - ->inject('dbForPlatform') - ->action(function (string $resourceType, string $subdomain, Response $response, Database $dbForPlatform) { - //TODO: Add tests for this endpoint - $resourceDomain = $resourceType === 'site' ? System::getEnv('_APP_DOMAIN_SITES', '') : System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - $domain = $subdomain . '.' . $resourceDomain; - - $document = $dbForPlatform->findOne('rules', [ - Query::equal('domain', [$domain]), - ]); - - if ($document && !$document->isEmpty()) { - throw new Exception(Exception::RULE_ALREADY_EXISTS, 'Subdomain already assigned to different project.'); - } - - $response->noContent(); - }); diff --git a/app/controllers/general.php b/app/controllers/general.php index c9efc90426..a1a5dac3d1 100644 --- a/app/controllers/general.php +++ b/app/controllers/general.php @@ -737,9 +737,19 @@ App::init() $validator = new Hostname($clients); if ($validator->isValid($origin)) { $refDomainOrigin = $origin; - } else { + } elseif (!empty($origin)) { // Auto-allow domains with linked rule - $rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin))); + if (System::getEnv('_APP_RULES_FORMAT') === 'md5') { + $rule = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', md5($origin))); + } else { + $rule = Authorization::skip( + fn () => $dbForPlatform->find('rules', [ + Query::equal('domain', [$origin]), + Query::limit(1) + ]) + )[0] ?? new Document(); + } + if (!$rule->isEmpty() && $rule->getAttribute('projectInternalId') === $project->getInternalId()) { $refDomainOrigin = $origin; } diff --git a/docs/references/proxy/create-rule.md b/docs/references/proxy/create-rule.md deleted file mode 100644 index be567b1cc0..0000000000 --- a/docs/references/proxy/create-rule.md +++ /dev/null @@ -1 +0,0 @@ -Create a new proxy rule. \ No newline at end of file diff --git a/src/Appwrite/Platform/Appwrite.php b/src/Appwrite/Platform/Appwrite.php index 8633da6cb6..be6ddbae8a 100644 --- a/src/Appwrite/Platform/Appwrite.php +++ b/src/Appwrite/Platform/Appwrite.php @@ -5,6 +5,7 @@ namespace Appwrite\Platform; use Appwrite\Platform\Modules\Console; use Appwrite\Platform\Modules\Core; use Appwrite\Platform\Modules\Functions; +use Appwrite\Platform\Modules\Proxy; use Appwrite\Platform\Modules\Sites; use Utopia\Platform\Platform; @@ -16,5 +17,6 @@ class Appwrite extends Platform $this->addModule(new Functions\Module()); $this->addModule(new Sites\Module()); $this->addModule(new Console\Module()); + $this->addModule(new Proxy\Module()); } } diff --git a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php index 0bf08ea412..0507c68068 100644 --- a/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php +++ b/src/Appwrite/Platform/Modules/Functions/Http/Functions/Create.php @@ -6,7 +6,6 @@ use Appwrite\Event\Event; use Appwrite\Event\Validator\FunctionEvent; use Appwrite\Extend\Exception; use Appwrite\Functions\Validator\RuntimeSpecification; -use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -14,7 +13,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Task\Validator\Cron; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model\Rule; use Utopia\Abuse\Abuse; use Utopia\App; use Utopia\Config\Config; @@ -229,72 +227,6 @@ class Create extends Base $function = $dbForProject->updateDocument('functions', $function->getId(), $function); - $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); - if (!empty($functionsDomain)) { - $routeSubdomain = ID::unique(); - $domain = "{$routeSubdomain}.{$functionsDomain}"; - $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain) : ID::unique(); - - $rule = Authorization::skip( - fn () => $dbForPlatform->createDocument('rules', new Document([ - '$id' => $ruleId, - 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), - 'domain' => $domain, - 'resourceType' => 'function', - 'resourceId' => $function->getId(), - 'resourceInternalId' => $function->getInternalId(), - 'status' => 'verified', - 'certificateId' => '', - ])) - ); - - /** Trigger Webhook */ - $ruleModel = new Rule(); - $ruleCreate = - $queueForEvents - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setQueue(Event::WEBHOOK_QUEUE_NAME); - - $ruleCreate - ->setProject($project) - ->setEvent('rules.[ruleId].create') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) - ->trigger(); - - /** Trigger Functions */ - $ruleCreate - ->setClass(Event::FUNCTIONS_CLASS_NAME) - ->setQueue(Event::FUNCTIONS_QUEUE_NAME) - ->trigger(); - - /** Trigger realtime event */ - $allEvents = Event::generateEvents('rules.[ruleId].create', [ - 'ruleId' => $rule->getId(), - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $rule, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - } - $queueForEvents->setParam('functionId', $function->getId()); $response diff --git a/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php new file mode 100644 index 0000000000..b989310a3d --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Http/Rules/Create.php @@ -0,0 +1,179 @@ +setHttpMethod(Action::HTTP_REQUEST_METHOD_POST) + ->setHttpPath('/v1/proxy/rules') + ->groups(['api', 'proxy']) + ->desc('Create rule') + ->label('scope', 'rules.write') + ->label('event', 'rules.[ruleId].create') + ->label('audits.event', 'rule.create') + ->label('audits.resource', 'rule/{response.$id}') + ->label('sdk', new Method( + namespace: 'proxy', + name: 'createRule', + description: <<label('abuse-limit', 10) + ->label('abuse-key', 'userId:{userId}, url:{url}') + ->label('abuse-time', 60) + ->param('domain', null, new ValidatorDomain(), 'Domain name.') + ->param('resourceType', null, new WhiteList(['api', 'function', 'site']), 'Action definition for the rule. Possible values are "api", "function" and "site"') + ->param('resourceId', '', new UID(), 'ID of resource for the action type. If resourceType is "api", leave empty. If resourceType is "function", provide ID of the function.', true) + ->inject('response') + ->inject('project') + ->inject('queueForCertificates') + ->inject('queueForEvents') + ->inject('dbForPlatform') + ->inject('dbForProject') + ->callback([$this, 'action']); + } + + public function action(string $domain, string $resourceType, string $resourceId, Response $response, Document $project, Certificate $queueForCertificates, Event $queueForEvents, Database $dbForPlatform, Database $dbForProject) + { + $mainDomain = System::getEnv('_APP_DOMAIN', ''); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + + $deniedDomains = [ + $mainDomain, + $sitesDomain, + $functionsDomain, + 'localhost', + APP_HOSTNAME_INTERNAL, + ]; + + if (in_array($domain, $deniedDomains, true)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'This domain name is not allowed. Please pick another one.'); + } + + $resourceInternalId = ''; + + switch ($resourceType) { + case 'function': + case 'site': + if (empty($resourceId)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'resourceId cannot be empty for resourceType "' . $resourceType . '".'); + } + + $expectedDomain = ($resourceType === 'function') ? $functionsDomain : $sitesDomain; + if (!\str_ends_with($domain, $expectedDomain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain must end with ' . $expectedDomain . ' for resourceType "' . $resourceType . '".'); + } + + $collection = ($resourceType === 'function') ? 'functions' : 'sites'; + $document = $dbForProject->getDocument($collection, $resourceId); + + if ($document->isEmpty()) { + throw new Exception(Exception::RULE_RESOURCE_NOT_FOUND); + } + + $resourceInternalId = $document->getInternalId(); + break; + case 'api': + if (\str_ends_with($domain, $functionsDomain) || \str_ends_with($domain, $sitesDomain)) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain must not end with ' . $functionsDomain . ' or ' . $sitesDomain . ' for resourceType "api".'); + } + break; + } + + try { + $domain = new Domain($domain); + } catch (\Throwable) { + throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Domain may not start with http:// or https://.'); + } + + // TODO: @christyjacob remove once we migrate the rules in 1.7.x + $ruleId = System::getEnv('_APP_RULES_FORMAT') === 'md5' ? md5($domain->get()) : ID::unique(); + + try { + $rule = new Document([ + '$id' => $ruleId, + 'projectId' => $project->getId(), + 'projectInternalId' => $project->getInternalId(), + 'domain' => $domain->get(), + 'resourceType' => $resourceType, + 'resourceId' => $resourceId, + 'resourceInternalId' => $resourceInternalId, + 'certificateId' => '', + ]); + } catch (\Throwable $e) { + if ($e->getCode() === Exception::DOCUMENT_ALREADY_EXISTS) { + throw new Exception(Exception::RULE_ALREADY_EXISTS); + } + + throw new Exception(Exception::GENERAL_SERVER_ERROR, 'An unexpected error occurred: ' . $e->getMessage()); + } + + $status = 'created'; + + if (\str_ends_with($domain->get(), $functionsDomain) || \str_ends_with($domain->get(), $sitesDomain)) { + $status = 'verified'; + } + + if ($status === 'created') { + $target = new Domain(System::getEnv('_APP_DOMAIN_TARGET', '')); + $validator = new CNAME($target->get()); // Verify Domain with DNS records + + if ($validator->isValid($domain->get())) { + $status = 'verifying'; + + $queueForCertificates + ->setDomain(new Document([ + 'domain' => $rule->getAttribute('domain') + ])) + ->trigger(); + } + } + + $rule->setAttribute('status', $status); + $rule = $dbForPlatform->createDocument('rules', $rule); + + $queueForEvents->setParam('ruleId', $rule->getId()); + + $response + ->setStatusCode(Response::STATUS_CODE_CREATED) + ->dynamic($rule, Response::MODEL_PROXY_RULE); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Module.php b/src/Appwrite/Platform/Modules/Proxy/Module.php new file mode 100644 index 0000000000..cd8f6f86dc --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Module.php @@ -0,0 +1,14 @@ +addService('http', new Http()); + } +} diff --git a/src/Appwrite/Platform/Modules/Proxy/Services/Http.php b/src/Appwrite/Platform/Modules/Proxy/Services/Http.php new file mode 100644 index 0000000000..bc564f3714 --- /dev/null +++ b/src/Appwrite/Platform/Modules/Proxy/Services/Http.php @@ -0,0 +1,16 @@ +type = Service::TYPE_HTTP; + // Rules + $this->addAction(CreateRule::getName(), new CreateRule()); + } +} diff --git a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php index 52fa8e47a2..6072489310 100644 --- a/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php +++ b/src/Appwrite/Platform/Modules/Sites/Http/Sites/Create.php @@ -4,7 +4,6 @@ namespace Appwrite\Platform\Modules\Sites\Http\Sites; use Appwrite\Event\Event; use Appwrite\Extend\Exception; -use Appwrite\Messaging\Adapter\Realtime; use Appwrite\Platform\Modules\Compute\Base; use Appwrite\SDK\AuthType; use Appwrite\SDK\Method; @@ -12,7 +11,6 @@ use Appwrite\SDK\Response as SDKResponse; use Appwrite\Sites\Validator\FrameworkSpecification; use Appwrite\Utopia\Database\Validator\CustomId; use Appwrite\Utopia\Response; -use Appwrite\Utopia\Response\Model\Rule; use Utopia\App; use Utopia\Config\Config; use Utopia\Database\Database; @@ -20,7 +18,6 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; -use Utopia\Database\Validator\Authorization; use Utopia\Platform\Action; use Utopia\Platform\Scope\HTTP; use Utopia\System\System; @@ -71,7 +68,6 @@ class Create extends Base ->param('installCommand', '', new Text(8192, 0), 'Install Command.', true) ->param('buildCommand', '', new Text(8192, 0), 'Build Command.', true) ->param('outputDirectory', '', new Text(8192, 0), 'Output Directory for site.', true) - ->param('subdomain', '', new CustomId(), 'Unique custom sub-domain. Valid chars are a-z, A-Z, 0-9, period, hyphen, and underscore. Can\'t start with a special char. Max length is 36 chars.', true) ->param('buildRuntime', '', new WhiteList(array_keys(Config::getParam('runtimes')), true), 'Runtime to use during build step.') ->param('adapter', '', new Text(8192, 0), 'Framework adapter. Allows: static, ssr', true) ->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true) @@ -94,7 +90,7 @@ class Create extends Base ->callback([$this, 'action']); } - public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $subdomain, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForPlatform) + public function action(string $siteId, string $name, string $framework, bool $enabled, int $timeout, string $installCommand, string $buildCommand, string $outputDirectory, string $buildRuntime, string $adapter, string $installationId, ?string $fallbackFile, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $specification, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Database $dbForPlatform) { if (!empty($adapter)) { $configFramework = Config::getParam('frameworks')[$framework] ?? []; @@ -105,21 +101,6 @@ class Create extends Base } } - $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); - $routeSubdomain = ''; - $domain = ''; - - if (!empty($sitesDomain)) { - $routeSubdomain = $subdomain ?: ID::unique(); - $domain = "{$routeSubdomain}.{$sitesDomain}"; - - $subdomain = Authorization::skip(fn () => $dbForPlatform->getDocument('rules', \md5($domain))); - - if ($subdomain && !$subdomain->isEmpty()) { - throw new Exception(Exception::GENERAL_ARGUMENT_INVALID, 'Subdomain already exists. Please choose a different subdomain.'); - } - } - $siteId = ($siteId == 'unique()') ? ID::unique() : $siteId; $allowList = \array_filter(\explode(',', System::getEnv('_APP_SITES_FRAMEWORKS', ''))); @@ -195,67 +176,6 @@ class Create extends Base $site = $dbForProject->updateDocument('sites', $site->getId(), $site); - if (!empty($sitesDomain)) { - $rule = Authorization::skip( - fn () => $dbForPlatform->createDocument('rules', new Document([ - '$id' => \md5($domain), - 'projectId' => $project->getId(), - 'projectInternalId' => $project->getInternalId(), - 'domain' => $domain, - 'resourceType' => 'site', - 'resourceId' => $site->getId(), - 'resourceInternalId' => $site->getInternalId(), - 'status' => 'verified', - 'certificateId' => '', - ])) - ); - - /** Trigger Webhook */ - $ruleModel = new Rule(); - $ruleCreate = - $queueForEvents - ->setClass(Event::WEBHOOK_CLASS_NAME) - ->setQueue(Event::WEBHOOK_QUEUE_NAME); - - $ruleCreate - ->setProject($project) - ->setEvent('rules.[ruleId].create') - ->setParam('ruleId', $rule->getId()) - ->setPayload($rule->getArrayCopy(array_keys($ruleModel->getRules()))) - ->trigger(); - - /** Trigger Sites */ - $ruleCreate - ->setClass(Event::SITES_CLASS_NAME) - ->setQueue(Event::SITES_QUEUE_NAME) - ->trigger(); - - /** Trigger realtime event */ - $allEvents = Event::generateEvents('rules.[ruleId].create', [ - 'ruleId' => $rule->getId(), - ]); - $target = Realtime::fromPayload( - // Pass first, most verbose event pattern - event: $allEvents[0], - payload: $rule, - project: $project - ); - Realtime::send( - projectId: 'console', - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - Realtime::send( - projectId: $project->getId(), - payload: $rule->getArrayCopy(), - events: $allEvents, - channels: $target['channels'], - roles: $target['roles'] - ); - } - $queueForEvents->setParam('siteId', $site->getId()); $response diff --git a/tests/e2e/General/UsageTest.php b/tests/e2e/General/UsageTest.php index 74ae1c00bc..b053e4b9cd 100644 --- a/tests/e2e/General/UsageTest.php +++ b/tests/e2e/General/UsageTest.php @@ -12,10 +12,12 @@ use Tests\E2E\Scopes\ProjectCustom; use Tests\E2E\Scopes\Scope; use Tests\E2E\Scopes\SideServer; use Tests\E2E\Services\Functions\FunctionsBase; +use Utopia\Database\Helpers\ID; use Utopia\Database\Helpers\Permission; use Utopia\Database\Helpers\Role; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\System\System; class UsageTest extends Scope { @@ -1090,22 +1092,25 @@ class UsageTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), + $rule = $this->client->call( + Client::METHOD_POST, + '/proxy/rules', + array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), + [ + 'domain' => 'test-' . ID::unique() . System::getEnv('_APP_DOMAIN_FUNCTIONS'), + 'resourceType' => 'function', + 'resourceId' => $functionId, ], - ]); + ); - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertEquals(1, $rules['body']['total']); - $this->assertCount(1, $rules['body']['rules']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); + $this->assertEquals(201, $rule['headers']['status-code']); + $this->assertNotEmpty($rule['body']['$id']); + $this->assertNotEmpty($rule['body']['domain']); - $domain = $rules['body']['rules'][0]['domain']; + $domain = $rule['body']['domain']; $response = $this->client->call( Client::METHOD_GET, diff --git a/tests/e2e/Services/Functions/FunctionsBase.php b/tests/e2e/Services/Functions/FunctionsBase.php index e99aedc93e..ce5df8b746 100644 --- a/tests/e2e/Services/Functions/FunctionsBase.php +++ b/tests/e2e/Services/Functions/FunctionsBase.php @@ -6,6 +6,9 @@ use Appwrite\Tests\Async; use CURLFile; use Tests\E2E\Client; use Utopia\CLI\Console; +use Utopia\Database\Helpers\ID; +use Utopia\Database\Query; +use Utopia\System\System; trait FunctionsBase { @@ -255,4 +258,47 @@ trait FunctionsBase return $function; } + + protected function setupFunctionDomain(string $functionId, string $subdomain = ''): string + { + $subdomain = $subdomain ? $subdomain : ID::unique(); + $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_FUNCTIONS', ''), + 'resourceType' => 'function', + 'resourceId' => $functionId, + ]); + + $this->assertEquals(201, $rule['headers']['status-code']); + $this->assertNotEmpty($rule['body']['$id']); + $this->assertNotEmpty($rule['body']['domain']); + + $domain = $rule['body']['domain']; + + return $domain; + } + + protected function getFunctionDomain(string $functionId): string + { + $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'queries' => [ + Query::equal('resourceId', [$functionId])->toString(), + Query::equal('resourceType', ['function'])->toString(), + ], + ]); + + $this->assertEquals(200, $rules['headers']['status-code']); + $this->assertGreaterThanOrEqual(1, $rules['body']['total']); + $this->assertGreaterThanOrEqual(1, \count($rules['body']['rules'])); + $this->assertNotEmpty($rules['body']['rules'][0]['domain']); + + $domain = $rules['body']['rules'][0]['domain']; + + return $domain; + } } diff --git a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php index 8975a1a5dc..f941c0658a 100644 --- a/tests/e2e/Services/Functions/FunctionsCustomServerTest.php +++ b/tests/e2e/Services/Functions/FunctionsCustomServerTest.php @@ -1654,22 +1654,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), - ], - ]); - - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertEquals(1, $rules['body']['total']); - $this->assertCount(1, $rules['body']['rules']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); - - $domain = $rules['body']['rules'][0]['domain']; + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1730,22 +1715,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), - ], - ]); - - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertEquals(1, $rules['body']['total']); - $this->assertCount(1, $rules['body']['rules']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); - - $domain = $rules['body']['rules'][0]['domain']; + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1780,22 +1750,7 @@ class FunctionsCustomServerTest extends Scope 'execute' => ['any'] ]); - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), - ], - ]); - - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertEquals(1, $rules['body']['total']); - $this->assertCount(1, $rules['body']['rules']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); - - $domain = $rules['body']['rules'][0]['domain']; + $domain = $this->setupFunctionDomain($functionId); $this->setupDeployment($functionId, [ 'entrypoint' => 'index.php', @@ -1915,6 +1870,8 @@ class FunctionsCustomServerTest extends Scope $functionId = $function['body']['$id'] ?? ''; + $domain = $this->setupFunctionDomain($functionId); + $this->setupDeployment($functionId, [ 'code' => $this->packageFunction('node'), 'activate' => true @@ -1949,20 +1906,7 @@ class FunctionsCustomServerTest extends Scope }, 10000, 500); // Domain Executions test - $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ - 'content-type' => 'application/json', - 'x-appwrite-project' => $this->getProject()['$id'], - ], $this->getHeaders()), [ - 'queries' => [ - Query::equal('resourceId', [$functionId])->toString(), - Query::equal('resourceType', ['function'])->toString(), - ], - ]); - - $this->assertEquals(200, $rules['headers']['status-code']); - $this->assertNotEmpty($rules['body']['rules'][0]['domain']); - - $domain = $rules['body']['rules'][0]['domain']; + $domain = $this->getFunctionDomain($functionId); $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); diff --git a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php index f81290e707..60ae7e0bbb 100644 --- a/tests/e2e/Services/Projects/ProjectsCustomServerTest.php +++ b/tests/e2e/Services/Projects/ProjectsCustomServerTest.php @@ -31,11 +31,25 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(201, $response['headers']['status-code']); + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => 'abc.test.io', + ]); + + $this->assertEquals(201, $response['headers']['status-code']); + + // duplicate rule + $response2 = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => 'abc.test.io', + ]); + + $this->assertEquals(409, $response2['headers']['status-code']); + $response = $this->client->call(Client::METHOD_DELETE, '/proxy/rules/' . $response['body']['$id'], $headers); $this->assertEquals(204, $response['headers']['status-code']); - // prevent functions domain $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ @@ -45,7 +59,7 @@ class ProjectsCustomServerTest extends Scope $this->assertEquals(400, $response['headers']['status-code']); - // prevent sites domain + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ @@ -54,5 +68,42 @@ class ProjectsCustomServerTest extends Scope ]); $this->assertEquals(400, $response['headers']['status-code']); + + // prevent functions domain + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'function', + 'domain' => $functionsDomain, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + // prevent sites domain + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'site', + 'domain' => $sitesDomain, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + + $mainDomain = System::getEnv('_APP_DOMAIN', ''); + $sitesDomain = System::getEnv('_APP_DOMAIN_SITES', ''); + $functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', ''); + + $deniedDomains = [ + $mainDomain, + $sitesDomain, + $functionsDomain, + 'localhost', + APP_HOSTNAME_INTERNAL, + ]; + + foreach ($deniedDomains as $deniedDomain) { + $response = $this->client->call(Client::METHOD_POST, '/proxy/rules', $headers, [ + 'resourceType' => 'api', + 'domain' => $deniedDomain, + ]); + + $this->assertEquals(400, $response['headers']['status-code']); + } } } diff --git a/tests/e2e/Services/Sites/SitesBase.php b/tests/e2e/Services/Sites/SitesBase.php index bc7b6d2722..4fcd34572d 100644 --- a/tests/e2e/Services/Sites/SitesBase.php +++ b/tests/e2e/Services/Sites/SitesBase.php @@ -6,7 +6,9 @@ use Appwrite\Tests\Async; use CURLFile; use Tests\E2E\Client; use Utopia\CLI\Console; +use Utopia\Database\Helpers\ID; use Utopia\Database\Query; +use Utopia\System\System; trait SitesBase { @@ -267,6 +269,27 @@ trait SitesBase return $site; } + protected function setupSiteDomain(string $siteId, string $subdomain = ''): string + { + $subdomain = $subdomain ? $subdomain : ID::unique(); + $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'resourceType' => 'site', + 'resourceId' => $siteId, + ]); + + $this->assertEquals(201, $rule['headers']['status-code']); + $this->assertNotEmpty($rule['body']['$id']); + $this->assertNotEmpty($rule['body']['domain']); + + $domain = $rule['body']['domain']; + + return $domain; + } + protected function getSiteDomain(string $siteId): string { $rules = $this->client->call(Client::METHOD_GET, '/proxy/rules', array_merge([ diff --git a/tests/e2e/Services/Sites/SitesCustomServerTest.php b/tests/e2e/Services/Sites/SitesCustomServerTest.php index b01d31ada7..eefa0ddcbb 100644 --- a/tests/e2e/Services/Sites/SitesCustomServerTest.php +++ b/tests/e2e/Services/Sites/SitesCustomServerTest.php @@ -11,6 +11,7 @@ use Utopia\Database\Document; use Utopia\Database\Helpers\ID; use Utopia\Database\Query; use Utopia\Database\Validator\Datetime as DatetimeValidator; +use Utopia\System\System; class SitesCustomServerTest extends Scope { @@ -73,13 +74,12 @@ class SitesCustomServerTest extends Scope 'framework' => 'other', 'buildRuntime' => 'ssr-22', 'outputDirectory' => './', - 'subdomain' => 'test-site', 'fallbackFile' => null, ]); $this->assertNotEmpty($siteId); - $rule = $this->getSiteDomain($siteId); + $rule = $this->setupSiteDomain($siteId); $response = $this->client->call(Client::METHOD_GET, '/console/resources', [ 'origin' => 'http://localhost', @@ -289,6 +289,8 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); + $domain = $this->setupSiteDomain($siteId); + $secretVariable = $this->createVariable($siteId, [ 'key' => 'name', 'value' => 'Appwrite', @@ -1243,7 +1245,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($site['body']['deploymentId']); }, 50000, 500); - $domain = $this->getSiteDomain($siteId); + $domain = $this->setupSiteDomain($siteId); $proxyClient = new Client(); $proxyClient->setEndpoint('http://' . $domain); @@ -1270,8 +1272,6 @@ class SitesCustomServerTest extends Scope public function testSiteDomainReclaiming(): void { - $subdomain = 'startup' . \uniqid(); - $siteId = $this->setupSite([ 'siteId' => ID::unique(), 'name' => 'Startup site', @@ -1282,11 +1282,13 @@ class SitesCustomServerTest extends Scope 'buildCommand' => '', 'installCommand' => '', 'fallbackFile' => '', - 'subdomain' => $subdomain ]); $this->assertNotEmpty($siteId); + $subdomain = 'startup' . \uniqid(); + $domain = $this->setupSiteDomain($siteId, $subdomain); + $deploymentId = $this->setupDeployment($siteId, [ 'code' => $this->packageSite('static'), 'activate' => 'true' @@ -1306,7 +1308,7 @@ class SitesCustomServerTest extends Scope $this->assertEquals(200, $response['headers']['status-code']); $this->assertStringNotContainsString("This domain is not connected to any Appwrite resource yet", $response['body']); - $site = $this->createSite([ + $site2 = $this->createSite([ 'siteId' => ID::unique(), 'name' => 'Startup 2 site', 'framework' => 'other', @@ -1316,11 +1318,21 @@ class SitesCustomServerTest extends Scope 'buildCommand' => '', 'installCommand' => '', 'fallbackFile' => '', - 'subdomain' => $subdomain ]); - $this->assertEquals(400, $site['headers']['status-code']); - $this->assertStringContainsString("Subdomain already exists.", $site['body']['message']); + $siteId2 = $site2['body']['$id']; + + $rule = $this->client->call(Client::METHOD_POST, '/proxy/rules', array_merge([ + 'content-type' => 'application/json', + 'x-appwrite-project' => $this->getProject()['$id'], + ], $this->getHeaders()), [ + 'domain' => $subdomain . '.' . System::getEnv('_APP_DOMAIN_SITES', ''), + 'resourceType' => 'site', + 'resourceId' => $siteId2, + ]); + + $this->assertEquals(409, $rule['headers']['status-code']); + $this->assertStringContainsString("Document with the requested ID already exists. Try again with a different ID or use ID.unique() to generate a unique ID.", $rule['body']['message']); $this->cleanupSite($siteId); @@ -1356,10 +1368,16 @@ class SitesCustomServerTest extends Scope 'buildCommand' => '', 'installCommand' => '', 'fallbackFile' => '', - 'subdomain' => $subdomain ]); $this->assertEquals(201, $site['headers']['status-code']); + $this->assertNotEmpty($site['body']['$id']); + + $siteId = $site['body']['$id']; + + $domain = $this->setupSiteDomain($siteId, $subdomain); + + $this->assertNotEmpty($domain); $this->cleanupSite($site['body']['$id']); } @@ -1380,6 +1398,8 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); + $domain = $this->setupSiteDomain($siteId); + $deploymentId = $this->setupDeployment($siteId, [ 'code' => $this->packageSite('static'), 'activate' => 'true' @@ -1443,6 +1463,7 @@ class SitesCustomServerTest extends Scope $this->assertNotEmpty($siteId); + $this->setupSiteDomain($siteId, $subdomain); $domain = $this->getSiteDomain($siteId); $this->assertNotEmpty($domain);