Compare commits

...
Author SHA1 Message Date
Christy Jacob d4652d09fe chore: linter 2024-09-18 17:55:30 +00:00
Christy Jacob 2bb1294add chore: update composer 2024-09-17 22:19:37 +00:00
Christy Jacob 95d3b4449b chore: update composer 2024-09-17 22:18:22 +00:00
Christy Jacob 100eefedf4 Merge branch 'main' of https://github.com/appwrite/appwrite into 1.6.x-no-docs 2024-09-17 22:08:04 +00:00
Jake Barnby cd22fb5b2e Remove docs for PR readability 2024-08-20 20:06:22 +12:00
Eldad A. FuxandGitHub cdef3e0cde Merge pull request #8555 from appwrite/fix-env-vars
Fix: Env vars functiosn test
2024-08-19 07:05:34 -04:00
Matej BačoandGitHub 06f2f96747 Merge branch '1.6.x' into fix-env-vars 2024-08-19 11:02:12 +02:00
Matej Bačo 7c35464f73 Fix tests 2024-08-19 09:00:56 +00:00
Christy JacobandGitHub f53156a57c Merge pull request #8554 from appwrite/fix-env-vars
Fix missing function variables
2024-08-19 12:59:49 +04:00
Matej Bačo ef8075f86b Fix missing function variables 2024-08-19 08:50:30 +00:00
Christy JacobandGitHub 07525350b2 Merge pull request #8547 from appwrite/fix-templateBranch-in-request-filter
Default fallback to  for templateBranch
2024-08-18 18:41:48 +04:00
Khushboo VermaandGitHub 9175e39292 Merge branch '1.6.x' into fix-templateBranch-in-request-filter 2024-08-18 20:03:45 +05:30
Christy Jacob fb55fb1136 chupdate cache buster 2024-08-16 21:15:48 +00:00
Christy Jacob a5ccba7561 chore: update console and specs 2024-08-16 20:58:55 +00:00
Christy JacobandGitHub 94070fdc71 Merge pull request #8542 from appwrite/refactor-changing-function-sdk-types
Refactor changing function sdk types
2024-08-17 00:27:12 +04:00
Binyamin Yawitz f60992e81f chore: matching latest specs 2024-08-16 16:26:20 -04:00
Binyamin Yawitz 2de3803796 Merge branch 'refs/heads/1.6.x' into refactor-changing-function-sdk-types
# Conflicts:
#	app/config/specs/open-api3-latest-client.json
#	app/config/specs/open-api3-latest-console.json
#	app/config/specs/open-api3-latest-server.json
#	app/config/specs/swagger2-latest-client.json
#	app/config/specs/swagger2-latest-console.json
#	app/config/specs/swagger2-latest-server.json
2024-08-16 16:20:30 -04:00
Binyamin Yawitz db359e5892 Merge remote-tracking branch 'origin/refactor-changing-function-sdk-types' into refactor-changing-function-sdk-types 2024-08-16 16:19:59 -04:00
Christy JacobandGitHub 911b152d4f Merge pull request #8485 from appwrite/update-docker-base
chore: update docker base image
2024-08-17 00:16:25 +04:00
Khushboo VermaandGitHub 73819ced1b Merge branch '1.6.x' into fix-templateBranch-in-request-filter 2024-08-16 22:17:30 +05:30
Christy JacobandGitHub b115b6fb67 Merge pull request #8546 from appwrite/fix-seconds-precision-exception
Feat: Add seconds precision to scheduledAt
2024-08-16 20:21:27 +04:00
Khushboo Verma 0960a0cd3c Update tests 2024-08-16 18:01:15 +05:30
Khushboo Verma e36c8786ea Add test for response format 2024-08-16 17:50:19 +05:30
Khushboo Verma f4fb3e3c75 Default fallback to for templateBranch 2024-08-16 17:26:19 +05:30
Matej Bačo d5db9253cd Fix breaking changes in lib 2024-08-16 11:25:01 +00:00
Matej Bačo d17efb0180 Upgrade libs 2024-08-16 11:03:54 +00:00
Matej Bačo 8999c2d3f2 Fix failing tests 2024-08-16 10:17:35 +00:00
Matej Bačo edbef8200e Merge remote-tracking branch 'origin/1.6.x' into fix-seconds-precision-exception 2024-08-16 09:15:10 +00:00
Matej Bačo 894a2bb646 Add seconds precision to scheduledAt 2024-08-16 09:11:13 +00:00
Christy JacobandGitHub a114977100 Merge pull request #8543 from appwrite/fix-anyof-validator-specs
Fix: anyof validator specs
2024-08-16 13:01:05 +04:00
Christy JacobandGitHub 22a3508c24 Merge pull request #8217 from appwrite/feat-extract-team-deletion
Feat Sync team deletion
2024-08-15 23:51:36 +04:00
Matej Bačo 616123868d Revert unwanted change 2024-08-15 14:30:32 +00:00
Matej Bačo 9bac44850e Upgrade lib 2024-08-15 14:22:19 +00:00
Matej Bačo 92c423f744 Fix specs of AnyOf validator 2024-08-15 12:12:15 +00:00
Christy JacobandGitHub e8f40342b1 Merge pull request #8358 from appwrite/fix-16x-qa
fix: Remaning 1.6 bugs
2024-08-15 14:08:40 +04:00
Matej BačoandGitHub c54daae118 Merge branch '1.6.x' into fix-16x-qa 2024-08-15 11:46:54 +02:00
Matej Bačo 56a43c8f74 PR review changes 2024-08-15 09:10:03 +00:00
Matej Bačo 9619d59a13 Update specs 2024-08-15 09:08:00 +00:00
Christy JacobandGitHub 9416f3c12a Merge pull request #8540 from appwrite/fix-reorder-runtimes
fix: reorder runtimes
2024-08-15 13:05:19 +04:00
Matej Bačo bc83b3e5ed Merge remote-tracking branch 'origin/1.6.x' into fix-16x-qa 2024-08-15 09:02:09 +00:00
Christy JacobandGitHub 78af3ab867 Merge pull request #8527 from appwrite/fix-manual-templates
Fix: Manual templates
2024-08-15 12:55:56 +04:00
Matej BačoandGitHub f55ae4f485 Merge branch '1.6.x' into refactor-changing-function-sdk-types 2024-08-14 21:58:45 +02:00
Binyamin Yawitz 77329e79e0 feat: generating the latest specs 2024-08-14 14:34:00 -04:00
Binyamin Yawitz b5f55ac5a5 feat: generating latest specs 2024-08-14 14:17:03 -04:00
Binyamin Yawitz cf237d5130 refactor: changing create execution sdk types 2024-08-14 14:16:46 -04:00
Matej Bačo d6676c2e4d Fix tests 2024-08-14 16:35:24 +00:00
Matej Bačo f45f2f127b Merge remote-tracking branch 'origin/1.6.x' into fix-manual-templates 2024-08-14 16:00:34 +00:00
Matej Bačo 9d8c0d37f6 PR review changes 2024-08-14 16:00:13 +00:00
Matej Bačo e7b9218db8 Fix test 2024-08-14 14:52:00 +00:00
Matej Bačo a7df513d33 Faster test 2024-08-14 14:38:41 +00:00
Matej Bačo dae977acd7 Improve test 2024-08-14 14:38:15 +00:00
Christy JacobandGitHub 0d2fbaca45 Merge pull request #8539 from appwrite/fix-header-for-multipart
Add boundary to multipart header
2024-08-14 18:27:02 +04:00
Matej Bačo 839ed27158 Improve failure test logs 2024-08-14 14:19:08 +00:00
Matej Bačo c91bfebbe7 Flaky test improvement 2024-08-14 13:32:15 +00:00
Matej Bačo a7e68836e9 Make flaky test consistent 2024-08-14 13:32:05 +00:00
loks0n bcafec734d fix: reorder templates 2024-08-14 14:11:14 +01:00
Matej Bačo 71840c1f93 Fix tests 2024-08-14 12:58:28 +00:00
Matej Bačo af9acf9f70 Fix tests 2024-08-14 12:50:25 +00:00
Matej Bačo c8e9f2e913 Add template deployment test 2024-08-14 12:14:07 +00:00
Matej Bačo b1d6ff4bcc Merge branch 'fix-16x-qa' of https://github.com/appwrite/appwrite into fix-16x-qa 2024-08-14 10:26:13 +00:00
Matej BačoandGitHub 468a540d17 Merge branch '1.6.x' into fix-16x-qa 2024-08-14 12:25:57 +02:00
Matej Bačo 365edae7a9 Fix failing test 2024-08-14 10:12:57 +00:00
Matej Bačo 9964631bb7 Apply new terminology for template branch 2024-08-14 10:01:04 +00:00
Khushboo Verma 59aa5fb7ce Add test for boundary 2024-08-14 15:23:07 +05:30
Matej Bačo 72e4534025 Merge remote-tracking branch 'origin/1.6.x' into fix-manual-templates 2024-08-14 09:50:46 +00:00
Khushboo Verma 95e6f148da Merge branch '1.6.x' into fix-header-for-multipart 2024-08-14 15:12:46 +05:30
Khushboo Verma 60e42c2e07 Add boundary to multipart header 2024-08-14 15:09:55 +05:30
Christy JacobandGitHub b523521434 Merge pull request #8537 from appwrite/1.6.x-update-migration-specifications
Update size to specification for variable runtimes
2024-08-14 12:47:07 +04:00
Matej Bačo 3b20e53818 Merge remote-tracking branch 'origin/1.6.x' into fix-16x-qa 2024-08-14 08:06:10 +00:00
Bradley Schofield 79a757297d Update size to specification for variable runtimes 2024-08-14 11:48:40 +09:00
Christy JacobandGitHub c3199210fa Merge pull request #8528 from appwrite/fix-tempaltes-cors
Fix: Templates CORS
2024-08-14 00:53:30 +04:00
Christy JacobandGitHub bf63ab8f18 Merge pull request #8529 from appwrite/sync-1.6-main
Sync 1.6 with main
2024-08-14 00:09:56 +04:00
Christy Jacob 748c298e06 chore: generate specs 2024-08-13 15:59:04 +00:00
Christy Jacob bfa253297b Merge branch 'main' of https://github.com/appwrite/appwrite into sync-1.6-main 2024-08-13 15:36:13 +00:00
Matej Bačo c1e62a4e11 Add api group to templates 2024-08-13 12:59:37 +00:00
Matej Bačo 380dc8a106 Fix manual templates 2024-08-13 10:50:58 +00:00
Matej Bačo 6406cb485f Post-merge sync 2024-08-13 09:06:46 +00:00
Matej Bačo 1bf879f16a Merge remote-tracking branch 'origin/main' into fix-16x-qa 2024-08-13 09:05:53 +00:00
Christy JacobandGitHub 282f5088db Merge pull request #8523 from appwrite/refactor-console-links
Refactor console links
2024-08-13 01:12:31 +04:00
Christy JacobandGitHub 3da1cc69d7 Merge pull request #8484 from appwrite/fix-missing-build-logs
Fix: missing build logs
2024-08-13 00:38:23 +04:00
Binyamin Yawitz 4373e72262 fix: typo 2024-08-12 16:37:49 -04:00
Christy JacobandGitHub 87d6900f48 Merge pull request #8517 from appwrite/fix-deploy-from-git-with-space
Fix deploy from git with space
2024-08-13 00:36:52 +04:00
Binyamin Yawitz fbd55eb17e review: addressing 2024-08-12 16:36:16 -04:00
Binyamin Yawitz aa27a37064 review: addressing 2024-08-12 16:33:00 -04:00
Christy JacobandGitHub 2de6a93b77 Merge pull request #8477 from appwrite/feat-add-multipart-support
Add multipart support
2024-08-13 00:31:45 +04:00
Binyamin Yawitz 0690e05807 chore: Updating console version 2024-08-12 16:27:44 -04:00
Binyamin Yawitz 22deb883c1 refactor: console links 2024-08-12 15:59:42 -04:00
Binyamin Yawitz 5bf9794cb2 chore: pint 2024-08-12 15:00:19 -04:00
Binyamin Yawitz 9b79b14821 fix: github cloning and archiving of functions with space 2024-08-12 14:46:12 -04:00
Khushboo Verma cd5a9c5c84 Update proxy version 2024-08-12 23:41:33 +05:30
Khushboo Verma e4cfcd00fd Updated composer.json 2024-08-12 23:17:22 +05:30
Christy JacobandGitHub ad43e1c419 Merge pull request #8412 from appwrite/feat-schedule-execution-improvements
feat: schedule execution improvements
2024-08-12 19:59:56 +04:00
Christy JacobandGitHub 6d7fe6e28d Merge pull request #8452 from appwrite/fix-buckets-metadata
fix: Increasing buckets metadata
2024-08-12 19:10:36 +04:00
Christy JacobandGitHub 856abeb76b Merge pull request #8466 from appwrite/feat-adding-email
Feat changing emails
2024-08-12 19:01:33 +04:00
Binyamin Yawitz c0770621cf fix: reviews 2024-08-12 07:36:15 -04:00
Christy JacobandGitHub 8671609944 Merge pull request #8497 from appwrite/chore-upgrading-logger
chore: Upgrading logger
2024-08-12 15:23:28 +04:00
Christy JacobandGitHub ec56ef9831 Merge pull request #8500 from appwrite/fix-keep-git
Fix keep git
2024-08-12 15:22:00 +04:00
Christy JacobandGitHub 891e709ba2 Merge pull request #8501 from appwrite/feat-adding-email-templates
Feat adding email templates
2024-08-12 14:35:41 +04:00
Christy JacobandGitHub a3238568ed Merge pull request #8515 from appwrite/chore-allow-more-deployment-attributes
Allow deployment queries on type and size
2024-08-12 14:34:56 +04:00
Khushboo Verma 468a97bcd2 Merge branch '1.6.x' into feat-add-multipart-support 2024-08-12 15:39:46 +05:30
Khushboo Verma 285b48a9e8 Replace regex with ctype function 2024-08-12 15:39:35 +05:30
Khushboo Verma 70c0c7707a Add tests for negative size and invalid-string 2024-08-12 15:27:50 +05:30
Khushboo Verma 1a5b5f15b0 Merge branch '1.6.x' into chore-allow-more-deployment-attributes 2024-08-12 15:21:21 +05:30
Khushboo Verma 6f1655a420 Add tests for querying deployments with size and type 2024-08-12 15:20:24 +05:30
Luke B. SilverandGitHub 4b5d8b4102 Merge branch '1.6.x' into feat-schedule-execution-improvements 2024-08-12 10:42:52 +01:00
Christy JacobandGitHub 4f0a265520 Merge pull request #8503 from appwrite/feat-base-runtimes
feat: base runtimes key
2024-08-12 13:41:54 +04:00
Christy JacobandGitHub 70c933f8d8 Merge pull request #8492 from appwrite/feat-dynamic-keys-build
Feat: Dynamic keys in builds
2024-08-12 13:24:30 +04:00
Khushboo Verma 74615f6d1c Merge branch '1.6.x' into chore-allow-more-deployment-attributes 2024-08-12 14:53:16 +05:30
Christy JacobandGitHub 4abb6bcabf Merge pull request #8493 from appwrite/fix-4xx-executions-status
Fix: 4XX execution status
2024-08-12 13:20:33 +04:00
Khushboo Verma 34fc855c8a Allow deployment queries on type and size 2024-08-12 14:49:16 +05:30
Matej BačoandGitHub 47a0998211 Merge branch '1.6.x' into fix-4xx-executions-status 2024-08-12 09:54:55 +02:00
Matej Bačo 1334cf116b Speed up new test 2024-08-12 07:26:20 +00:00
Matej BačoandGitHub e6664fde7a Merge branch '1.6.x' into feat-dynamic-keys-build 2024-08-12 09:20:30 +02:00
Khushboo Verma d895e62c81 Fixed GraphQL tests 2024-08-11 23:06:05 +05:30
Khushboo Verma f6530fd6a5 Simplified headers validator 2024-08-11 18:32:14 +05:30
Khushboo Verma a8973ab26b Improved header tests 2024-08-11 18:15:10 +05:30
Khushboo Verma 8667e9f821 Make application/json as default 2024-08-11 17:56:02 +05:30
Khushboo Verma c8d04c11eb Added more tests to headers validator 2024-08-11 02:25:03 +05:30
Khushboo Verma 2b006ba7af Merge branch '1.6.x' into feat-add-multipart-support 2024-08-10 22:59:31 +05:30
Steven NguyenandGitHub 4dc2b57502 Merge pull request #8507 from appwrite/feat-update-benchmark-comment
Feat update benchmark comment
2024-08-09 17:16:38 -07:00
Binyamin Yawitz 6f1e3722bb fix: checking another package 2024-08-09 14:44:51 -04:00
Binyamin Yawitz bd30c9c153 fix: checking other package 2024-08-09 14:19:30 -04:00
Binyamin Yawitz 738b08aab5 fix: Updating benchmark comment instead of a new one 2024-08-09 12:43:05 -04:00
Khushboo Verma deeffa2a6b Updated test 2024-08-09 20:38:50 +05:30
Khushboo VermaandGitHub f3e4987f6c Merge branch '1.6.x' into feat-add-multipart-support 2024-08-09 19:38:26 +05:30
Binyamin Yawitz 24137abd42 fix: Adding otp session email customizing 2024-08-09 09:05:35 -04:00
loks0n 0e773fefdf feat: improve test 2024-08-09 13:10:07 +01:00
loks0n e1c8cfc4a1 chore: refactor $key/$id names 2024-08-09 12:59:43 +01:00
loks0n f6dec62c50 feat: add base/key runtime 2024-08-09 12:56:55 +01:00
loks0n 1024c98bb2 Merge branch 'feat-schedule-execution-improvements' of https://github.com/appwrite/appwrite into feat-schedule-execution-improvements 2024-08-09 11:30:16 +01:00
loks0n 464e1e247c test: fix expect 400 2024-08-09 11:30:01 +01:00
Luke B. SilverandGitHub a1df29d5f6 Merge branch '1.6.x' into feat-schedule-execution-improvements 2024-08-09 10:44:38 +01:00
loks0n 30132e7e93 test: fix body check, not possible 2024-08-09 10:43:08 +01:00
Binyamin Yawitz 850052b92f fix: Adding otp session email customizing 2024-08-08 14:30:07 -04:00
Binyamin Yawitz 95fe939136 feat: adding explanation 2024-08-08 13:50:25 -04:00
Binyamin Yawitz 1c6b735a27 feat: disconnecting git only when provider it is an empty string 2024-08-08 13:48:45 -04:00
Christy JacobandGitHub ce438f182c Merge pull request #8496 from appwrite/fix-add-scopes-to-function-template
Add scopes to function template
2024-08-08 18:30:54 +04:00
Khushboo Verma 7cfdf40b47 Fix tests 2024-08-08 19:38:59 +05:30
Binyamin Yawitz 01f27874b2 fix: Addressing review 2024-08-08 10:03:10 -04:00
Binyamin Yawitz 844ec11bd3 Merge branch 'refs/heads/1.6.x' into feat-extract-team-deletion 2024-08-08 09:58:30 -04:00
Binyamin Yawitz 9e81e5fe3c chore: Upgrading logger 2024-08-08 09:53:18 -04:00
Khushboo VermaandGitHub 107c9189b7 Merge branch '1.6.x' into fix-add-scopes-to-function-template 2024-08-08 19:17:10 +05:30
Khushboo Verma caa234f140 Add scopes to function template 2024-08-08 19:15:00 +05:30
Christy JacobandGitHub 07b695fae9 Merge pull request #8459 from appwrite/refactor-session-alerts
refactor: no session alerts for otp and magic-url logins
2024-08-08 17:42:43 +04:00
Luke B. SilverandGitHub f1013a79e8 Merge branch '1.6.x' into feat-schedule-execution-improvements 2024-08-08 13:32:58 +01:00
Khushboo Verma 516d05402f Merge branch 'feat-add-multipart-support' of github.com:appwrite/appwrite into feat-add-multipart-support 2024-08-08 15:43:35 +05:30
Khushboo Verma e6473db96c Update create execution body limit to 10MB 2024-08-08 15:42:58 +05:30
Matej Bačo 47316a250b Edge case for test case 2024-08-08 09:15:53 +00:00
Matej Bačo 90b9d176ba Fix unit test placement 2024-08-08 09:02:49 +00:00
Matej Bačo bc4a250813 Fix bug in response filter 2024-08-08 08:51:38 +00:00
Matej Bačo a9a6f65f8e Merge remote-tracking branch 'origin/1.6.x' into feat-dynamic-keys-build 2024-08-08 08:50:11 +00:00
Matej Bačo f561311d76 Fix failing test 2024-08-08 08:50:01 +00:00
Matej Bačo f8fb98d377 Update response filter 2024-08-08 08:42:38 +00:00
Matej Bačo b20ee17fea Merge remote-tracking branch 'origin/1.6.x' into fix-4xx-executions-status 2024-08-08 08:38:20 +00:00
Matej Bačo 25f5c83594 Fix 4XX execution status 2024-08-08 08:38:15 +00:00
Khushboo VermaandGitHub e297371aa4 Merge branch '1.6.x' into feat-add-multipart-support 2024-08-08 14:07:12 +05:30
Christy JacobandGitHub 5534c4fddb Merge pull request #8490 from appwrite/fix-deployment-method
Fix: Deployment method
2024-08-08 12:35:23 +04:00
Matej Bačo 5d9804f42a Update 1.6 specs 2024-08-08 08:23:43 +00:00
Khushboo Verma 066dc59064 Update error message 2024-08-08 13:49:33 +05:30
Khushboo Verma 41de522d04 Merge branch '1.6.x' into feat-add-multipart-support 2024-08-08 13:47:11 +05:30
Matej Bačo 5b184236c1 Dynamic keys in builds 2024-08-08 09:38:37 +02:00
Matej Bačo 288e718890 Update specs 2024-08-08 09:11:33 +02:00
Matej Bačo 90c7ab5c05 Rename deployment method 2024-08-08 09:07:27 +02:00
Christy JacobandGitHub 1452e168bf Merge pull request #8396 from appwrite/fix-build-size-limits
fix: build size limits
2024-08-08 01:43:44 +04:00
loks0n b87cabfc49 test: fix unused var 2024-08-07 17:49:03 +01:00
loks0n e2ed0b2693 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-schedule-execution-improvements 2024-08-07 17:43:07 +01:00
loks0n f1b40c69cd Merge branch '1.6.x' of https://github.com/appwrite/appwrite into fix-build-size-limits 2024-08-07 17:31:51 +01:00
loks0n da2c2e6f1d chore: update desc 2024-08-07 17:30:56 +01:00
Matej Bačo 870cbbb019 Add commments 2024-08-07 15:13:24 +00:00
Christy JacobandGitHub f20851e8f7 Merge pull request #8483 from appwrite/fix-template-variable
Add value to variables template
2024-08-07 18:05:34 +04:00
Khushboo Verma 2f56c123c9 Merge branch '1.6.x' into feat-add-multipart-support 2024-08-07 18:46:46 +05:30
Khushboo Verma dcf1684c8c Update dynamic response method 2024-08-07 18:45:53 +05:30
Christy Jacob 9ad86b5711 chore: update docker base image 2024-08-07 13:06:28 +00:00
Christy JacobandGitHub 75e6e2e734 Merge pull request #8422 from appwrite/feat-mock-numbers-review
feat: address review comments
2024-08-07 17:05:33 +04:00
Matej Bačo 8e3fbfc00d Fix missing build logs 2024-08-07 10:53:40 +02:00
Khushboo Verma 5eacde84ae Add value to variables template 2024-08-07 00:01:54 +05:30
Khushboo Verma 22853b7b59 Fixed test 2024-08-06 23:49:09 +05:30
Khushboo Verma d4cb90c608 Addressed PR comments 2024-08-06 23:01:09 +05:30
Christy JacobandGitHub a019ada0e7 Merge branch '1.6.x' into feat-mock-numbers-review 2024-08-06 19:32:30 +04:00
Khushboo Verma ccdfc8b1ea Resolve merge conflicts 2024-08-06 14:02:48 +05:30
Khushboo Verma 6691c985dd Add headers validator 2024-08-06 13:49:28 +05:30
Binyamin Yawitz 834fad78e1 refactor: Variables names 2024-08-05 16:20:52 -04:00
Binyamin Yawitz 4ca3ef020f Merge branch 'refs/heads/1.6.x' into refactor-session-alerts
# Conflicts:
#	app/controllers/api/account.php
2024-08-05 16:19:41 -04:00
Binyamin Yawitz 47d1bc44e5 refactor: Variables names 2024-08-05 16:16:59 -04:00
18a519f32d typo
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2024-08-05 15:46:23 -04:00
e12e28c1f1 typo
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2024-08-05 15:46:12 -04:00
Christy JacobandGitHub 21ece879b2 Merge pull request #8473 from appwrite/feat-functions-autorestart
Feat: Functions autorestart
2024-08-05 18:11:31 +04:00
Christy JacobandGitHub ad9586e7c7 Merge pull request #8434 from appwrite/fix-redeployments
Fix: function redeployments
2024-08-05 17:38:44 +04:00
Matej BačoandGitHub 9d271effdb Merge branch '1.6.x' into feat-functions-autorestart 2024-08-05 15:27:49 +02:00
Khushboo Verma 6c6ef78238 Resolve conflict 2024-08-05 18:41:35 +05:30
Khushboo Verma 3190384d24 Add multipart support 2024-08-05 18:38:41 +05:30
Christy JacobandGitHub 6120391eb5 Merge pull request #8399 from appwrite/feat-session-alert-improvements
feat: session alert improvements
2024-08-05 13:44:54 +04:00
Matej Bačo bce06a6178 Supply autorestart to executor 2024-08-03 09:14:19 +00:00
Christy JacobandGitHub 877a6a3f54 Merge pull request #8467 from appwrite/chore-updating-dbip
chore: Upgrading dbip to latest version
2024-08-03 11:02:09 +04:00
Binyamin Yawitz 2aa1511bbf chore: Upgrading dbip to latest version 2024-08-02 10:19:23 -04:00
Binyamin Yawitz 73ecec099d feat: adding team email and changing base to noreplay 2024-08-02 08:59:47 -04:00
Christy JacobandGitHub c095cb6e2e Merge pull request #8465 from appwrite/chores-upgrade-sdks
Chode: Upgrade SDK version
2024-08-02 16:46:51 +04:00
Matej Bačo bb1371c1fa Update sdks to 1.6.x 2024-08-02 14:36:29 +02:00
Binyamin Yawitz be1753a198 refactor: no session alerts for otp and magic-url logins 2024-08-01 14:36:21 -04:00
Christy JacobandGitHub 8fee03bd36 Merge pull request #8398 from appwrite/fix-disabled-function-logging
Handle false appended to string
2024-08-01 02:04:33 +04:00
Christy JacobandGitHub 0ae40748fe Merge branch '1.6.x' into feat-schedule-execution-improvements 2024-08-01 01:01:43 +04:00
Christy JacobandGitHub 733b654c9a Merge pull request #8414 from appwrite/feat-adding-cicd-benchmarker
feat: Adding ci/cd benchmark
2024-08-01 00:51:37 +04:00
Christy Jacob cf4030095f chore: linter 2024-07-31 20:48:59 +00:00
Christy JacobandGitHub 2686e06e68 Merge branch '1.6.x' into feat-session-alert-improvements 2024-08-01 00:44:39 +04:00
Christy Jacob de1d205341 chore: update changes from merge 2024-07-31 20:43:28 +00:00
Christy Jacob 654b73c966 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-schedule-execution-improvements 2024-07-31 20:41:42 +00:00
Christy JacobandGitHub c70f3476e2 Merge pull request #8444 from appwrite/tests-functions-refactoring
tests: Refactor deployment check in function tests
2024-08-01 00:16:00 +04:00
Binyamin Yawitz 821d0bee19 feat: Purging collection cache 2024-07-30 20:36:29 -04:00
Binyamin Yawitz 999c2cfac7 fix: reviews 2024-07-30 17:39:32 -04:00
Binyamin Yawitz 1559fec29a fix: Increasing buckets metadata 2024-07-30 16:53:18 -04:00
Khushboo VermaandGitHub e3017a83e0 Merge branch '1.6.x' into fix-disabled-function-logging 2024-07-30 19:21:24 +05:30
Christy JacobandGitHub c857f2ddf9 Merge pull request #8427 from appwrite/feat-move-functions-marketplace-to-appwrite
Move functions marketplace to appwrite
2024-07-30 17:07:15 +04:00
Khushboo Verma 395f431718 Add endpoint to get template by ID 2024-07-30 17:16:45 +05:30
Khushboo Verma 939adfd09b Improve tests 2024-07-30 16:11:04 +05:30
Khushboo Verma c9f8671af1 Small fixes 2024-07-30 15:13:38 +05:30
Khushboo Verma 198b460e22 Improve tests 2024-07-30 02:25:18 +05:30
Khushboo Verma df9f4ffb04 Add tests for filtering and pagination 2024-07-29 19:09:48 +05:30
Khushboo Verma 0369e8913f Add filter by runtimes 2024-07-29 18:59:16 +05:30
Binyamin Yawitz 6265a25e90 tests: Refactor deployment check in function tests 2024-07-29 08:39:11 -04:00
Khushboo Verma 9f0f5657cb Add filtering using usecase 2024-07-29 17:55:58 +05:30
Khushboo Verma 7dca530f70 Add pagination to templates 2024-07-29 16:02:49 +05:30
Khushboo Verma 57bd180fd4 Make code verbose 2024-07-29 11:22:32 +05:30
Khushboo Verma d43039387e Merge branch '1.6.x' into fix-disabled-function-logging 2024-07-29 11:19:36 +05:30
Khushboo Verma af94fc7882 Add test for endpoint 2024-07-26 17:59:18 +05:30
Khushboo Verma de114e61cd Merge branch '1.6.x' into feat-move-functions-marketplace-to-appwrite 2024-07-26 17:48:48 +05:30
Khushboo Verma 03de578293 Rename as per convensions 2024-07-26 17:47:03 +05:30
Khushboo Verma df5c495e8f Remove APPWRITE_API_KEY and add scopes 2024-07-26 16:06:40 +05:30
Jake BarnbyandGitHub 153736a020 Merge pull request #8425 from appwrite/feat-expose-execution-schedule-date
Feat: scheduledAt in execution response model
2024-07-25 22:01:41 +12:00
Matej Bačo 75db8946a6 Fix function redeployments 2024-07-25 08:36:11 +00:00
Matej Bačo c924e417be Merge remote-tracking branch 'origin/1.6.x' into feat-expose-execution-schedule-date 2024-07-25 06:54:58 +00:00
Matej Bačo 5c65fa895d PR review changes 2024-07-25 06:54:50 +00:00
Damodar LohaniandGitHub 4765f05d8e Merge pull request #8432 from appwrite/fix-calculations-1.6
fix file size calculations
2024-07-24 18:29:21 +05:45
Damodar LohaniandGitHub d2b2d1a135 fix file size calculations 2024-07-24 16:33:37 +05:45
Jake BarnbyandGitHub 966108dc1b Merge pull request #8428 from appwrite/fix-router-dynamic-keys
Fix: Dynamic keys in Router executions
2024-07-24 19:39:45 +12:00
Matej BačoandGitHub 0666ce2aad Merge branch '1.6.x' into fix-router-dynamic-keys 2024-07-24 09:15:13 +02:00
Jake BarnbyandGitHub 71cd582adc Merge pull request #8430 from appwrite/chore-sync-main
Chores: Sync main to 1.6.x
2024-07-24 19:13:45 +12:00
Matej Bačo c42a2bc5b7 PR review changes 2024-07-24 07:11:48 +00:00
Matej Bačo ab55e6f511 Linter fix 2024-07-24 07:00:20 +00:00
Matej Bačo 51c163a42f Apply migrations properly 2024-07-24 06:55:36 +00:00
Matej BačoandGitHub c6ab0d8523 Merge branch '1.6.x' into feat-expose-execution-schedule-date 2024-07-24 08:38:09 +02:00
Matej Bačo 6d31f18bca Merge branch 'main' into chore-sync-main 2024-07-24 08:32:55 +02:00
Christy JacobandGitHub bcd3af90f0 Merge pull request #8394 from appwrite/feat-add-appwrite-region-env-var
Add APPWRITE_REGION in function variables
2024-07-23 23:46:55 +04:00
Matej Bačo cb3c7505fe Fix Router dynamic keys 2024-07-23 14:28:13 +02:00
Khushboo Verma 12487e17b0 Merge branch '1.6.x' into feat-move-functions-marketplace-to-appwrite 2024-07-23 14:22:08 +05:30
Jake BarnbyandGitHub c465026fd1 Merge pull request #8403 from appwrite/feat-1.6.x-migrations-and-filters
Feat 1.6.x migrations and filters
2024-07-23 19:25:58 +12:00
Bradley SchofieldandGitHub 0bdd2c5204 Merge branch '1.6.x' into feat-1.6.x-migrations-and-filters 2024-07-23 11:42:23 +09:00
Christy JacobandGitHub f85ad00ee4 Merge pull request #8420 from appwrite/fix-reqried-jwt-session
Fix: users JWT methods
2024-07-22 16:40:13 +04:00
Bradley Schofield 4b585466e1 Update api.php 2024-07-22 20:03:45 +09:00
Bradley Schofield 4f558d35be Improvement v3 -> v4 transition 2024-07-22 19:42:29 +09:00
Bradley Schofield 9e3c5b9368 Add variable runtimes sizes migrations 2024-07-22 19:40:05 +09:00
Matej Bačo 304910e303 Add schedule date to response format 2024-07-22 10:36:18 +00:00
Christy Jacob eb4e259be5 chore: linter 2024-07-22 13:08:02 +04:00
Christy Jacob 7614a356a2 feat: address review comments 2024-07-22 12:55:15 +04:00
Bradley Schofield 3a6df3c973 Merge branch '1.6.x' into feat-1.6.x-migrations-and-filters 2024-07-22 11:38:17 +09:00
Bradley Schofield 8e61b2149f Auto update V3 Runtimes into V4 Runtimes on execution 2024-07-22 11:33:31 +09:00
Christy Jacob 98b8e13add feat: address review comments 2024-07-21 23:45:39 +04:00
Christy Jacob 711ca8801a feat: address review comments 2024-07-21 23:30:06 +04:00
Matej Bačo d4a5891c0d Fix user JWTs without sessionID 2024-07-21 13:28:23 +00:00
Matej Bačo 2065468708 Make session not required in users JWT generation 2024-07-21 13:00:12 +00:00
loks0n 9022606242 Merge branch 'feat-session-alert-improvements' of https://github.com/appwrite/appwrite into feat-session-alert-improvements 2024-07-19 22:19:55 +01:00
loks0n 2060b23379 feat: improve date 2024-07-19 22:19:52 +01:00
Luke B. SilverandGitHub 79aca9f4cd Merge branch '1.6.x' into feat-session-alert-improvements 2024-07-19 21:58:52 +01:00
Binyamin YawitzandGitHub 471de45be2 Merge branch '1.6.x' into feat-adding-cicd-benchmarker 2024-07-19 14:38:18 -04:00
Binyamin Yawitz be4c0f52a7 feat: Adding thousands' comma 2024-07-19 13:40:15 -04:00
Binyamin Yawitz 0cc75b6c87 feat: Adding thousands' comma 2024-07-19 13:19:34 -04:00
Binyamin Yawitz 546161ce08 feat: Benchmark to sub-job in test 2024-07-19 10:48:30 -04:00
Binyamin Yawitz a4eeebef88 feat: Benchmark to sub-job in test 2024-07-19 10:47:39 -04:00
Khushboo Verma 14589a11c0 Add Go templates 2024-07-19 18:24:59 +05:30
Khushboo Verma 6af886687e Add Go templates 2024-07-19 18:24:20 +05:30
Torsten DittmannandGitHub cc7d6ad76c Merge pull request #8417 from appwrite/feat-migration-1.6
feat: base migration for 1.6.x
2024-07-19 14:48:13 +02:00
Khushboo Verma 1b5d815532 Add get function templates endpoint 2024-07-19 17:39:18 +05:30
Torsten Dittmann 2d195fb74e revert: wildcard hostname in .env 2024-07-19 13:54:52 +02:00
Torsten Dittmann 8ad15b2df1 feat: base migration for 1.6.x 2024-07-19 13:53:42 +02:00
loks0n 76aaa50ad6 chore: matej suggested test 2024-07-19 10:54:20 +01:00
loks0n 5a56131efa chore: binyamin review 2024-07-19 10:22:50 +01:00
Binyamin Yawitz bf29d9c1af fixes: Review 2024-07-18 18:21:58 -04:00
Binyamin YawitzandGitHub ebc5cea97f Merge branch '1.6.x' into feat-adding-cicd-benchmarker 2024-07-18 17:42:15 -04:00
Binyamin Yawitz 8b7c76de91 feat: Making sure benchmark uses production version 2024-07-18 17:20:55 -04:00
Binyamin Yawitz da3a60cc50 fix: Wording 2024-07-18 17:19:04 -04:00
Binyamin Yawitz 5caff5abd3 feat: Compering to latest version 2024-07-18 17:16:43 -04:00
Binyamin Yawitz 314dcf8cd1 feat: Compering to latest version 2024-07-18 17:12:20 -04:00
Binyamin Yawitz a9ed4f77fa feat: Compering to latest version 2024-07-18 16:58:24 -04:00
Binyamin Yawitz 4a265c52bd feat: Adding ci/cd benchmark 2024-07-18 16:46:56 -04:00
Binyamin Yawitz 62837eebbc feat: Adding ci/cd benchmark 2024-07-18 16:35:08 -04:00
Binyamin Yawitz a616c87d05 feat: Adding ci/cd benchmark 2024-07-18 16:25:16 -04:00
Binyamin Yawitz 79b5698e26 feat: Adding ci/cd benchmark 2024-07-18 16:19:40 -04:00
Binyamin Yawitz d0b87fe4ae feat: Adding ci/cd benchmark 2024-07-18 15:49:49 -04:00
Binyamin Yawitz 18f5a5965e feat: Adding ci/cd benchmark 2024-07-18 15:44:55 -04:00
Torsten Dittmann 0661291d4c chore: upgrade console 2024-07-18 20:59:55 +02:00
Khushboo Verma 6c1cfb83a6 Formatted file 2024-07-18 23:50:09 +05:30
Khushboo Verma c0f7edc840 Add function templates to config 2024-07-18 23:47:28 +05:30
Torsten Dittmann af998ef868 chore: update console 2024-07-18 17:29:15 +02:00
loks0n d0a0329cc8 test: smaller sleep 2024-07-18 13:12:39 +01:00
loks0n f02bf6ac5a feat: use coroutines delay system 2024-07-18 13:03:24 +01:00
Luke B. SilverandGitHub 52d9057d1f Merge branch '1.6.x' into feat-session-alert-improvements 2024-07-18 12:35:17 +01:00
Torsten DittmannandGitHub 31d4932fdf Merge pull request #8406 from appwrite/feat-console-containerized 2024-07-18 01:36:02 +02:00
Torsten Dittmann 5187796345 Merge branch 'feat-console-containerized' of https://github.com/appwrite/appwrite into feat-console-containerized 2024-07-17 22:49:39 +02:00
Torsten Dittmann 9e1b643c7b tests: fix endpoint to use traefik 2024-07-17 22:49:32 +02:00
Torsten Dittmann a1dd9fe3ff tests: fix endpoint to use traefik 2024-07-17 22:47:33 +02:00
Torsten Dittmann bcd472ca59 fix: compose console port 2024-07-17 22:39:32 +02:00
Torsten Dittmann 8bc75b2bbe fix: remove console static files from loading 2024-07-17 22:22:03 +02:00
Torsten Dittmann 9c4f7dd108 fix: accidentally removed copy command from dockerfile 2024-07-17 20:35:09 +02:00
Torsten Dittmann 8210e0ffaf fix: new image 2024-07-17 20:29:58 +02:00
Torsten Dittmann 9e1b00d8e5 fix: console image 2024-07-17 20:19:34 +02:00
Torsten Dittmann eb673a931e fix: dockerfile 2024-07-17 19:29:48 +02:00
Torsten Dittmann e61ff28c0f fix: production compose template 2024-07-17 19:26:27 +02:00
Torsten Dittmann 0ecc1849a7 feat: containerized console 2024-07-17 19:24:52 +02:00
Torsten Dittmann 6cb9dd0ac8 chore: update composer lock 2024-07-17 18:22:54 +02:00
Torsten Dittmann 86ed408cfe chore: release cli 6.0.0-rc.4 2024-07-17 15:54:04 +02:00
loks0n 339a615359 feat: improve description 2024-07-17 10:25:50 +01:00
Bradley Schofield efa1924f26 Run Linter 2024-07-17 18:00:41 +09:00
Bradley Schofield 83c4350395 Make scopes optional on functions 2024-07-17 17:58:25 +09:00
Bradley Schofield 7dec5f68be Update V21.php 2024-07-17 15:20:42 +09:00
Bradley Schofield d78c880bf0 Update V21.php 2024-07-17 15:20:34 +09:00
Bradley Schofield 917de006bf Increate stable version number 2024-07-17 15:01:44 +09:00
Bradley Schofield 35375c42ad Fix typos 2024-07-17 14:54:37 +09:00
Bradley Schofield ec9bb8a898 Re-add V17 Response filter 2024-07-17 14:53:23 +09:00
Bradley Schofield 5d6b74e447 Add migrations and filters for 1.6.x 2024-07-17 14:52:17 +09:00
Khushboo Verma e42e7e91fb Update compose.phtml as well 2024-07-16 19:19:38 +05:30
loks0n 67778d5e96 fix: typo 2024-07-16 14:45:34 +01:00
loks0n d765c76576 fix: typo 2024-07-16 14:42:46 +01:00
loks0n 46fa5dbb74 test: update session alert tests 2024-07-16 13:31:32 +01:00
loks0n 20e77a1e9e feat: optimise 2024-07-16 13:03:26 +01:00
loks0n 67ec4b49ed feat: only send after first session 2024-07-16 13:01:23 +01:00
loks0n 612aa1421b feat: improve copy 2024-07-16 12:56:52 +01:00
loks0n 137d81be9e feat: add to templates 2024-07-16 12:49:30 +01:00
Khushboo Verma 81eb0f8507 Small fix 2024-07-16 16:33:34 +05:30
Khushboo Verma 0970d784ff Handle false appended to string 2024-07-16 16:10:17 +05:30
Luke B. SilverandGitHub 1025c575f6 Merge branch '1.6.x' into fix-build-size-limits 2024-07-16 10:23:44 +01:00
Christy JacobandGitHub 275552fcad Merge pull request #8366 from appwrite/feat-project-accessed-at
Feat project last activity
2024-07-16 11:35:09 +04:00
Damodar LohaniandGitHub 0374711298 Update projects.php 2024-07-16 06:53:21 +05:45
loks0n 67fa2c9e39 feat: max build size 2024-07-15 10:53:29 +01:00
Khushboo Verma e6686bd3dd Add APPWRITE_REGION in env var 2024-07-15 12:40:11 +05:30
Damodar Lohani 7ed877d96a fix typo 2024-07-15 04:53:27 +00:00
Damodar Lohani 8d52cb0f6b Merge remote-tracking branch 'origin/1.6.x' into feat-project-accessed-at 2024-07-14 10:18:54 +00:00
Torsten DittmannandGitHub 138e6f3405 Merge pull request #8359 from appwrite/1.6.x-sdks
feat: 1.6.x sdks
2024-07-10 18:05:52 +02:00
Torsten Dittmann 9fb3cc82c3 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into 1.6.x-sdks 2024-07-10 18:04:25 +02:00
Torsten Dittmann 37c23de29d chore: bump version 2024-07-10 18:00:22 +02:00
Jake BarnbyandGitHub eb10c498eb Merge pull request #8367 from appwrite/feat-update-delete-authenticator
Update delete authenticator to remove OTP Validation
2024-07-10 20:24:50 +12:00
Bradley Schofield 801f22856c Remove 1.6.x generated docs 2024-07-10 16:34:41 +09:00
Bradley Schofield a5ef5f3d75 Fix Lock 2024-07-10 16:34:02 +09:00
Bradley Schofield 1eb178bde9 Merge branch '1.6.x' into feat-update-delete-authenticator 2024-07-10 16:27:28 +09:00
Damodar Lohani db3b2e6cb9 composer update 2024-07-10 07:22:13 +00:00
Damodar LohaniandGitHub f4a4ec1def Merge branch '1.6.x' into feat-project-accessed-at 2024-07-10 13:01:00 +05:45
Jake Barnby b343158d19 Merge remote-tracking branch 'origin/main' into 1.6.x
# Conflicts:
#	.gitmodules
#	app/console
#	composer.lock
#	tests/e2e/Services/Health/HealthCustomServerTest.php
2024-07-10 19:11:04 +12:00
Bradley Schofield 14ce70b0ff Add 1.6.x Docs 2024-07-09 13:49:44 +09:00
Bradley SchofieldandGitHub 542d2e23bd Rollback Docs 2024-07-09 02:21:52 +00:00
Damodar LohaniandGitHub 4dd86da017 Update collections.php 2024-07-09 08:00:49 +05:45
Matej Bačo e02261d09f Fix download deployment 2024-07-08 10:06:01 +02:00
Bradley Schofield 8c19fcd087 Update documentation 2024-07-08 12:59:49 +09:00
Bradley Schofield 996911e385 Update delete authenticator to remove OTP Validation 2024-07-08 10:42:27 +09:00
Damodar Lohani 61333908a5 set project accessed at while creating 2024-07-08 01:11:09 +00:00
Damodar Lohani cfb00bf271 fix typo 2024-07-08 01:03:52 +00:00
Damodar Lohani bfebf79630 update project accessedAt once every 24 hours 2024-07-08 01:02:58 +00:00
Damodar Lohani fd2a2a45c6 project collection accessed at attribute 2024-07-08 01:01:50 +00:00
Matej Bačo 8a11183cf8 Fix env var in domain execution 2024-07-03 19:01:00 +02:00
Torsten Dittmann f25ceee7cb chore: bump sdk versions 2024-07-03 18:53:01 +02:00
Matej Bačo 2e9436aeae Add missing dynamic key 2024-07-03 18:52:25 +02:00
Binyamin Yawitz 363c8a7b9e fix: Sentry scheme 2024-07-03 11:51:58 -04:00
Torsten Dittmann 5a5609dc0a feat: 1.6.x sdks 2024-07-03 17:25:40 +02:00
Matej Bačo b9cebae561 Fix activate param 2024-07-03 17:14:59 +02:00
Binyamin Yawitz 46316c08bd fix: Logger version 2024-07-03 10:06:06 -04:00
Torsten DittmannandGitHub c8ee399cb7 Merge pull request #8357 from appwrite/chore-add-test-for-duration
Fix execution duration
2024-07-03 16:00:23 +02:00
Torsten DittmannandGitHub 8bc3fb18e2 Merge pull request #8356 from appwrite/chore-16x-specs
Chores: Update specs
2024-07-03 15:41:12 +02:00
Binyamin Yawitz c5650ba5f0 fix: Ensuring new logger syntax 2024-07-03 09:29:35 -04:00
Khushboo Verma d6a1ef80b3 Remove wrong test 2024-07-03 18:24:46 +05:30
Khushboo Verma 4ef42c9cf8 Fix execution duration and add test 2024-07-03 18:05:08 +05:30
Matej Bačo 7549b81a2d Upgrade Console 2024-07-03 12:14:07 +00:00
Matej Bačo b49a5fe03b Upgrade executor 2024-07-03 12:03:39 +00:00
Matej Bačo 88aefe230c QA changes with Console 2024-07-03 11:36:56 +00:00
Matej Bačo 914886d79f Update specs 2024-07-03 10:32:13 +00:00
Matej Bačo 70fc5e6676 Merge remote-tracking branch 'origin/1.6.x' into chore-16x-specs 2024-07-03 10:24:34 +00:00
Matej Bačo 3e62ca477d Update specs 2024-07-03 10:24:28 +00:00
Damodar LohaniandGitHub d2c5e55ca7 Merge pull request #8355 from appwrite/feat-scheduled-executions
Feat scheduled executions
2024-07-03 16:08:48 +05:45
Damodar LohaniandGitHub 71a9694f86 Merge pull request #7565 from appwrite/mock-numbers
Mock OTP and phone numbers
2024-07-03 15:21:58 +05:45
Matej Bačo b5fc9d5789 PR review changes 2024-07-03 09:35:56 +00:00
Matej Bačo 2262d516f1 Merge remote-tracking branch 'origin/1.6.x' into mock-numbers 2024-07-03 09:30:11 +00:00
Damodar LohaniandGitHub 75cd558670 Merge pull request #8323 from appwrite/feat-opr-v4-support
OPR v4 support
2024-07-03 15:11:54 +05:45
Damodar LohaniandGitHub 1c0490aa91 Merge pull request #8315 from appwrite/feat-new-session-alert
Feat new session alert
2024-07-03 14:58:16 +05:45
Damodar LohaniandGitHub 5f84edd983 Merge pull request #8337 from appwrite/feat-delete-execution
Feat delete execution
2024-07-03 14:55:57 +05:45
Damodar LohaniandGitHub b3ff29dca5 Merge pull request #8243 from appwrite/feat-scheduled-executions
Feat: Scheduled executions (execute later)
2024-07-03 14:54:58 +05:45
Matej Bačo 3a570b1f73 PR review changes 2024-07-03 08:27:54 +00:00
Matej Bačo f59a6cf22e Fix test 2024-07-03 08:02:16 +00:00
Matej Bačo 5fb41fd216 Fix executions 2024-07-03 08:00:17 +00:00
Matej Bačo 5ef196ec9a PR review changes 2024-07-03 07:11:15 +00:00
Matej Bačo 1c83668568 PR review changes 2024-07-03 07:02:41 +00:00
Binyamin YawitzandGitHub c7d030fef4 Merge branch 'feat-scheduled-executions' into feat-delete-execution 2024-07-02 16:21:20 -04:00
Binyamin YawitzandGitHub 5ba37f2fa5 Merge branch '1.6.x' into feat-scheduled-executions 2024-07-02 16:21:16 -04:00
Binyamin YawitzandGitHub efdf278254 Merge branch '1.6.x' into feat-new-session-alert 2024-07-02 16:21:12 -04:00
Binyamin YawitzandGitHub deaec44264 Merge branch '1.6.x' into mock-numbers 2024-07-02 16:21:07 -04:00
Binyamin YawitzandGitHub 1ce4eee34c Merge branch '1.6.x' into feat-opr-v4-support 2024-07-02 16:20:55 -04:00
Eldad A. FuxandGitHub 34076b903f Merge pull request #8350 from appwrite/chore-16x-tests
Tests: Ensure CI/CD works
2024-07-02 16:18:48 -04:00
Matej Bačo acc97ae52d Remove Leftover 2024-07-02 19:41:49 +00:00
Matej Bačo c7ccc38768 Fix tests 2024-07-02 19:08:31 +00:00
Matej Bačo 263981c927 Fix function tests 2024-07-02 18:39:54 +00:00
Matej Bačo 23b522da98 Empty commit 2024-07-02 17:17:22 +00:00
Matej Bačo 7dec126987 Revert unwanted change 2024-07-02 17:16:53 +00:00
Matej Bačo f1603ecb6a Upgrade executor 2024-07-02 17:15:20 +00:00
Matej Bačo cc176a4e7d Merge remote-tracking branch 'origin/1.6.x' into feat-scheduled-executions 2024-07-02 15:56:54 +00:00
Khushboo Verma e9034a9d9d Fix missing closing bracket 2024-07-02 17:08:59 +05:30
Khushboo Verma aab4665701 Resolve merge conflict 2024-07-02 15:02:58 +05:30
Khushboo Verma ead0b93900 Resolve merge conflict 2024-07-02 14:59:15 +05:30
Khushboo VermaandGitHub f72f9c7881 Merge branch '1.6.x' into feat-new-session-alert 2024-07-02 14:50:11 +05:30
Matej Bačo 6fdc5a3425 PR review changes 2024-07-02 08:43:04 +00:00
Binyamin Yawitz 30db70fe80 fix: reviews 2024-07-01 10:23:39 -04:00
Eldad A. FuxandGitHub 2ec038ed26 Merge pull request #8336 from appwrite/feat-add-appwrite-version
Add appwrite version in function variables
2024-07-01 10:18:38 -04:00
Eldad A. FuxandGitHub 23d91e3790 Merge pull request #8333 from appwrite/fix-16x-tests
Fix: 1.6.x failing tests
2024-07-01 10:15:30 -04:00
loks0n fef22825fb chore: delete schedule if not active 2024-07-01 14:35:37 +01:00
Khushboo Verma f8427155f9 Merge branch '1.6.x' into mock-numbers 2024-07-01 17:02:41 +05:30
Matej Bačo f87e2bc879 Binary support & tests for Router 2024-07-01 10:35:06 +02:00
Matej Bačo 7bdcd5c436 Multipoart support 2024-07-01 06:57:18 +00:00
loks0n bb3ee81065 chore: rename metadata to data 2024-06-28 22:42:55 +01:00
Binyamin Yawitz 87c7da0efa fix: unnecessary space 2024-06-28 11:12:24 -04:00
Khushboo Verma 66c761b73e Create execution even when logging is disabled 2024-06-28 19:51:33 +05:30
Binyamin Yawitz 4e4fcc06fd refactor: Project deletion in worker 2024-06-27 18:27:04 -04:00
Binyamin Yawitz d58a352313 fix: Removing debug var_dump 2024-06-27 18:13:17 -04:00
Binyamin Yawitz b19d7cb35f feat: Update scheduled when deleting an execution 2024-06-27 18:12:23 -04:00
Binyamin Yawitz 3aee3122a9 Merge remote-tracking branch 'refs/remotes/origin/feat-scheduled-executions' into feat-delete-execution 2024-06-27 15:10:51 -04:00
Binyamin Yawitz 010f8d3fb8 review: Delete execution 2024-06-27 12:36:24 -04:00
Binyamin Yawitz 9172ff6b14 feat: Delete execution 2024-06-27 11:39:27 -04:00
Binyamin Yawitz 366d75fe39 feat: Delete execution 2024-06-27 11:17:14 -04:00
Khushboo Verma 0a335ed8ac Add appwrite version 2024-06-27 18:13:15 +05:30
Matej Bačo b041db0658 Fix tests 2024-06-27 10:17:01 +00:00
Matej Bačo 6411dd9fb4 Mark domain as traefik 2024-06-27 08:24:12 +00:00
Binyamin Yawitz 5e14cdef11 test: Testing team delete removes membership 2024-06-26 13:28:41 -04:00
Binyamin Yawitz f8869efa42 test: Testing team delete removes membership 2024-06-26 13:19:41 -04:00
Binyamin Yawitz d113fb2751 test: Testing team delete removes membership 2024-06-26 13:17:08 -04:00
Binyamin Yawitz 93d4efbc0e test: Testing team delete removes membership 2024-06-26 13:08:36 -04:00
Binyamin Yawitz 9b951e86e9 test: Testing team delete removes membership 2024-06-26 13:06:10 -04:00
Binyamin Yawitz 184d76fc9e Merge branch 'refs/heads/1.6.x' into feat-extract-team-deletion 2024-06-26 12:28:12 -04:00
loks0n d0125398f1 fix: test 2024-06-26 15:46:12 +01:00
loks0n fd12449cc3 test: fix 2024-06-26 14:01:53 +01:00
loks0n e884009075 fix: test 2024-06-26 13:14:07 +01:00
Luke B. SilverandGitHub ecf761beab Merge branch '1.6.x' into feat-new-session-alert 2024-06-26 12:44:49 +01:00
loks0n 9eb8f02b53 fix: test 2024-06-26 12:42:35 +01:00
loks0n d9434517dc Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-scheduled-executions 2024-06-26 12:30:48 +01:00
loks0n 303ce498be feat: allow custom path method body 2024-06-26 12:30:23 +01:00
Torsten DittmannandGitHub c64e9e1fab Merge pull request #8330 from appwrite/feat-vcs-get-contents
Feat: vcs.getRepositoryContents()
2024-06-26 13:13:40 +02:00
Matej Bačo d7d5fb0bd7 Improve VCS tests 2024-06-26 10:27:24 +00:00
Matej Bačo 186cef458b Implemented vcs.getRepositoryContents() 2024-06-26 10:03:27 +00:00
loks0n 878f6c86df chore: matej review p1 2024-06-26 11:01:43 +01:00
loks0n 6353de6f10 chore: fix template 2024-06-26 09:42:01 +01:00
Luke B. SilverandGitHub dab0a17082 Merge branch '1.6.x' into feat-scheduled-executions 2024-06-25 17:21:44 +01:00
Christy Jacob fa93d9d1cc chore: linter 2024-06-25 15:31:02 +00:00
Christy Jacob 7cec278d27 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into mock-numbers 2024-06-25 15:17:22 +00:00
Christy JacobandGitHub 91fe8b7a8b Merge pull request #8295 from appwrite/sync-with-main
Sync with main
2024-06-25 19:13:32 +04:00
Christy JacobandGitHub c692661429 Merge pull request #8215 from appwrite/feat-func-deploy-type
feat: Adding cli to the function deployment type
2024-06-25 17:59:42 +04:00
Binyamin Yawitz b849d82772 refactor: Singular 2024-06-25 09:18:17 -04:00
Binyamin Yawitz 9cf0af5b45 refactor: Naming parameters 2024-06-25 09:17:34 -04:00
Binyamin Yawitz d62d5662f6 refactor: Naming parameters 2024-06-25 09:16:27 -04:00
Khushboo Verma e5fd3619f7 Merge branch '1.6.x' into feat-opr-v4-support 2024-06-25 15:13:43 +05:30
Khushboo Verma 0edbec4f44 OPR v4 support 2024-06-25 15:03:07 +05:30
Christy JacobandGitHub c38eda3cea Merge branch '1.6.x' into feat-func-deploy-type 2024-06-25 13:31:54 +04:00
Christy Jacob c774b952de chore: flaky function test 2024-06-25 09:31:09 +00:00
loks0n 0d987045bf chore: revert fmt 2024-06-25 08:46:51 +01:00
loks0n edf7af34f1 test: session alerts 2024-06-24 14:39:47 +01:00
loks0n 0ea2438069 fix: implementation 2024-06-24 14:12:09 +01:00
loks0n baf5e5ade0 Merge branch '1.6.x' of https://github.com/appwrite/appwrite into feat-new-session-alert 2024-06-24 10:34:11 +01:00
loks0n fbeca34df8 fix: func 2024-06-24 10:34:06 +01:00
loks0n f3f233eb14 feat: new session alert 2024-06-21 19:21:05 +01:00
Christy Jacob 7b63f68fc3 chore: fix tests 2024-06-21 18:02:01 +00:00
Christy Jacob bf8b2ea7ae Merge branch '1.6.x' of https://github.com/appwrite/appwrite into sync-with-main 2024-06-21 16:18:19 +00:00
Christy JacobandGitHub 5ba99ed4e1 Merge pull request #8304 from appwrite/1.6.x-specs
1.6.x specs
2024-06-21 19:41:13 +05:30
Binyamin Yawitz 5e0000376e chore: generate specs 2024-06-21 09:56:19 -04:00
Christy Jacob a2f27ec15f Merge branch '1.5.x' of https://github.com/appwrite/appwrite into sync-with-main 2024-06-21 13:11:35 +00:00
Christy Jacob 330e7a93c6 chore: composer update 2024-06-20 15:27:16 +00:00
Christy Jacob fdf44c7bab Merge branch 'main' of https://github.com/appwrite/appwrite into sync-with-main 2024-06-20 15:26:17 +00:00
Christy Jacob c5fa7eb7dc Merge branch '1.6.x' of github.com:appwrite/appwrite into mock-numbers 2024-06-20 20:53:06 +05:30
Christy Jacob 3b791f0b6f chore: linter 2024-06-20 20:44:02 +05:30
Christy Jacob 814b59a13a Merge branch 'main' of github.com:appwrite/appwrite into mock-numbers 2024-06-20 20:42:29 +05:30
Christy Jacob 8d5b79c13d fix: undefined variable 2024-06-20 20:31:54 +05:30
Christy Jacob 1b1cfc38c8 fix: undefined variable 2024-06-20 20:31:20 +05:30
Christy Jacob 392aaca681 chore: fix tests 2024-06-20 20:19:56 +05:30
Christy Jacob e436ad721f chore: linter 2024-06-20 20:02:36 +05:30
Christy Jacob e32e7063bb chore: linter 2024-06-20 20:01:33 +05:30
Binyamin Yawitz 74360abdb0 test: Test function creation type from CLI 2024-06-17 14:18:01 -04:00
Binyamin YawitzandGitHub 6cfada946f Merge branch '1.6.x' into feat-func-deploy-type 2024-06-17 13:53:21 -04:00
loks0n 67fddeb4fa fix: path and method 2024-06-17 14:23:14 +01:00
loks0n 5f144f91ad chore: matej review 2024-06-17 14:12:02 +01:00
loks0n 83ffc41d92 chore: fmt 2024-06-17 13:46:18 +01:00
loks0n 2f0f7bf9c7 fix: test 2024-06-17 13:44:12 +01:00
Christy Jacob fa070c5e1e chore: sync & updates 2024-06-16 22:41:13 +00:00
Christy Jacob 9e1541d96a Merge branch 'main' of https://github.com/appwrite/appwrite into mock-numbers 2024-06-16 07:28:22 +00:00
Christy Jacob a912bbe374 chore: update variable name 2024-06-16 07:19:10 +00:00
Luke B. SilverandGitHub 9341c9d76b Merge branch '1.6.x' into feat-scheduled-executions 2024-06-14 14:07:41 +01:00
loks0n 10e37bb8ca test: fix 2024-06-14 14:01:04 +01:00
Christy JacobandGitHub 83d60612f2 Merge pull request #7605 from appwrite/feat-cancel-build-deployment
Added cancel build endpoint
2024-06-13 19:06:03 +05:30
loks0n 6f38ef3a18 test: poll scheduled function 2024-06-13 14:22:38 +01:00
loks0n dff9bed882 test: increase delay 2024-06-13 13:41:37 +01:00
loks0n a83d125f54 chore: adjust timers 2024-06-13 13:34:10 +01:00
loks0n cfdd40dbeb test: execution is completed 2024-06-13 09:36:01 +01:00
loks0n 865b12ba9d chore: exception type 2024-06-13 09:24:51 +01:00
loks0n 0a78d41ecb chore: remove endTime 2024-06-12 13:50:18 +01:00
loks0n 7559c9af68 chore: us spelling of canceled 2024-06-12 11:21:48 +01:00
loks0n d6bfe084c1 fix: tests 2024-06-11 17:27:22 +01:00
loks0n 682b854aad test: fix functions test 2024-06-11 17:01:56 +01:00
loks0n 1269397475 chore: fmt 2024-06-11 15:50:50 +01:00
loks0n 853ffe804e feat: improve duration calc 2024-06-11 15:48:55 +01:00
loks0n 67b32ecb05 test: cancel deployment 2024-06-11 15:41:13 +01:00
loks0n 1e9ced2878 test: scheduled status 2024-06-11 13:57:03 +01:00
loks0n 7e8f72d267 feat: implement scheduledAt in schedule 2024-06-11 13:39:38 +01:00
Matej Bačo b8b81a9bd1 WIP: Schedulded executions 2024-06-07 19:05:29 +00:00
Khushboo Verma 97786a05ff Updated cancelled build duration 2024-06-07 20:29:24 +05:30
Khushboo Verma d233f1ac64 Update duration and endTime 2024-06-07 16:53:33 +05:30
Khushboo Verma c2c6d337ed Resolved merge conflict 2024-06-06 19:39:16 +05:30
Christy JacobandGitHub 3e47b43908 Merge pull request #8214 from appwrite/chore-update-create-build-endpoint
Remove build id from api endpoint
2024-06-06 17:53:05 +05:30
Eldad A. FuxandGitHub f91a68c8a0 Merge pull request #8207 from appwrite/feat-users-create-jwt
Feat: users.createJWT()
2024-06-04 07:00:32 -04:00
Eldad A. FuxandGitHub 2dd8c49698 Merge pull request #8213 from appwrite/feat-projects-createJWT
Feat:  projects.createJWT (dynamic keys)
2024-06-04 07:00:18 -04:00
Matej BačoandGitHub 9279468c27 Merge branch '1.6.x' into feat-users-create-jwt 2024-06-04 09:43:14 +02:00
Matej BačoandGitHub c279745259 Merge branch '1.6.x' into feat-projects-createJWT 2024-06-04 09:43:12 +02:00
Khushboo Verma d7a40e2b4d Remove the code to fetch build document 2024-06-02 23:14:41 +05:30
Khushboo Verma c60b8b05a3 Merge branch 'feat-cancel-build-deployment' of github.com:appwrite/appwrite into feat-cancel-build-deployment 2024-06-02 23:05:18 +05:30
Khushboo Verma 2643202c97 Replaced build1 with build 2024-06-02 23:04:49 +05:30
Khushboo VermaandGitHub beb3fe1d2b Merge branch '1.6.x' into chore-update-create-build-endpoint 2024-05-31 14:17:34 +05:30
Khushboo VermaandGitHub 1beaeba214 Merge branch '1.6.x' into feat-cancel-build-deployment 2024-05-31 14:16:14 +05:30
Christy JacobandGitHub b135c69bd6 Merge pull request #8187 from appwrite/feat-unified-app-logger-config
feat(logger): Unifying logger environment variable and adapting to th…
2024-05-31 03:38:54 +04:00
Khushboo Verma 0eabff5faf Refactor builds document creation 2024-05-31 01:53:29 +05:30
Khushboo Verma cfec6c974f Updated spec 2024-05-30 22:40:09 +05:30
Khushboo Verma d3c2e9db1f Add comment for clarity 2024-05-30 22:33:37 +05:30
Binyamin Yawitz 18ca583df8 feat: Deleting teams in sync 2024-05-29 15:52:22 -04:00
Binyamin Yawitz ca7f222640 feat: Adding cli to the function deployment type 2024-05-29 12:55:14 -04:00
Khushboo Verma 94e7669b41 Remove build id from api endpoint 2024-05-29 17:48:52 +05:30
Matej Bačo 72e3898f09 CI/CD trigger 2024-05-29 11:45:32 +02:00
Matej Bačo 49b2bcf462 Implement JWT dynamic keys test 2024-05-29 09:33:39 +00:00
Khushboo Verma edafeece9b Add console info before return 2024-05-29 14:39:57 +05:30
Matej Bačo b0317fb19d Implement project dynamic (JWT) keys 2024-05-29 08:57:25 +00:00
Matej Bačo 3b8799353d PR review changes 2024-05-29 07:51:51 +00:00
Matej BačoandGitHub d9745ca249 Merge branch '1.6.x' into feat-users-create-jwt 2024-05-29 09:33:09 +02:00
11827216ff Update tests/e2e/Services/Users/UsersBase.php
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2024-05-29 09:32:58 +02:00
2dac8bc7ed Update app/controllers/api/users.php
Co-authored-by: Steven Nguyen <1477010+stnguyen90@users.noreply.github.com>
2024-05-29 09:31:08 +02:00
Binyamin YawitzandGitHub ccafc06d93 Merge branch '1.6.x' into feat-unified-app-logger-config 2024-05-28 10:18:39 -04:00
Binyamin Yawitz b98930e5a0 feat(logger): Adding fallback comment 2024-05-28 10:18:29 -04:00
Khushboo Verma a465b23716 Merge branch '1.6.x' into feat-cancel-build-deployment 2024-05-28 19:06:09 +05:30
Christy JacobandGitHub 1cb490a36e Merge pull request #8210 from appwrite/feat-track-build-metrics-1
Add metrics for successful and failed builds
2024-05-28 17:28:27 +04:00
Khushboo Verma 0b3c188bbf Add metrics for successful and failed builds 2024-05-28 18:29:28 +05:30
Matej Bačo 711e26c605 Relay on lib to verify JWT expiry 2024-05-28 10:59:53 +00:00
Matej Bačo b1ff989c3f Implement tests, fix JWT maxAge 2024-05-28 09:25:54 +00:00
Matej Bačo 613677e9f7 Implement users.createJWT 2024-05-27 20:04:50 +00:00
Khushboo Verma 93b4bfe4df Add one more cancel check 2024-05-27 18:31:35 +05:30
Khushboo Verma 63a5b9eed3 Revert changes in app/console 2024-05-27 18:25:11 +05:30
Khushboo Verma b880a0a7b2 Remove :buildId from endpoint as discussed 2024-05-27 18:21:28 +05:30
Khushboo Verma 4e8855fad3 Merge branch '1.6.x' into feat-cancel-build-deployment 2024-05-27 18:03:59 +05:30
Christy JacobandGitHub 3ae4ed2604 Merge pull request #7512 from appwrite/feat-function-tokens
Feat: API Tokens
2024-05-24 14:45:11 +04:00
Binyamin Yawitz ebe386acd9 feat(logger): Unifying logger environment variable and adapting to the latest Logger version 2024-05-23 12:44:19 -04:00
Khushboo Verma f0de4c6eac Resolved merge conflict 2024-05-23 18:34:48 +05:30
Matej Bačo a115f878b3 Merge remote-tracking branch 'origin/1.6.x' into feat-function-tokens 2024-05-21 10:28:21 +00:00
Christy JacobandGitHub 6eef9a8394 Merge pull request #8163 from appwrite/sync-main
Sync 1.6.x with main
2024-05-21 14:05:48 +04:00
Christy Jacob 984da4f4ac Merge branch 'main' of https://github.com/appwrite/appwrite into sync-main 2024-05-20 11:05:24 +00:00
Matej BačoandGitHub d57c459e6f Merge branch '1.6.x' into feat-function-tokens 2024-05-17 18:16:43 +02:00
Matej Bačo 2e1f67245d Fix bugs, add old api key test 2024-05-16 08:39:15 +00:00
Matej Bačo 37176e7865 Backwards compatible approach 2024-05-14 12:33:49 +00:00
Matej Bačo 4d249c5d36 Bug fix 2024-05-14 12:27:35 +00:00
Matej Bačo 9846b573bd Merge remote-tracking branch 'origin/main' into feat-function-tokens 2024-05-14 11:59:43 +00:00
Matej Bačo 54c953d559 Add prefix of api key types 2024-05-14 11:58:31 +00:00
Matej Bačo 5002b0f3fa PR review changes 2024-05-09 11:50:45 +00:00
Matej Bačo 9252b66493 Add scope tests 2024-05-06 11:27:28 +00:00
Matej Bačo 3a3d5b61a6 PR review changes 2024-05-06 09:55:59 +00:00
Matej Bačo 448b0a5607 Re-add api token auth 2024-05-06 08:35:27 +00:00
Matej Bačo 9a0d9cee15 Merge remote-tracking branch 'origin/main' into feat-function-tokens 2024-05-06 08:35:14 +00:00
Khushboo Verma fba60f974e Throw exception for cancellation if build is already complete 2024-04-30 15:46:58 +05:30
Khushboo Verma ea9c71757c Fix tests 2024-04-30 00:19:14 +05:30
Khushboo Verma e2b2df1e31 Fix formatting 2024-04-29 21:18:36 +05:30
Khushboo Verma 0dd908b14c Fix formatting 2024-04-29 19:47:29 +05:30
Khushboo Verma 3123bf6b72 Fix formatting 2024-04-29 19:16:17 +05:30
Khushboo Verma 2b69f0b7e2 Fix formatting 2024-04-29 19:14:37 +05:30
Khushboo Verma 564fe96eec Resolve merge conflicts 2024-04-29 19:05:51 +05:30
Christy JacobandGitHub 88abc6511a Merge pull request #7607 from appwrite/feat-function-execution-filters
Allow execution filter attributes
2024-03-25 19:28:24 +05:30
Khushboo Verma f4cf7ba414 Merge branch 'main' into feat-function-execution-filters 2024-03-20 23:51:27 +05:30
Khushboo Verma 974104f8b0 address PR comments 2024-03-04 21:30:06 +01:00
Khushboo Verma a2b19ef116 Merge branch 'main' into feat-cancel-build-deployment 2024-03-01 18:27:34 +05:30
Khushboo Verma b552d052da Update index length 2024-02-29 19:51:13 +05:30
Khushboo Verma 1d282da6b3 Update index length 2024-02-29 19:35:58 +05:30
Khushboo Verma f8f54271d1 Fix execution indexes 2024-02-29 18:39:38 +05:30
Khushboo Verma c105cfda1a Merge branch 'main' into feat-function-execution-filters 2024-02-29 18:20:26 +05:30
Khushboo Verma 56760b14cb Restore create build endpoint 2024-02-26 15:37:47 +05:30
Khushboo Verma 727fe2c9a9 Update newBuild condition 2024-02-26 12:33:54 +05:30
Khushboo Verma 353092ba00 Remove create build endpoint 2024-02-26 00:05:00 +05:30
Khushboo Verma ed4773c2d9 Merge branch 'main' into feat-cancel-build-deployment 2024-02-25 19:14:42 +05:30
Khushboo Verma 0e3a354f1f Create build document in API instead of worker 2024-02-25 19:13:55 +05:30
Khushboo Verma c2e7497c76 Merge branch 'main' into feat-function-execution-filters 2024-02-25 16:00:14 +05:30
Khushboo Verma f1c6790240 Add indexes for execution attributes 2024-02-25 15:57:39 +05:30
Khushboo Verma a02a03e322 Fetch build document from DB 2024-02-24 00:13:11 +05:30
Christy JacobandGitHub 35562715ad Merge branch 'main' into mock-numbers 2024-02-20 12:27:14 +05:30
Khushboo Verma 3471359735 Allow execution filter attributes 2024-02-19 10:24:12 +05:30
Khushboo Verma ada1f2abb7 Mark cancelled first, then kill container 2024-02-19 00:44:21 +05:30
Khushboo Verma 12f2e761ef Updated description and response model 2024-02-18 23:25:57 +05:30
Khushboo Verma 6ebca949c0 Fixed composer error 2024-02-18 18:04:16 +05:30
Khushboo Verma f5919aad4a Merge branch 'main' into feat-cancel-build-deployment 2024-02-18 18:03:21 +05:30
Khushboo Verma ed3d598f2e Added cancel build endpoint 2024-02-18 18:01:25 +05:30
Christy Jacob abad778a1e chore: linter 2024-02-14 17:22:43 +00:00
Christy Jacob 48e87cdc7c Merge branch 'main' of github.com:appwrite/appwrite into mock-numbers 2024-02-14 21:24:53 +05:30
Christy Jacob 28a16173ab feat: add tests 2024-02-14 12:10:19 +05:30
Christy Jacob be92aa92e5 chore: rename files 2024-02-11 20:28:05 +05:30
Christy Jacob 89dcc34453 feat: initial commit 2024-02-11 20:21:19 +05:30
Matej Bačo ba6f5c1a57 Revert unnessessary changes 2024-01-29 11:21:51 +00:00
Matej Bačo 36ce992e9f Add API Tokens 2024-01-29 11:15:07 +00:00
120 changed files with 7910 additions and 889 deletions
+5 -4
View File
@@ -4,11 +4,13 @@ _APP_LOCALE=en
_APP_WORKER_PER_CORE=6
_APP_CONSOLE_WHITELIST_ROOT=disabled
_APP_CONSOLE_WHITELIST_EMAILS=
_APP_CONSOLE_SESSION_ALERTS=enabled
_APP_CONSOLE_WHITELIST_IPS=
_APP_CONSOLE_COUNTRIES_DENYLIST=AQ
_APP_CONSOLE_HOSTNAMES=localhost,appwrite.io,*.appwrite.io
_APP_SYSTEM_EMAIL_NAME=Appwrite
_APP_SYSTEM_EMAIL_ADDRESS=team@appwrite.io
_APP_SYSTEM_EMAIL_ADDRESS=noreply@appwrite.io
_APP_SYSTEM_TEAM_EMAIL=team@appwrite.io
_APP_EMAIL_SECURITY=security@appwrite.io
_APP_EMAIL_CERTIFICATES=certificates@appwrite.io
_APP_SYSTEM_RESPONSE_FORMAT=
@@ -17,7 +19,7 @@ _APP_OPTIONS_ROUTER_PROTECTION=disabled
_APP_OPTIONS_FORCE_HTTPS=disabled
_APP_OPTIONS_FUNCTIONS_FORCE_HTTPS=disabled
_APP_OPENSSL_KEY_V1=your-secret-key
_APP_DOMAIN=localhost
_APP_DOMAIN=traefik
_APP_DOMAIN_FUNCTIONS=functions.localhost
_APP_DOMAIN_TARGET=localhost
_APP_REDIS_HOST=redis
@@ -85,7 +87,6 @@ _APP_USAGE_AGGREGATION_INTERVAL=30
_APP_MAINTENANCE_RETENTION_USAGE_HOURLY=8640000
_APP_MAINTENANCE_RETENTION_SCHEDULES=86400
_APP_USAGE_STATS=enabled
_APP_LOGGING_PROVIDER=
_APP_LOGGING_CONFIG=
_APP_GRAPHQL_MAX_BATCH_SIZE=10
_APP_GRAPHQL_MAX_COMPLEXITY=250
@@ -105,4 +106,4 @@ _APP_MESSAGE_SMS_TEST_DSN=
_APP_MESSAGE_EMAIL_TEST_DSN=
_APP_MESSAGE_PUSH_TEST_DSN=
_APP_WEBHOOK_MAX_FAILED_ATTEMPTS=10
_APP_PROJECT_REGIONS=default
_APP_PROJECT_REGIONS=default
+78
View File
@@ -148,3 +148,81 @@ jobs:
- name: Run ${{matrix.service}} Shared Tables Tests
run: _APP_DATABASE_SHARED_TABLES=database_db_main docker compose exec -T appwrite test /usr/src/code/tests/e2e/Services/${{matrix.service}} --debug
benchamrking:
name: Benchmark
runs-on: ubuntu-latest
needs: setup
steps:
- name: Checkout repository
uses: actions/checkout@v3
- name: Load Cache
uses: actions/cache@v3
with:
key: ${{ env.CACHE_KEY }}
path: /tmp/${{ env.IMAGE }}.tar
fail-on-cache-miss: true
- name: Load and Start Appwrite
run: |
sed -i 's/traefik/localhost/g' .env
docker load --input /tmp/${{ env.IMAGE }}.tar
docker compose up -d
sleep 10
- name: Install Oha
run: |
echo "deb [signed-by=/usr/share/keyrings/azlux-archive-keyring.gpg] http://packages.azlux.fr/debian/ stable main" | sudo tee /etc/apt/sources.list.d/azlux.list
sudo wget -O /usr/share/keyrings/azlux-archive-keyring.gpg https://azlux.fr/repo.gpg
sudo apt update
sudo apt install oha
- name: Benchmark PR
run: oha -z 180s http://localhost/v1/health/version -j > benchmark.json
- name: Cleaning
run: docker compose down -v
- name: Installing latest version
run: |
rm docker-compose.yml
rm .env
curl https://appwrite.io/install/compose -o docker-compose.yml
curl https://appwrite.io/install/env -o .env
sed -i 's/_APP_OPTIONS_ABUSE=enabled/_APP_OPTIONS_ABUSE=disabled/g' .env
docker compose up -d
sleep 10
- name: Benchmark Latest
run: oha -z 180s http://localhost/v1/health/version -j > benchmark-latest.json
- name: Prepare comment
run: |
echo '## :sparkles: Benchmark results' > benchmark.txt
echo ' ' >> benchmark.txt
echo "- Requests per second: $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- Requests with 200 status code: $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json)" >> benchmark.txt
echo "- P99 latency: $(jq -r '.latencyPercentiles.p99' benchmark.json )" >> benchmark.txt
echo " " >> benchmark.txt
echo " " >> benchmark.txt
echo "## :zap: Benchmark Comparison" >> benchmark.txt
echo " " >> benchmark.txt
echo "| Metric | This PR | Latest version | " >> benchmark.txt
echo "| --- | --- | --- | " >> benchmark.txt
echo "| RPS | $(jq -r '.summary.requestsPerSec|tonumber?|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.summary.requestsPerSec|tonumber|floor|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| 200 | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark.json) | $(jq -r '.statusCodeDistribution."200"|tostring|[while(length>0;.[:-3])|.[-3:]]|reverse|join(",")' benchmark-latest.json) | " >> benchmark.txt
echo "| P99 | $(jq -r '.latencyPercentiles.p99' benchmark.json ) | $(jq -r '.latencyPercentiles.p99' benchmark-latest.json ) | " >> benchmark.txt
- name: Save results
uses: actions/upload-artifact@v4
if: ${{ !cancelled() }}
with:
name: benchmark.json
path: benchmark.json
retention-days: 7
- name: Find Comment
uses: peter-evans/find-comment@v3
id: fc
with:
issue-number: ${{ github.event.pull_request.number }}
comment-author: 'github-actions[bot]'
body-includes: Benchmark results
- name: Comment on PR
uses: peter-evans/create-or-update-comment@v4
with:
comment-id: ${{ steps.fc.outputs.comment-id }}
issue-number: ${{ github.event.pull_request.number }}
body-path: benchmark.txt
edit-mode: replace
+1
View File
@@ -9,6 +9,7 @@
!/.idea/php.xml
.DS_Store
.php_cs.cache
.phpactor.json
debug/
app/sdks
dev/yasd_init.php
-4
View File
@@ -1,4 +0,0 @@
[submodule "app/console"]
path = app/console
url = https://github.com/appwrite/console
branch = 4.3.30
+6
View File
@@ -1,3 +1,9 @@
# Version 1.6.0
## What's Changed
### Notable changes
# Version 1.5.10
## What's Changed
+6
View File
@@ -553,6 +553,12 @@ To run end-2-end tests for a specific service use:
docker compose exec appwrite test /usr/src/code/tests/e2e/Services/[ServiceName]
```
To run one specific test:
```bash
docker compose exec appwrite vendor/bin/phpunit --filter [FunctionName]
```
## Benchmarking
You can use WRK Docker image to benchmark the server performance. Benchmarking is extremely useful when you want to compare how the server behaves before and after a change has been applied. Replace [APPWRITE_HOSTNAME_OR_IP] with your Appwrite server hostname or IP. Note that localhost is not accessible from inside the WRK container.
+2 -19
View File
@@ -12,24 +12,7 @@ RUN composer install --ignore-platform-reqs --optimize-autoloader \
--no-plugins --no-scripts --prefer-dist \
`if [ "$TESTING" != "true" ]; then echo "--no-dev"; fi`
FROM --platform=$BUILDPLATFORM node:20.11.0-alpine3.19 as node
COPY app/console /usr/local/src/console
WORKDIR /usr/local/src/console
ARG VITE_GA_PROJECT
ARG VITE_CONSOLE_MODE
ARG VITE_APPWRITE_GROWTH_ENDPOINT=https://growth.appwrite.io/v1
ENV VITE_GA_PROJECT=$VITE_GA_PROJECT
ENV VITE_CONSOLE_MODE=$VITE_CONSOLE_MODE
ENV VITE_APPWRITE_GROWTH_ENDPOINT=$VITE_APPWRITE_GROWTH_ENDPOINT
RUN npm ci
RUN npm run build
FROM appwrite/base:0.9.1 as final
FROM appwrite/base:0.9.2 as final
LABEL maintainer="team@appwrite.io"
@@ -48,7 +31,6 @@ RUN \
WORKDIR /usr/src/code
COPY --from=composer /usr/local/src/vendor /usr/src/code/vendor
COPY --from=node /usr/local/src/console/build /usr/src/code/console
# Add Source Code
COPY ./app /usr/src/code/app
@@ -79,6 +61,7 @@ RUN chmod +x /usr/local/bin/doctor && \
chmod +x /usr/local/bin/migrate && \
chmod +x /usr/local/bin/realtime && \
chmod +x /usr/local/bin/schedule-functions && \
chmod +x /usr/local/bin/schedule-executions && \
chmod +x /usr/local/bin/schedule-messages && \
chmod +x /usr/local/bin/sdks && \
chmod +x /usr/local/bin/specs && \
+3 -3
View File
@@ -67,7 +67,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
### Windows
@@ -79,7 +79,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
#### PowerShell
@@ -89,7 +89,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
运行后,可以在浏览器上访问 http://localhost 找到 Appwrite 控制台。在非 Linux 的本机主机上完成安装后,服务器可能需要几分钟才能启动。
+3 -3
View File
@@ -75,7 +75,7 @@ docker run -it --rm \
--volume /var/run/docker.sock:/var/run/docker.sock \
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
--entrypoint="install" \
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
### Windows
@@ -87,7 +87,7 @@ docker run -it --rm ^
--volume //var/run/docker.sock:/var/run/docker.sock ^
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
--entrypoint="install" ^
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
#### PowerShell
@@ -97,7 +97,7 @@ docker run -it --rm `
--volume /var/run/docker.sock:/var/run/docker.sock `
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
--entrypoint="install" `
appwrite/appwrite:1.5.10
appwrite/appwrite:1.6.0
```
Once the Docker installation is complete, go to http://localhost to access the Appwrite console from your browser. Please note that on non-Linux native hosts, the server might take a few minutes to start after completing the installation.
Binary file not shown.
Binary file not shown.
+92 -5
View File
@@ -3029,7 +3029,7 @@ $projectCollections = array_merge([
'size' => 8,
'signed' => true,
'required' => false,
'default' => 'v3',
'default' => 'v4',
'array' => false,
'filters' => [],
],
@@ -3054,7 +3054,18 @@ $projectCollections = array_merge([
'required' => false,
'default' => null,
'filters' => [],
]
],
[
'$id' => ID::custom('scopes'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'array' => true,
'filters' => [],
],
],
'indexes' => [
[
@@ -3837,6 +3848,39 @@ $projectCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('scheduledAt'),
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('scheduleInternalId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('scheduleId'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => false,
'default' => null,
'array' => false,
'filters' => [],
],
],
'indexes' => [
[
@@ -3867,6 +3911,27 @@ $projectCollections = array_merge([
'lengths' => [32],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_requestMethod'),
'type' => Database::INDEX_KEY,
'attributes' => ['requestMethod'],
'lengths' => [128],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_requestPath'),
'type' => Database::INDEX_KEY,
'attributes' => ['requestPath'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_deployment'),
'type' => Database::INDEX_KEY,
'attributes' => ['deploymentId'],
'lengths' => [Database::LENGTH_KEY],
'orders' => [Database::ORDER_ASC],
],
[
'$id' => ID::custom('_key_responseStatusCode'),
'type' => Database::INDEX_KEY,
@@ -4311,6 +4376,17 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => 'accessedAt',
'type' => Database::VAR_DATETIME,
'format' => '',
'size' => 0,
'signed' => false,
'required' => false,
'default' => null,
'array' => false,
'filters' => ['datetime'],
],
[
'$id' => ID::custom('services'),
'type' => Database::VAR_STRING,
@@ -4518,6 +4594,17 @@ $consoleCollections = array_merge([
'array' => false,
'filters' => [],
],
[
'$id' => ID::custom('data'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 65535,
'signed' => true,
'required' => false,
'default' => new \stdClass(),
'array' => false,
'filters' => ['json', 'encrypt'],
],
[
'$id' => ID::custom('active'),
'type' => Database::VAR_BOOLEAN,
@@ -4697,8 +4784,8 @@ $consoleCollections = array_merge([
'format' => '',
'size' => Database::LENGTH_KEY,
'signed' => true,
'required' => true,
'default' => null,
'required' => false,
'default' => [],
'array' => true,
'filters' => [],
],
@@ -5685,7 +5772,7 @@ $bucketCollections = [
'$id' => ID::custom('metadata'),
'type' => Database::VAR_STRING,
'format' => '',
'size' => 16384, // https://tools.ietf.org/html/rfc4288#section-4.2
'size' => 75000, // https://tools.ietf.org/html/rfc4288#section-4.2
'signed' => true,
'required' => false,
'default' => null,
+21
View File
@@ -332,6 +332,11 @@ return [
'description' => 'API key and session used in the same request. Use either `setSession` or `setKey`. Learn about which authentication method to use in the SSR docs: https://appwrite.io/docs/products/auth/server-side-rendering',
'code' => 403,
],
Exception::API_KEY_EXPIRED => [
'name' => Exception::API_KEY_EXPIRED,
'description' => 'The dynamic API key has expired. Please don\'t use dynamic API keys for more than duration of the execution.',
'code' => 401,
],
/** Teams */
Exception::TEAM_NOT_FOUND => [
@@ -524,6 +529,11 @@ return [
'description' => 'Synchronous function execution timed out. Use asynchronous execution instead, or ensure the execution duration doesn\'t exceed 30 seconds.',
'code' => 408,
],
Exception::FUNCTION_TEMPLATE_NOT_FOUND => [
'name' => Exception::FUNCTION_TEMPLATE_NOT_FOUND,
'description' => 'Function Template with the requested ID could not be found.',
'code' => 404,
],
/** Builds */
Exception::BUILD_NOT_FOUND => [
@@ -541,6 +551,11 @@ return [
'description' => 'Build with the requested ID is already in progress. Please wait before you can retry.',
'code' => 400,
],
Exception::BUILD_ALREADY_COMPLETED => [
'name' => Exception::BUILD_ALREADY_COMPLETED,
'description' => 'Build with the requested ID is already completed and cannot be canceled.',
'code' => 400,
],
/** Deployments */
Exception::DEPLOYMENT_NOT_FOUND => [
@@ -556,6 +571,12 @@ return [
'code' => 404,
],
Exception::EXECUTION_IN_PROGRESS => [
'name' => Exception::EXECUTION_IN_PROGRESS,
'description' => 'Can\'t delete ongoing execution. Please wait for execution to finish before deleting it.',
'code' => 400,
],
/** Databases */
Exception::DATABASE_NOT_FOUND => [
'name' => Exception::DATABASE_NOT_FOUND,
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -6,7 +6,9 @@ return [
'magicSession',
'recovery',
'invitation',
'mfaChallenge'
'mfaChallenge',
'sessionAlert',
'otpSession'
],
'sms' => [
'verification',
@@ -0,0 +1,14 @@
<p>{{hello}},</p>
<p>{{body}}</p>
<ol>
<li>{{listDevice}}</li>
<li>{{listIpAddress}}</li>
<li>{{listCountry}}</li>
</ol>
<p>{{footer}}</p>
<p style="margin-bottom: 0px;">{{thanks}}</p>
<p style="margin-top: 0px;">{{signature}}</p>
+10 -1
View File
@@ -18,6 +18,15 @@
"emails.magicSession.securityPhrase": "Security phrase for this email is {{b}}{{phrase}}{{/b}}. You can trust this email if this phrase matches the phrase shown during sign in.",
"emails.magicSession.thanks": "Thanks,",
"emails.magicSession.signature": "{{project}} team",
"emails.sessionAlert.subject": "Security alert: new session on your {{project}} account",
"emails.sessionAlert.hello":"Hello {{user}}",
"emails.sessionAlert.body": "A new session has been created on your {{b}}{{project}}{{/b}} account, on {{b}}{{dateTime}}{{/b}}.\nHere are the details of the new session: ",
"emails.sessionAlert.listDevice": "Device: {{b}}{{device}}{{/b}}",
"emails.sessionAlert.listIpAddress": "IP Address: {{b}}{{ipAddress}}{{/b}}",
"emails.sessionAlert.listCountry": "Country: {{b}}{{country}}{{/b}}",
"emails.sessionAlert.footer": "If this was you, there's nothing more you need to do.\nIf you didn't initiate this session or suspect any unauthorized activity, please secure your account.",
"emails.sessionAlert.thanks": "Thanks,",
"emails.sessionAlert.signature": "{{project}} team",
"emails.otpSession.subject": "OTP for {{project}} Login",
"emails.otpSession.hello": "Hello {{user}}",
"emails.otpSession.description": "Enter the following verification code when prompted to securely sign in to your {{b}}{{project}}{{/b}} account. This code will expire in 15 minutes.",
@@ -34,7 +43,7 @@
"emails.recovery.subject": "Password Reset",
"emails.recovery.hello": "Hello {{user}}",
"emails.recovery.body": "Follow this link to reset your {{b}}{{project}}{{/b}} password.",
"emails.recovery.footer": "If you didnt ask to reset your password, you can ignore this message.",
"emails.recovery.footer": "If you didn't ask to reset your password, you can ignore this message.",
"emails.recovery.thanks": "Thanks",
"emails.recovery.signature": "{{project}} team",
"emails.invitation.subject": "Invitation to %s Team at %s",
+7 -7
View File
@@ -15,7 +15,7 @@ return [
[
'key' => 'web',
'name' => 'Web',
'version' => '15.0.0',
'version' => '16.0.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-web',
'package' => 'https://www.npmjs.com/package/appwrite',
'enabled' => true,
@@ -63,7 +63,7 @@ return [
[
'key' => 'flutter',
'name' => 'Flutter',
'version' => '12.0.4',
'version' => '13.0.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-flutter',
'package' => 'https://pub.dev/packages/appwrite',
'enabled' => true,
@@ -221,7 +221,7 @@ return [
[
'key' => 'cli',
'name' => 'Command Line',
'version' => '5.0.5',
'version' => '6.0.0-rc.4',
'url' => 'https://github.com/appwrite/sdk-for-cli',
'package' => 'https://www.npmjs.com/package/appwrite-cli',
'enabled' => true,
@@ -249,7 +249,7 @@ return [
[
'key' => 'nodejs',
'name' => 'Node.js',
'version' => '13.0.0',
'version' => '14.0.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-node',
'package' => 'https://www.npmjs.com/package/node-appwrite',
'enabled' => true,
@@ -341,8 +341,8 @@ return [
'name' => 'Go',
'version' => '4.0.1',
'url' => 'https://github.com/appwrite/sdk-for-go',
'package' => '',
'enabled' => false,
'package' => 'https://github.com/appwrite/sdk-for-go',
'enabled' => true,
'beta' => true,
'dev' => false,
'hidden' => false,
@@ -393,7 +393,7 @@ return [
[
'key' => 'dart',
'name' => 'Dart',
'version' => '11.0.3',
'version' => '12.0.0-rc.1',
'url' => 'https://github.com/appwrite/sdk-for-dart',
'package' => 'https://pub.dev/packages/dart_appwrite',
'enabled' => true,
+1 -1
View File
@@ -6,4 +6,4 @@
use Appwrite\Runtimes\Runtimes;
return (new Runtimes('v3'))->getAll();
return (new Runtimes('v4'))->getAll();
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+31 -4
View File
@@ -144,8 +144,17 @@ return [
],
[
'name' => '_APP_SYSTEM_EMAIL_ADDRESS',
'description' => 'This is the sender email address that will appear on email messages sent to developers from the Appwrite console. The default value is \'team@appwrite.io\'. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users\' SPAM folders.',
'description' => 'This is the sender email address that will appear on email messages sent to developers from the Appwrite console. The default value is \'noreply@appwrite.io\'. You should choose an email address that is allowed to be used from your SMTP server to avoid the server email ending in the users\' SPAM folders.',
'introduction' => '0.7.0',
'default' => 'noreply@appwrite.io',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_SYSTEM_TEAM_EMAIL',
'description' => 'This is the sender email address that will appear in the generated specs. The default value is \'team@appwrite.io\'.',
'introduction' => '1.6.0',
'default' => 'team@appwrite.io',
'required' => false,
'question' => '',
@@ -198,7 +207,7 @@ return [
],
[
'name' => '_APP_LOGGING_PROVIDER',
'description' => 'This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.',
'description' => 'Deprecated since 1.6.0, use `_APP_LOGGING_CONFIG` with DSN value instead. This variable allows you to enable logging errors to 3rd party providers. This value is empty by default, set the value to one of \'sentry\', \'raygun\', \'appSignal\', \'logOwl\' to enable the logger.',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
@@ -207,7 +216,7 @@ return [
],
[
'name' => '_APP_LOGGING_CONFIG',
'description' => 'This variable configures authentication to 3rd party error logging providers. If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.',
'description' => 'This variable allows you to enable logging errors to third party providers. This value is empty by default, set a DSN value to one of the following `sentry://PROJECT_ID:SENTRY_API_KEY@SENTRY_HOST/`, , `logowl://SERVICE_TICKET@SERIVCE_HOST/` `raygun://RAYGUN_API_KEY/`, `appSignal://API_KEY/` to enable the logger.\n\nFor versions prior `1.5.6` you can use the old syntax.\n\nOld syntax: If using Sentry, this should be \'SENTRY_API_KEY;SENTRY_APP_ID\'. If using Raygun, this should be Raygun API key. If using AppSignal, this should be AppSignal API key. If using LogOwl, this should be LogOwl Service Ticket.',
'introduction' => '0.12.0',
'default' => '',
'required' => false,
@@ -250,6 +259,15 @@ return [
'question' => '',
'filter' => ''
],
[
'name' => '_APP_CONSOLE_SESSION_ALERTS',
'description' => 'This option allows you configure if a new login in the Appwrite Console should send an alert email to the user. It\'s disabled by default with value "disabled", and to enable it, pass value "enabled".',
'introduction' => '1.6.0',
'default' => 'disabled',
'required' => false,
'question' => '',
'filter' => ''
],
],
],
[
@@ -702,13 +720,22 @@ return [
'variables' => [
[
'name' => '_APP_FUNCTIONS_SIZE_LIMIT',
'description' => 'The maximum size deployment in bytes. The default value is 30MB.',
'description' => 'The maximum size of a function in bytes. The default value is 30MB.',
'introduction' => '0.13.0',
'default' => '30000000',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_BUILD_SIZE_LIMIT',
'description' => 'The maximum size of a built deployment in bytes. The default value is 2,000,000,000 (2GB), and the maximum value is 4,294,967,295 (4.2GB).',
'introduction' => '1.6.0',
'default' => '2000000000',
'required' => false,
'question' => '',
'filter' => ''
],
[
'name' => '_APP_FUNCTIONS_TIMEOUT',
'description' => 'The maximum number of seconds allowed as a timeout value when creating a new function. The default value is 900 seconds. This is the global limit, timeout for individual functions are configured in the function\'s settings or in appwrite.json.',
Submodule app/console deleted from 0959b594b3
+205 -91
View File
@@ -55,10 +55,95 @@ use Utopia\Validator\Text;
use Utopia\Validator\URL;
use Utopia\Validator\WhiteList;
$oauthDefaultSuccess = '/auth/oauth2/success';
$oauthDefaultFailure = '/auth/oauth2/failure';
$oauthDefaultSuccess = '/console/auth/oauth2/success';
$oauthDefaultFailure = '/console/auth/oauth2/failure';
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents) {
function sendSessionAlert(Locale $locale, Document $user, Document $project, Document $session, Mail $queueForMails)
{
$subject = $locale->getText("emails.sessionAlert.subject");
$customTemplate = $project->getAttribute('templates', [])['email.sessionAlert-' . $locale->default] ?? [];
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/email-session-alert.tpl');
$message
->setParam('{{hello}}', $locale->getText("emails.sessionAlert.hello"))
->setParam('{{body}}', $locale->getText("emails.sessionAlert.body"))
->setParam('{{listDevice}}', $locale->getText("emails.sessionAlert.listDevice"))
->setParam('{{listIpAddress}}', $locale->getText("emails.sessionAlert.listIpAddress"))
->setParam('{{listCountry}}', $locale->getText("emails.sessionAlert.listCountry"))
->setParam('{{footer}}', $locale->getText("emails.sessionAlert.footer"))
->setParam('{{thanks}}', $locale->getText("emails.sessionAlert.thanks"))
->setParam('{{signature}}', $locale->getText("emails.sessionAlert.signature"));
$body = $message->render();
$smtp = $project->getAttribute('smtp', []);
$smtpEnabled = $smtp['enabled'] ?? false;
$senderEmail = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$senderName = System::getEnv('_APP_SYSTEM_EMAIL_NAME', APP_NAME . ' Server');
$replyTo = "";
if ($smtpEnabled) {
if (!empty($smtp['senderEmail'])) {
$senderEmail = $smtp['senderEmail'];
}
if (!empty($smtp['senderName'])) {
$senderName = $smtp['senderName'];
}
if (!empty($smtp['replyTo'])) {
$replyTo = $smtp['replyTo'];
}
$queueForMails
->setSmtpHost($smtp['host'] ?? '')
->setSmtpPort($smtp['port'] ?? '')
->setSmtpUsername($smtp['username'] ?? '')
->setSmtpPassword($smtp['password'] ?? '')
->setSmtpSecure($smtp['secure'] ?? '');
if (!empty($customTemplate)) {
if (!empty($customTemplate['senderEmail'])) {
$senderEmail = $customTemplate['senderEmail'];
}
if (!empty($customTemplate['senderName'])) {
$senderName = $customTemplate['senderName'];
}
if (!empty($customTemplate['replyTo'])) {
$replyTo = $customTemplate['replyTo'];
}
$body = $customTemplate['message'] ?? '';
$subject = $customTemplate['subject'] ?? $subject;
}
$queueForMails
->setSmtpReplyTo($replyTo)
->setSmtpSenderEmail($senderEmail)
->setSmtpSenderName($senderName);
}
$emailVariables = [
'direction' => $locale->getText('settings.direction'),
'dateTime' => DateTime::format(new \DateTime(), 'h:ia MMMM dS'),
'user' => $user->getAttribute('name'),
'project' => $project->getAttribute('name'),
'device' => $session->getAttribute('clientName'),
'ipAddress' => $session->getAttribute('ip'),
'country' => $locale->getText('countries.' . $session->getAttribute('countryCode'), $locale->getText('locale.country.unknown')),
];
$email = $user->getAttribute('email');
$queueForMails
->setSubject($subject)
->setBody($body)
->setVariables($emailVariables)
->setRecipient($email)
->trigger();
};
$createSession = function (string $userId, string $secret, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails) {
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
@@ -92,6 +177,12 @@ $createSession = function (string $userId, string $secret, Request $request, Res
default => throw new Exception(Exception::USER_INVALID_TOKEN)
});
$sendAlert = (match ($verifiedToken->getAttribute('type')) {
Auth::TOKEN_TYPE_MAGIC_URL,
Auth::TOKEN_TYPE_EMAIL => false,
default => true
});
$session = new Document(array_merge(
[
'$id' => ID::unique(),
@@ -138,6 +229,14 @@ $createSession = function (string $userId, string $secret, Request $request, Res
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed saving user to DB');
}
if (($project->getAttribute('auths', [])['sessionAlerts'] ?? false) && $sendAlert) {
if ($dbForProject->count('sessions', [
Query::equal('userId', [$user->getId()]),
]) !== 1) {
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
}
}
$queueForEvents
->setParam('userId', $user->getId())
->setParam('sessionId', $session->getId());
@@ -290,7 +389,7 @@ App::post('/v1/account')
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -719,8 +818,9 @@ App::post('/v1/account/sessions/email')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->inject('hooks')
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Hooks $hooks) {
->action(function (string $email, string $password, Request $request, Response $response, Document $user, Database $dbForProject, Document $project, Locale $locale, Reader $geodb, Event $queueForEvents, Mail $queueForMails, Hooks $hooks) {
$email = \strtolower($email);
$protocol = $request->getProtocol();
@@ -813,6 +913,14 @@ App::post('/v1/account/sessions/email')
->setParam('sessionId', $session->getId())
;
if ($project->getAttribute('auths', [])['sessionAlerts'] ?? false) {
if ($dbForProject->count('sessions', [
Query::equal('userId', [$user->getId()]),
]) !== 1) {
sendSessionAlert($locale, $user, $project, $session, $queueForMails);
}
}
$response->dynamic($session, Response::MODEL_SESSION);
});
@@ -981,6 +1089,7 @@ App::post('/v1/account/sessions/token')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::get('/v1/account/sessions/oauth2/:provider')
@@ -1765,7 +1874,7 @@ App::post('/v1/account/tokens/magic-url')
$dbForProject->purgeCachedDocument('users', $user->getId());
if (empty($url)) {
$url = $request->getProtocol() . '://' . $request->getHostname() . '/auth/magic-url';
$url = $request->getProtocol() . '://' . $request->getHostname() . '/console/auth/magic-url';
}
$url = Template::parseURL($url);
@@ -2142,6 +2251,7 @@ App::put('/v1/account/sessions/magic-url')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::put('/v1/account/sessions/phone')
@@ -2172,6 +2282,7 @@ App::put('/v1/account/sessions/phone')
->inject('locale')
->inject('geodb')
->inject('queueForEvents')
->inject('queueForMails')
->action($createSession);
App::post('/v1/account/tokens/phone')
@@ -2274,7 +2385,18 @@ App::post('/v1/account/tokens/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
}
$secret = Auth::codeGenerator();
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
foreach ($mockNumbers as $mockNumber) {
if ($mockNumber['phone'] === $phone) {
$secret = $mockNumber['otp'];
$sendSMS = false;
break;
}
}
$secret ??= Auth::codeGenerator();
$expire = DateTime::formatTz(DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_OTP));
$token = new Document([
@@ -2299,35 +2421,37 @@ App::post('/v1/account/tokens/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
if ($sendSMS) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.login-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
$customTemplate = $project->getAttribute('templates', [])['sms.login-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $token->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $token->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$phone])
->setProviderType(MESSAGE_TYPE_SMS);
// Set to unhashed secret for events and server responses
$token->setAttribute('secret', $secret);
@@ -2342,7 +2466,8 @@ App::post('/v1/account/tokens/phone')
->dynamic($token, Response::MODEL_TOKEN);
});
App::post('/v1/account/jwt')
App::post('/v1/account/jwts')
->alias('/v1/account/jwt')
->desc('Create JWT')
->groups(['api', 'account', 'auth'])
->label('scope', 'account')
@@ -2375,15 +2500,11 @@ App::post('/v1/account/jwt')
throw new Exception(Exception::USER_SESSION_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
// 'uid' => 1,
// 'aud' => 'http://site.com',
// 'scopes' => ['user'],
// 'iss' => 'http://api.mysite.com',
'userId' => $user->getId(),
'sessionId' => $current->getId(),
])]), Response::MODEL_JWT);
@@ -3347,7 +3468,8 @@ App::post('/v1/account/verification/phone')
throw new Exception(Exception::GENERAL_PHONE_DISABLED, 'Phone provider not configured');
}
if (empty($user->getAttribute('phone'))) {
$phone = $user->getAttribute('phone');
if (empty($phone)) {
throw new Exception(Exception::USER_PHONE_NOT_FOUND);
}
@@ -3358,7 +3480,19 @@ App::post('/v1/account/verification/phone')
$roles = Authorization::getRoles();
$isPrivilegedUser = Auth::isPrivilegedUser($roles);
$isAppUser = Auth::isAppUser($roles);
$secret = Auth::codeGenerator();
$secret = null;
$sendSMS = true;
$mockNumbers = $project->getAttribute('auths', [])['mockNumbers'] ?? [];
foreach ($mockNumbers as $mockNumber) {
if ($mockNumber['phone'] === $phone) {
$secret = $mockNumber['otp'];
$sendSMS = false;
break;
}
}
$secret ??= Auth::codeGenerator();
$expire = DateTime::addSeconds(new \DateTime(), Auth::TOKEN_EXPIRATION_CONFIRM);
$verification = new Document([
@@ -3383,35 +3517,37 @@ App::post('/v1/account/verification/phone')
$dbForProject->purgeCachedDocument('users', $user->getId());
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
if ($sendSMS) {
$message = Template::fromFile(__DIR__ . '/../../config/locale/templates/sms-base.tpl');
$customTemplate = $project->getAttribute('templates', [])['sms.verification-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
$customTemplate = $project->getAttribute('templates', [])['sms.verification-' . $locale->default] ?? [];
if (!empty($customTemplate)) {
$message = $customTemplate['message'] ?? $message;
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $verification->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
}
$messageContent = Template::fromString($locale->getText("sms.verification.body"));
$messageContent
->setParam('{{project}}', $project->getAttribute('name'))
->setParam('{{secret}}', $secret);
$messageContent = \strip_tags($messageContent->render());
$message = $message->setParam('{{token}}', $messageContent);
$message = $message->render();
$messageDoc = new Document([
'$id' => $verification->getId(),
'data' => [
'content' => $message,
],
]);
$queueForMessaging
->setType(MESSAGE_SEND_TYPE_INTERNAL)
->setMessage($messageDoc)
->setRecipients([$user->getAttribute('phone')])
->setProviderType(MESSAGE_TYPE_SMS);
// Set to unhashed secret for events and server responses
$verification->setAttribute('secret', $secret);
@@ -3825,7 +3961,7 @@ App::get('/v1/account/mfa/recovery-codes')
App::delete('/v1/account/mfa/authenticators/:type')
->desc('Delete Authenticator')
->groups(['api', 'account'])
->groups(['api', 'account', 'mfaProtected'])
->label('event', 'users.[userId].delete.mfa')
->label('scope', 'account')
->label('audits.event', 'user.update')
@@ -3838,12 +3974,11 @@ App::delete('/v1/account/mfa/authenticators/:type')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('type', null, new WhiteList([Type::TOTP]), 'Type of authenticator.')
->param('otp', '', new Text(256), 'Valid verification token.')
->inject('response')
->inject('user')
->inject('dbForProject')
->inject('queueForEvents')
->action(function (string $type, string $otp, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
->action(function (string $type, Response $response, Document $user, Database $dbForProject, Event $queueForEvents) {
$authenticator = (match ($type) {
Type::TOTP => TOTP::getAuthenticatorFromUser($user),
@@ -3854,27 +3989,6 @@ App::delete('/v1/account/mfa/authenticators/:type')
throw new Exception(Exception::USER_AUTHENTICATOR_NOT_FOUND);
}
$success = (match ($type) {
Type::TOTP => Challenge\TOTP::verify($user, $otp),
default => false
});
if (!$success) {
$mfaRecoveryCodes = $user->getAttribute('mfaRecoveryCodes', []);
if (in_array($otp, $mfaRecoveryCodes)) {
$mfaRecoveryCodes = array_diff($mfaRecoveryCodes, [$otp]);
$mfaRecoveryCodes = array_values($mfaRecoveryCodes);
$user->setAttribute('mfaRecoveryCodes', $mfaRecoveryCodes);
$dbForProject->updateDocument('users', $user->getId(), $user);
$success = true;
}
}
if (!$success) {
throw new Exception(Exception::USER_INVALID_TOKEN);
}
$dbForProject->deleteDocument('authenticators', $authenticator->getId());
$dbForProject->purgeCachedDocument('users', $user->getId());
+396 -63
View File
@@ -10,7 +10,9 @@ use Appwrite\Event\Usage;
use Appwrite\Event\Validator\FunctionEvent;
use Appwrite\Extend\Exception;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Functions\Validator\Headers;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Platform\Tasks\ScheduleExecutions;
use Appwrite\Task\Validator\Cron;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Deployments;
@@ -33,6 +35,7 @@ use Utopia\Database\Helpers\Permission;
use Utopia\Database\Helpers\Role;
use Utopia\Database\Query;
use Utopia\Database\Validator\Authorization;
use Utopia\Database\Validator\Datetime as DatetimeValidator;
use Utopia\Database\Validator\Roles;
use Utopia\Database\Validator\UID;
use Utopia\Storage\Device;
@@ -42,9 +45,11 @@ use Utopia\Storage\Validator\FileSize;
use Utopia\Storage\Validator\Upload;
use Utopia\Swoole\Request;
use Utopia\System\System;
use Utopia\Validator\AnyOf;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
use Utopia\Validator\Nullable;
use Utopia\Validator\Range;
use Utopia\Validator\Text;
use Utopia\Validator\WhiteList;
@@ -151,6 +156,7 @@ App::post('/v1/functions')
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Control System) deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function.', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function.', true)
@@ -159,7 +165,7 @@ App::post('/v1/functions')
->param('templateRepository', '', new Text(128, 0), 'Repository name of the template.', true)
->param('templateOwner', '', new Text(128, 0), 'The name of the owner of the template.', true)
->param('templateRootDirectory', '', new Text(128, 0), 'Path to function code in the template repo.', true)
->param('templateBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function template.', true)
->param('templateVersion', '', new Text(128, 0), 'Version (tag) for the repo linked to the function template.', true)
->inject('request')
->inject('response')
->inject('dbForProject')
@@ -169,7 +175,7 @@ App::post('/v1/functions')
->inject('queueForBuilds')
->inject('dbForConsole')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateBranch, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, string $templateRepository, string $templateOwner, string $templateRootDirectory, string $templateVersion, Request $request, Response $response, Database $dbForProject, Document $project, Document $user, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
$functionId = ($functionId == 'unique()') ? ID::unique() : $functionId;
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
@@ -184,12 +190,12 @@ App::post('/v1/functions')
!empty($templateRepository)
&& !empty($templateOwner)
&& !empty($templateRootDirectory)
&& !empty($templateBranch)
&& !empty($templateVersion)
) {
$template->setAttribute('repositoryName', $templateRepository)
->setAttribute('ownerName', $templateOwner)
->setAttribute('rootDirectory', $templateRootDirectory)
->setAttribute('branch', $templateBranch);
->setAttribute('version', $templateVersion);
}
$installation = $dbForConsole->getDocument('installations', $installationId);
@@ -219,8 +225,9 @@ App::post('/v1/functions')
'timeout' => $timeout,
'entrypoint' => $entrypoint,
'commands' => $commands,
'scopes' => $scopes,
'search' => implode(' ', [$functionId, $name, $runtime]),
'version' => 'v3',
'version' => 'v4',
'installationId' => $installation->getId(),
'installationInternalId' => $installation->getInternalId(),
'providerRepositoryId' => $providerRepositoryId,
@@ -277,9 +284,34 @@ App::post('/v1/functions')
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
// Redeploy vcs logic
if (!empty($providerRepositoryId)) {
// Deploy VCS
$redeployVcs($request, $function, $project, $installation, $dbForProject, $queueForBuilds, $template, $github);
} elseif (!$template->isEmpty()) {
// Deploy non-VCS from template
$deploymentId = ID::unique();
$deployment = $dbForProject->createDocument('deployments', new Document([
'$id' => $deploymentId,
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'resourceId' => $function->getId(),
'resourceInternalId' => $function->getInternalId(),
'resourceType' => 'functions',
'entrypoint' => $function->getAttribute('entrypoint', ''),
'commands' => $function->getAttribute('commands', ''),
'type' => 'manual',
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint', '')]),
'activate' => true,
]));
$queueForBuilds
->setType(BUILD_TYPE_DEPLOYMENT)
->setResource($function)
->setDeployment($deployment)
->setTemplate($template);
}
$functionsDomain = System::getEnv('_APP_DOMAIN_FUNCTIONS', '');
@@ -427,13 +459,13 @@ App::get('/v1/functions/runtimes')
$allowList = \array_filter(\explode(',', System::getEnv('_APP_FUNCTIONS_RUNTIMES', '')));
$allowed = [];
foreach ($runtimes as $key => $runtime) {
if (!empty($allowList) && !\in_array($key, $allowList)) {
foreach ($runtimes as $id => $runtime) {
if (!empty($allowList) && !\in_array($id, $allowList)) {
continue;
}
$runtimes[$key]['$id'] = $key;
$allowed[] = $runtimes[$key];
$runtime['$id'] = $id;
$allowed[] = $runtime;
}
$response->dynamic(new Document([
@@ -694,8 +726,9 @@ App::put('/v1/functions/:functionId')
->param('logging', true, new Boolean(), 'Whether executions will be logged. When set to false, executions will not be logged, but will reduce resource used by your Appwrite project.', true)
->param('entrypoint', '', new Text(1028, 0), 'Entrypoint File. This path is relative to the "providerRootDirectory".', true)
->param('commands', '', new Text(8192, 0), 'Build Commands.', true)
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for API Key auto-generated for every execution. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.', true)
->param('installationId', '', new Text(128, 0), 'Appwrite Installation ID for VCS (Version Controle System) deployment.', true)
->param('providerRepositoryId', '', new Text(128, 0), 'Repository ID of the repo linked to the function', true)
->param('providerRepositoryId', null, new Nullable(new Text(128, 0)), 'Repository ID of the repo linked to the function', true)
->param('providerBranch', '', new Text(128, 0), 'Production branch for the repo linked to the function', true)
->param('providerSilentMode', false, new Boolean(), 'Is the VCS (Version Control System) connection in silent mode for the repo linked to the function? In silent mode, comments will not be made on commits and pull requests.', true)
->param('providerRootDirectory', '', new Text(128, 0), 'Path to function code in the linked repo.', true)
@@ -707,7 +740,7 @@ App::put('/v1/functions/:functionId')
->inject('queueForBuilds')
->inject('dbForConsole')
->inject('gitHub')
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, string $installationId, string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
->action(function (string $functionId, string $name, string $runtime, array $execute, array $events, string $schedule, int $timeout, bool $enabled, bool $logging, string $entrypoint, string $commands, array $scopes, string $installationId, ?string $providerRepositoryId, string $providerBranch, bool $providerSilentMode, string $providerRootDirectory, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Database $dbForConsole, GitHub $github) use ($redeployVcs) {
// TODO: If only branch changes, re-deploy
$function = $dbForProject->getDocument('functions', $functionId);
@@ -745,8 +778,8 @@ App::put('/v1/functions/:functionId')
$isConnected = !empty($function->getAttribute('providerRepositoryId', ''));
// Git disconnect logic
if ($isConnected && empty($providerRepositoryId)) {
// Git disconnect logic. Disconnecting only when providerRepositoryId is empty, allowing for continue updates without disconnecting git
if ($isConnected && ($providerRepositoryId !== null && empty($providerRepositoryId))) {
$repositories = $dbForConsole->find('repositories', [
Query::equal('projectInternalId', [$project->getInternalId()]),
Query::equal('resourceInternalId', [$function->getInternalId()]),
@@ -819,6 +852,7 @@ App::put('/v1/functions/:functionId')
'logging' => $logging,
'entrypoint' => $entrypoint,
'commands' => $commands,
'scopes' => $scopes,
'installationId' => $installation->getId(),
'installationInternalId' => $installation->getInternalId(),
'providerRepositoryId' => $providerRepositoryId,
@@ -852,10 +886,10 @@ App::get('/v1/functions/:functionId/deployments/:deploymentId/download')
->groups(['api', 'functions'])
->desc('Download deployment')
->label('scope', 'functions.read')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.auth', [APP_AUTH_TYPE_SESSION, APP_AUTH_TYPE_KEY, APP_AUTH_TYPE_JWT])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'downloadDeployment')
->label('sdk.description', '/docs/references/functions/download-deployment.md')
->label('sdk.method', 'getDeploymentDownload')
->label('sdk.description', '/docs/references/functions/get-deployment-download.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', '*/*')
->label('sdk.methodType', 'location')
@@ -1071,9 +1105,9 @@ App::post('/v1/functions/:functionId/deployments')
->inject('deviceForFunctions')
->inject('deviceForLocal')
->inject('queueForBuilds')
->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, bool $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) {
->action(function (string $functionId, ?string $entrypoint, ?string $commands, mixed $code, mixed $activate, Request $request, Response $response, Database $dbForProject, Event $queueForEvents, Document $project, Device $deviceForFunctions, Device $deviceForLocal, Build $queueForBuilds) {
$activate = filter_var($activate, FILTER_VALIDATE_BOOLEAN);
$activate = \strval($activate) === 'true' || \strval($activate) === '1';
$function = $dbForProject->getDocument('functions', $functionId);
@@ -1171,7 +1205,7 @@ App::post('/v1/functions/:functionId/deployments')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed moving file');
}
$activate = (bool) filter_var($activate, FILTER_VALIDATE_BOOLEAN);
$type = $request->getHeader('x-sdk-language') === 'cli' ? 'cli' : 'manual';
if ($chunksUploaded === $chunks) {
if ($activate) {
@@ -1209,7 +1243,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => 'manual'
'type' => $type
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('size', $fileSize)->setAttribute('metadata', $metadata));
@@ -1242,7 +1276,7 @@ App::post('/v1/functions/:functionId/deployments')
'search' => implode(' ', [$deploymentId, $entrypoint]),
'activate' => $activate,
'metadata' => $metadata,
'type' => 'manual'
'type' => $type
]));
} else {
$deployment = $dbForProject->updateDocument('deployments', $deploymentId, $deployment->setAttribute('chunksUploaded', $chunksUploaded)->setAttribute('metadata', $metadata));
@@ -1442,9 +1476,10 @@ App::delete('/v1/functions/:functionId/deployments/:deploymentId')
$response->noContent();
});
App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
App::post('/v1/functions/:functionId/deployments/:deploymentId/build')
->alias('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->groups(['api', 'functions'])
->desc('Create build')
->desc('Rebuild deployment')
->label('scope', 'functions.write')
->label('event', 'functions.[functionId].deployments.[deploymentId].update')
->label('audits.event', 'deployment.update')
@@ -1452,45 +1487,47 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'createBuild')
->label('sdk.description', '/docs/references/functions/create-build.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->param('buildId', '', new UID(), 'Build unique ID.')
->param('buildId', '', new UID(), 'Build unique ID.', true) // added as optional param for backward compatibility
->inject('request')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->inject('queueForBuilds')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds) {
->inject('deviceForFunctions')
->action(function (string $functionId, string $deploymentId, string $buildId, Request $request, Response $response, Database $dbForProject, Document $project, Event $queueForEvents, Build $queueForBuilds, Device $deviceForFunctions) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $buildId));
if ($build->isEmpty()) {
throw new Exception(Exception::BUILD_NOT_FOUND);
$path = $deployment->getAttribute('path');
if (empty($path) || !$deviceForFunctions->exists($path)) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$deploymentId = ID::unique();
$destination = $deviceForFunctions->getPath($deploymentId . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
$deviceForFunctions->transfer($path, $destination, $deviceForFunctions);
$deployment->removeAttribute('$internalId');
$deployment = $dbForProject->createDocument('deployments', $deployment->setAttributes([
'$internalId' => '',
'$id' => $deploymentId,
'buildId' => '',
'buildInternalId' => '',
'path' => $destination,
'entrypoint' => $function->getAttribute('entrypoint'),
'commands' => $function->getAttribute('commands', ''),
'search' => implode(' ', [$deploymentId, $function->getAttribute('entrypoint')]),
@@ -1508,6 +1545,86 @@ App::post('/v1/functions/:functionId/deployments/:deploymentId/builds/:buildId')
$response->noContent();
});
App::patch('/v1/functions/:functionId/deployments/:deploymentId/build')
->groups(['api', 'functions'])
->desc('Cancel deployment')
->label('scope', 'functions.write')
->label('audits.event', 'deployment.update')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'updateDeploymentBuild')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_BUILD)
->param('functionId', '', new UID(), 'Function ID.')
->param('deploymentId', '', new UID(), 'Deployment ID.')
->inject('response')
->inject('dbForProject')
->inject('project')
->inject('queueForEvents')
->action(function (string $functionId, string $deploymentId, Response $response, Database $dbForProject, Document $project, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$deployment = $dbForProject->getDocument('deployments', $deploymentId);
if ($deployment->isEmpty()) {
throw new Exception(Exception::DEPLOYMENT_NOT_FOUND);
}
$build = Authorization::skip(fn () => $dbForProject->getDocument('builds', $deployment->getAttribute('buildId', '')));
if ($build->isEmpty()) {
$buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
'$permissions' => [],
'startTime' => DateTime::now(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentId' => $deployment->getId(),
'status' => 'canceled',
'path' => '',
'runtime' => $function->getAttribute('runtime'),
'source' => $deployment->getAttribute('path', ''),
'sourceType' => '',
'logs' => '',
'duration' => 0,
'size' => 0
]));
$deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId());
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
} else {
if (\in_array($build->getAttribute('status'), ['ready', 'failed'])) {
throw new Exception(Exception::BUILD_ALREADY_COMPLETED);
}
$startTime = new \DateTime($build->getAttribute('startTime'));
$endTime = new \DateTime('now');
$duration = $endTime->getTimestamp() - $startTime->getTimestamp();
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttributes([
'endTime' => DateTime::now(),
'duration' => $duration,
'status' => 'canceled'
]));
}
$executor = new Executor(App::getEnv('_APP_EXECUTOR_HOST'));
$deleteBuild = $executor->deleteRuntime($project->getId(), $deploymentId . "-build");
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId());
$response->dynamic($build, Response::MODEL_BUILD);
});
App::post('/v1/functions/:functionId/executions')
->groups(['api', 'functions'])
->desc('Create execution')
@@ -1518,24 +1635,54 @@ App::post('/v1/functions/:functionId/executions')
->label('sdk.method', 'createExecution')
->label('sdk.description', '/docs/references/functions/create-execution.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.type', Response::CONTENT_TYPE_MULTIPART)
->label('sdk.response.model', Response::MODEL_EXECUTION)
->label('sdk.request.type', Response::CONTENT_TYPE_MULTIPART)
->param('functionId', '', new UID(), 'Function ID.')
->param('body', '', new Text(0, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('body', '', new Text(10485760, 0), 'HTTP body of execution. Default value is empty string.', true)
->param('async', false, new Boolean(), 'Execute code in the background. Default value is false.', true)
->param('path', '/', new Text(2048), 'HTTP path of execution. Path can include query params. Default value is /', true)
->param('method', 'POST', new Whitelist(['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'], true), 'HTTP method of execution. Default value is GET.', true)
->param('headers', [], new Assoc(), 'HTTP headers of execution. Defaults to empty.', true)
->param('headers', [], new AnyOf([new Assoc(), new Text(65535)], AnyOf::TYPE_MIXED), 'HTTP headers of execution. Defaults to empty.', true)
->param('scheduledAt', null, new DatetimeValidator(true, DateTimeValidator::PRECISION_MINUTES, 60), 'Scheduled execution time in [ISO 8601](https://www.iso.org/iso-8601-date-and-time-format.html) format. DateTime value must be in future with precision in minutes.', true)
->inject('response')
->inject('request')
->inject('project')
->inject('dbForProject')
->inject('dbForConsole')
->inject('user')
->inject('queueForEvents')
->inject('queueForUsage')
->inject('mode')
->inject('queueForFunctions')
->inject('geodb')
->action(function (string $functionId, string $body, bool $async, string $path, string $method, array $headers, Response $response, Document $project, Database $dbForProject, Document $user, Event $queueForEvents, Usage $queueForUsage, string $mode, Func $queueForFunctions, Reader $geodb) {
->action(function (string $functionId, string $body, bool $async, string $path, string $method, mixed $headers, ?string $scheduledAt, Response $response, Request $request, Document $project, Database $dbForProject, Database $dbForConsole, Document $user, Event $queueForEvents, Usage $queueForUsage, Func $queueForFunctions, Reader $geodb) {
if (!$async && !is_null($scheduledAt)) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Scheduled executions must run asynchronously. Set scheduledAt to a future date, or set async to true.');
}
/**
* @var array<string, mixed> $headers
*/
$assocParams = ['headers'];
foreach ($assocParams as $assocParam) {
if (!empty('headers') && !is_array($$assocParam)) {
$$assocParam = \json_decode($$assocParam, true);
}
}
$booleanParams = ['async'];
foreach ($booleanParams as $booleamParam) {
if (!empty($$booleamParam) && !is_bool($$booleamParam)) {
$$booleamParam = $$booleamParam === "true" ? true : false;
}
}
// 'headers' validator
$validator = new Headers();
if (!$validator->isValid($headers)) {
throw new Exception($validator->getDescription(), 400);
}
$function = Authorization::skip(fn () => $dbForProject->getDocument('functions', $functionId));
@@ -1594,7 +1741,8 @@ App::post('/v1/functions/:functionId/executions')
}
if (!$current->isEmpty()) {
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$jwt = $jwtObj->encode([
'userId' => $user->getId(),
'sessionId' => $current->getId(),
@@ -1602,6 +1750,14 @@ App::post('/v1/functions/:functionId/executions')
}
}
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
@@ -1631,6 +1787,12 @@ App::post('/v1/functions/:functionId/executions')
$executionId = ID::unique();
$status = $async ? 'waiting' : 'processing';
if (!is_null($scheduledAt)) {
$status = 'scheduled';
}
$execution = new Document([
'$id' => $executionId,
'$permissions' => !$user->isEmpty() ? [Permission::read(Role::user($user->getId()))] : [],
@@ -1638,8 +1800,8 @@ App::post('/v1/functions/:functionId/executions')
'functionId' => $function->getId(),
'deploymentInternalId' => $deployment->getInternalId(),
'deploymentId' => $deployment->getId(),
'trigger' => 'http', // http / schedule / event
'status' => $async ? 'waiting' : 'processing', // waiting / processing / completed / failed
'trigger' => (!is_null($scheduledAt)) ? 'schedule' : 'http',
'status' => $status, // waiting / processing / completed / failed / scheduled
'responseStatusCode' => 0,
'responseHeaders' => [],
'requestPath' => $path,
@@ -1657,26 +1819,51 @@ App::post('/v1/functions/:functionId/executions')
->setContext('function', $function);
if ($async) {
if ($function->getAttribute('logging')) {
/** @var Document $execution */
if (is_null($scheduledAt)) {
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
$queueForFunctions
->setType('http')
->setExecution($execution)
->setFunction($function)
->setBody($body)
->setHeaders($headers)
->setPath($path)
->setMethod($method)
->setJWT($jwt)
->setProject($project)
->setUser($user)
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->trigger();
} else {
$data = [
'headers' => $headers,
'path' => $path,
'method' => $method,
'body' => $body,
'jwt' => $jwt,
];
$schedule = $dbForConsole->createDocument('schedules', new Document([
'region' => System::getEnv('_APP_REGION', 'default'),
'resourceType' => ScheduleExecutions::getSupportedResource(),
'resourceId' => $execution->getId(),
'resourceInternalId' => $execution->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'data' => $data,
'active' => true,
]));
$execution = $execution
->setAttribute('scheduleId', $schedule->getId())
->setAttribute('scheduleInternalId', $schedule->getInternalId())
->setAttribute('scheduledAt', $scheduledAt);
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$queueForFunctions
->setType('http')
->setExecution($execution)
->setFunction($function)
->setBody($body)
->setHeaders($headers)
->setPath($path)
->setMethod($method)
->setJWT($jwt)
->setProject($project)
->setUser($user)
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->trigger();
return $response
->setStatusCode(Response::STATUS_CODE_ACCEPTED)
->dynamic($execution, Response::MODEL_EXECUTION);
@@ -1706,14 +1893,21 @@ App::post('/v1/functions/:functionId/executions')
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
@@ -1736,6 +1930,7 @@ App::post('/v1/functions/:functionId/executions')
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
@@ -1747,7 +1942,7 @@ App::post('/v1/functions/:functionId/executions')
}
/** Update execution status */
$status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed';
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
$execution->setAttribute('status', $status);
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
$execution->setAttribute('responseHeaders', $headersFiltered);
@@ -1777,10 +1972,7 @@ App::post('/v1/functions/:functionId/executions')
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_MB_SECONDS), (int)(512 * $execution->getAttribute('duration', 0)))
;
if ($function->getAttribute('logging')) {
/** @var Document $execution */
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$execution = Authorization::skip(fn () => $dbForProject->createDocument('executions', $execution));
}
$roles = Authorization::getRoles();
@@ -1800,6 +1992,17 @@ App::post('/v1/functions/:functionId/executions')
$execution->setAttribute('responseBody', $executionResponse['body'] ?? '');
$execution->setAttribute('responseHeaders', $headers);
$acceptTypes = \explode(', ', $request->getHeader('accept'));
foreach ($acceptTypes as $acceptType) {
if (\str_starts_with($acceptType, 'application/json') || \str_starts_with($acceptType, 'application/*')) {
$response->setContentType(Response::CONTENT_TYPE_JSON);
break;
} elseif (\str_starts_with($acceptType, 'multipart/form-data') || \str_starts_with($acceptType, 'multipart/*')) {
$response->setContentType(Response::CONTENT_TYPE_MULTIPART);
break;
}
}
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($execution, Response::MODEL_EXECUTION);
@@ -1933,6 +2136,74 @@ App::get('/v1/functions/:functionId/executions/:executionId')
$response->dynamic($execution, Response::MODEL_EXECUTION);
});
App::delete('/v1/functions/:functionId/executions/:executionId')
->groups(['api', 'functions'])
->desc('Delete execution')
->label('scope', 'execution.write')
->label('event', 'functions.[functionId].executions.[executionId].delete')
->label('audits.event', 'executions.delete')
->label('audits.resource', 'function/{request.functionId}')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'functions')
->label('sdk.method', 'deleteExecution')
->label('sdk.description', '/docs/references/functions/delete-execution.md')
->label('sdk.response.code', Response::STATUS_CODE_NOCONTENT)
->label('sdk.response.model', Response::MODEL_NONE)
->param('functionId', '', new UID(), 'Function ID.')
->param('executionId', '', new UID(), 'Execution ID.')
->inject('response')
->inject('dbForProject')
->inject('dbForConsole')
->inject('queueForEvents')
->action(function (string $functionId, string $executionId, Response $response, Database $dbForProject, Database $dbForConsole, Event $queueForEvents) {
$function = $dbForProject->getDocument('functions', $functionId);
if ($function->isEmpty()) {
throw new Exception(Exception::FUNCTION_NOT_FOUND);
}
$execution = $dbForProject->getDocument('executions', $executionId);
if ($execution->isEmpty()) {
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
if ($execution->getAttribute('functionId') !== $function->getId()) {
throw new Exception(Exception::EXECUTION_NOT_FOUND);
}
$status = $execution->getAttribute('status');
if (!in_array($status, ['completed', 'failed', 'scheduled'])) {
throw new Exception(Exception::EXECUTION_IN_PROGRESS);
}
if (!$dbForProject->deleteDocument('executions', $execution->getId())) {
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove execution from DB');
}
if ($status === 'scheduled') {
$schedule = $dbForConsole->findOne('schedules', [
Query::equal('resourceId', [$execution->getId()]),
Query::equal('resourceType', [ScheduleExecutions::getSupportedResource()]),
Query::equal('active', [true]),
]);
if ($schedule && !$schedule->isEmpty()) {
$schedule
->setAttribute('resourceUpdatedAt', DateTime::now())
->setAttribute('active', false);
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
}
}
$queueForEvents
->setParam('functionId', $function->getId())
->setParam('executionId', $execution->getId())
->setPayload($response->output($execution, Response::MODEL_EXECUTION));
$response->noContent();
});
// Variables
App::post('/v1/functions/:functionId/variables')
@@ -2173,3 +2444,65 @@ App::delete('/v1/functions/:functionId/variables/:variableId')
$response->noContent();
});
App::get('/v1/functions/templates')
->groups(['api'])
->desc('List function templates')
->label('scope', 'public')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'listTemplates')
->label('sdk.description', '/docs/references/functions/list-templates.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION_LIST)
->param('runtimes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('runtimes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of runtimes allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' runtimes are allowed.', true)
->param('useCases', [], new ArrayList(new WhiteList(['dev-tools','starter','databases','ai','messaging','utilities']), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of use cases allowed for filtering function templates. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' use cases are allowed.', true)
->param('limit', 25, new Range(1, 5000), 'Limit the number of templates returned in the response. Default limit is 25, and maximum limit is 5000.', true)
->param('offset', 0, new Range(0, 5000), 'Offset the list of returned templates. Maximum offset is 5000.', true)
->inject('response')
->action(function (array $runtimes, array $usecases, int $limit, int $offset, Response $response) {
$templates = Config::getParam('function-templates', []);
if (!empty($runtimes)) {
$templates = \array_filter($templates, function ($template) use ($runtimes) {
return \count(\array_intersect($runtimes, \array_column($template['runtimes'], 'name'))) > 0;
});
}
if (!empty($usecases)) {
$templates = \array_filter($templates, function ($template) use ($usecases) {
return \count(\array_intersect($usecases, $template['useCases'])) > 0;
});
}
$responseTemplates = \array_slice($templates, $offset, $limit);
$response->dynamic(new Document([
'templates' => $responseTemplates,
'total' => \count($responseTemplates),
]), Response::MODEL_TEMPLATE_FUNCTION_LIST);
});
App::get('/v1/functions/templates/:templateId')
->desc('Get function template')
->label('scope', 'public')
->label('sdk.namespace', 'functions')
->label('sdk.method', 'getTemplate')
->label('sdk.description', '/docs/references/functions/get-template.md')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_TEMPLATE_FUNCTION)
->param('templateId', '', new Text(128), 'Template ID.')
->inject('response')
->action(function (string $templateId, Response $response) {
$templates = Config::getParam('function-templates', []);
$template = array_shift(\array_filter($templates, function ($template) use ($templateId) {
return $template['id'] === $templateId;
}));
if (empty($template)) {
throw new Exception(Exception::FUNCTION_TEMPLATE_NOT_FOUND);
}
$response->dynamic(new Document($template), Response::MODEL_TEMPLATE_FUNCTION);
});
+5 -9
View File
@@ -2697,7 +2697,7 @@ App::post('/v1/messaging/messages/email')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -2813,7 +2813,7 @@ App::post('/v1/messaging/messages/sms')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -2939,11 +2939,9 @@ App::post('/v1/messaging/messages/push')
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
@@ -2991,7 +2989,7 @@ App::post('/v1/messaging/messages/push')
'resourceInternalId' => $message->getInternalId(),
'resourceUpdatedAt' => DateTime::now(),
'projectId' => $project->getId(),
'schedule' => $scheduledAt,
'schedule' => $scheduledAt,
'active' => true,
]));
@@ -3801,11 +3799,9 @@ App::patch('/v1/messaging/messages/push/:messageId')
$expiry = (new \DateTime())->add(new \DateInterval('P15D'))->format('U');
}
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$encoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', \intval($expiry), 0);
$jwt = $encoder->encode([
'iat' => \time(),
'exp' => $expiry,
'bucketId' => $bucket->getId(),
'fileId' => $file->getId(),
'projectId' => $project->getId(),
+117 -5
View File
@@ -1,6 +1,8 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Auth\Validator\MockNumber;
use Appwrite\Event\Delete;
use Appwrite\Event\Mail;
use Appwrite\Event\Validator\Event;
@@ -14,12 +16,13 @@ use Appwrite\Utopia\Database\Validator\Queries\Projects;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use PHPMailer\PHPMailer\PHPMailer;
use Utopia\Abuse\Adapters\Database\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\Cache\Cache;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
use Utopia\Database\Exception\Duplicate;
use Utopia\Database\Exception\Query as QueryException;
@@ -104,8 +107,11 @@ App::post('/v1/projects')
'passwordHistory' => 0,
'passwordDictionary' => false,
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG,
'personalDataCheck' => false
'personalDataCheck' => false,
'mockNumbers' => [],
'sessionAlerts' => false,
];
foreach ($auth as $method) {
$auths[$method['key'] ?? ''] = true;
}
@@ -168,6 +174,7 @@ App::post('/v1/projects')
'webhooks' => null,
'keys' => null,
'auths' => $auths,
'accessedAt' => DateTime::now(),
'search' => implode(' ', [$projectId, $name]),
'database' => $dsn,
]));
@@ -202,7 +209,7 @@ App::post('/v1/projects')
$audit = new Audit($dbForProject);
$audit->setup();
$abuse = new TimeLimit('', 0, 1, $dbForProject);
$abuse = new AbuseDatabase('', 0, 1, $dbForProject);
$abuse->setup();
/** @var array $collections */
@@ -362,7 +369,7 @@ App::patch('/v1/projects/:projectId')
});
App::patch('/v1/projects/:projectId/team')
->desc('Update Project Team')
->desc('Update project team')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
@@ -603,6 +610,37 @@ App::patch('/v1/projects/:projectId/oauth2')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/session-alerts')
->desc('Update project sessions emails')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateSessionAlerts')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('alerts', false, new Boolean(true), 'Set to true to enable session emails.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, bool $alerts, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['sessionAlerts'] = $alerts;
$dbForConsole->updateDocument('projects', $project->getId(), $project
->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/limit')
->desc('Update project users limit')
->groups(['api', 'projects'])
@@ -823,6 +861,45 @@ App::patch('/v1/projects/:projectId/auth/max-sessions')
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::patch('/v1/projects/:projectId/auth/mock-numbers')
->desc('Update the mock numbers for the project')
->groups(['api', 'projects'])
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'updateMockNumbers')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_PROJECT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('numbers', '', new ArrayList(new MockNumber(), 10), 'An array of mock numbers and their corresponding verification codes (OTPs). Each number should be a valid E.164 formatted phone number. Maximum of 10 numbers are allowed.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, array $numbers, Response $response, Database $dbForConsole) {
$uniqueNumbers = [];
foreach ($numbers as $number) {
if (isset($uniqueNumbers[$number['phone']])) {
throw new Exception(Exception::GENERAL_BAD_REQUEST, 'Duplicate phone numbers are not allowed.');
}
$uniqueNumbers[$number['phone']] = $number['otp'];
}
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$auths = $project->getAttribute('auths', []);
$auths['mockNumbers'] = $numbers;
$project = $dbForConsole->updateDocument('projects', $project->getId(), $project->setAttribute('auths', $auths));
$response->dynamic($project, Response::MODEL_PROJECT);
});
App::delete('/v1/projects/:projectId')
->desc('Delete project')
->groups(['api', 'projects'])
@@ -1157,7 +1234,7 @@ App::post('/v1/projects/:projectId/keys')
'expire' => $expire,
'sdks' => [],
'accessedAt' => null,
'secret' => \bin2hex(\random_bytes(128)),
'secret' => API_KEY_STANDARD . '_' . \bin2hex(\random_bytes(128)),
]);
$key = $dbForConsole->createDocument('keys', $key);
@@ -1318,6 +1395,41 @@ App::delete('/v1/projects/:projectId/keys/:keyId')
$response->noContent();
});
// JWT Keys
App::post('/v1/projects/:projectId/jwts')
->groups(['api', 'projects'])
->desc('Create JWT')
->label('scope', 'projects.write')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.namespace', 'projects')
->label('sdk.method', 'createJWT')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->param('projectId', '', new UID(), 'Project unique ID.')
->param('scopes', [], new ArrayList(new WhiteList(array_keys(Config::getParam('scopes')), true), APP_LIMIT_ARRAY_PARAMS_SIZE), 'List of scopes allowed for JWT key. Maximum of ' . APP_LIMIT_ARRAY_PARAMS_SIZE . ' scopes are allowed.')
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, array $scopes, int $duration, Response $response, Database $dbForConsole) {
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => API_KEY_DYNAMIC . '_' . $jwt->encode([
'projectId' => $project->getId(),
'scopes' => $scopes
])]), Response::MODEL_JWT);
});
// Platforms
App::post('/v1/projects/:projectId/platforms')
+2 -3
View File
@@ -1309,7 +1309,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
->action(function (string $bucketId, string $fileId, string $jwt, Response $response, Request $request, Database $dbForProject, Document $project, string $mode, Device $deviceForFiles) {
$bucket = Authorization::skip(fn () => $dbForProject->getDocument('buckets', $bucketId));
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'));
$decoder = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$decoded = $decoder->decode($jwt);
@@ -1320,8 +1320,7 @@ App::get('/v1/storage/buckets/:bucketId/files/:fileId/push')
if (
$decoded['projectId'] !== $project->getId() ||
$decoded['bucketId'] !== $bucketId ||
$decoded['fileId'] !== $fileId ||
$decoded['exp'] < \time()
$decoded['fileId'] !== $fileId
) {
throw new Exception(Exception::USER_UNAUTHORIZED);
}
+13 -5
View File
@@ -10,6 +10,7 @@ use Appwrite\Event\Mail;
use Appwrite\Event\Messaging;
use Appwrite\Extend\Exception;
use Appwrite\Network\Validator\Email;
use Appwrite\Platform\Workers\Deletes;
use Appwrite\Template\Template;
use Appwrite\Utopia\Database\Validator\CustomId;
use Appwrite\Utopia\Database\Validator\Queries\Memberships;
@@ -338,10 +339,12 @@ App::delete('/v1/teams/:teamId')
->label('sdk.response.model', Response::MODEL_NONE)
->param('teamId', '', new UID(), 'Team ID.')
->inject('response')
->inject('getProjectDB')
->inject('dbForProject')
->inject('queueForEvents')
->inject('queueForDeletes')
->action(function (string $teamId, Response $response, Database $dbForProject, Event $queueForEvents, Delete $queueForDeletes) {
->inject('queueForEvents')
->inject('project')
->action(function (string $teamId, Response $response, callable $getProjectDB, Database $dbForProject, Delete $queueForDeletes, Event $queueForEvents, Document $project) {
$team = $dbForProject->getDocument('teams', $teamId);
@@ -353,9 +356,14 @@ App::delete('/v1/teams/:teamId')
throw new Exception(Exception::GENERAL_SERVER_ERROR, 'Failed to remove team from DB');
}
$queueForDeletes
->setType(DELETE_TYPE_DOCUMENT)
->setDocument($team);
$deletes = new Deletes();
$deletes->deleteMemberships($getProjectDB, $team, $project);
if ($project->getId() === 'console') {
$queueForDeletes
->setType(DELETE_TYPE_TEAM_PROJECTS)
->setDocument($team);
}
$queueForEvents
->setParam('teamId', $team->getId())
+54 -2
View File
@@ -1,5 +1,6 @@
<?php
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Type;
use Appwrite\Auth\MFA\Type\TOTP;
@@ -39,6 +40,7 @@ use Utopia\Database\Validator\Query\Limit;
use Utopia\Database\Validator\Query\Offset;
use Utopia\Database\Validator\UID;
use Utopia\Locale\Locale;
use Utopia\System\System;
use Utopia\Validator\ArrayList;
use Utopia\Validator\Assoc;
use Utopia\Validator\Boolean;
@@ -138,7 +140,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$email]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -162,7 +164,7 @@ function createUser(string $hash, mixed $hashOptions, string $userId, ?string $e
$existingTarget = $dbForProject->findOne('targets', [
Query::equal('identifier', [$phone]),
]);
if($existingTarget) {
if ($existingTarget) {
$user->setAttribute('targets', $existingTarget, Document::SET_TYPE_APPEND);
}
}
@@ -2095,6 +2097,56 @@ App::delete('/v1/users/identities/:identityId')
return $response->noContent();
});
App::post('/v1/users/:userId/jwts')
->desc('Create user JWT')
->groups(['api', 'users'])
->label('scope', 'users.write')
->label('sdk.auth', [APP_AUTH_TYPE_KEY])
->label('sdk.namespace', 'users')
->label('sdk.method', 'createJWT')
->label('sdk.description', '/docs/references/users/create-user-jwt.md')
->label('sdk.response.code', Response::STATUS_CODE_CREATED)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_JWT)
->param('userId', '', new UID(), 'User ID.')
->param('sessionId', '', new UID(), 'Session ID. Use the string \'recent\' to use the most recent session. Defaults to the most recent session.', true)
->param('duration', 900, new Range(0, 3600), 'Time in seconds before JWT expires. Default duration is 900 seconds, and maximum is 3600 seconds.', true)
->inject('response')
->inject('dbForProject')
->action(function (string $userId, string $sessionId, int $duration, Response $response, Database $dbForProject) {
$user = $dbForProject->getDocument('users', $userId);
if ($user->isEmpty()) {
throw new Exception(Exception::USER_NOT_FOUND);
}
$sessions = $user->getAttribute('sessions', []);
$session = new Document();
if ($sessionId === 'recent') {
// Get most recent
$session = \count($sessions) > 0 ? $sessions[\count($sessions) - 1] : new Document();
} else {
// Find by ID
foreach ($sessions as $loopSession) { /** @var Utopia\Database\Document $loopSession */
if ($loopSession->getId() == $sessionId) {
$session = $loopSession;
break;
}
}
}
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $duration, 0);
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic(new Document(['jwt' => $jwt->encode([
'userId' => $user->getId(),
'sessionId' => $session->isEmpty() ? '' : $session->getId()
])]), Response::MODEL_JWT);
});
App::get('/v1/users/usage')
->desc('Get users usage stats')
->groups(['api', 'users'])
+64 -1
View File
@@ -96,7 +96,7 @@ $createGitDeployments = function (GitHub $github, string $providerInstallationId
$commentStatus = $isAuthorized ? 'waiting' : 'failed';
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
$authorizeUrl = $request->getProtocol() . '://' . $request->getHostname() . "/console/git/authorize-contributor?projectId={$projectId}&installationId={$installationId}&repositoryId={$repositoryId}&providerPullRequestId={$providerPullRequestId}";
$action = $isAuthorized ? ['type' => 'logs'] : ['type' => 'authorize', 'url' => $authorizeUrl];
@@ -464,6 +464,67 @@ App::get('/v1/vcs/github/callback')
->redirect($redirectSuccess);
});
App::get('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/contents')
->desc('Get files and directories of a VCS repository')
->groups(['api', 'vcs'])
->label('scope', 'vcs.read')
->label('sdk.namespace', 'vcs')
->label('sdk.auth', [APP_AUTH_TYPE_ADMIN])
->label('sdk.method', 'getRepositoryContents')
->label('sdk.description', '')
->label('sdk.response.code', Response::STATUS_CODE_OK)
->label('sdk.response.type', Response::CONTENT_TYPE_JSON)
->label('sdk.response.model', Response::MODEL_VCS_CONTENT_LIST)
->param('installationId', '', new Text(256), 'Installation Id')
->param('providerRepositoryId', '', new Text(256), 'Repository Id')
->param('providerRootDirectory', '', new Text(256, 0), 'Path to get contents of nested directory', true)
->inject('gitHub')
->inject('response')
->inject('project')
->inject('dbForConsole')
->action(function (string $installationId, string $providerRepositoryId, string $providerRootDirectory, GitHub $github, Response $response, Document $project, Database $dbForConsole) {
$installation = $dbForConsole->getDocument('installations', $installationId);
if ($installation->isEmpty()) {
throw new Exception(Exception::INSTALLATION_NOT_FOUND);
}
$providerInstallationId = $installation->getAttribute('providerInstallationId');
$privateKey = System::getEnv('_APP_VCS_GITHUB_PRIVATE_KEY');
$githubAppId = System::getEnv('_APP_VCS_GITHUB_APP_ID');
$github->initializeVariables($providerInstallationId, $privateKey, $githubAppId);
$owner = $github->getOwnerName($providerInstallationId);
try {
$repositoryName = $github->getRepositoryName($providerRepositoryId) ?? '';
if (empty($repositoryName)) {
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
}
} catch (RepositoryNotFound $e) {
throw new Exception(Exception::PROVIDER_REPOSITORY_NOT_FOUND);
}
$contents = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
$vcsContents = [];
foreach ($contents as $content) {
$isDirectory = false;
if ($content['type'] === GitHub::CONTENTS_DIRECTORY) {
$isDirectory = true;
}
$vcsContents[] = new Document([
'isDirectory' => $isDirectory,
'name' => $content['name'] ?? '',
'size' => $content['size'] ?? 0
]);
}
$response->dynamic(new Document([
'contents' => $vcsContents
]), Response::MODEL_VCS_CONTENT_LIST);
});
App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:providerRepositoryId/detection')
->desc('Detect runtime settings from source code')
->groups(['api', 'vcs'])
@@ -505,6 +566,7 @@ App::post('/v1/vcs/github/installations/:installationId/providerRepositories/:pr
}
$files = $github->listRepositoryContents($owner, $repositoryName, $providerRootDirectory);
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($owner, $repositoryName);
$detectorFactory = new Detector($files, $languages);
@@ -586,6 +648,7 @@ App::get('/v1/vcs/github/installations/:installationId/providerRepositories')
return function () use ($repo, $github) {
try {
$files = $github->listRepositoryContents($repo['organization'], $repo['name'], '');
$files = \array_column($files, 'name');
$languages = $github->listRepositoryLanguages($repo['organization'], $repo['name']);
$detectorFactory = new Detector($files, $languages);
+28 -10
View File
@@ -2,6 +2,7 @@
require_once __DIR__ . '/../init.php';
use Ahc\Jwt\JWT;
use Appwrite\Auth\Auth;
use Appwrite\Event\Certificate;
use Appwrite\Event\Event;
@@ -11,9 +12,11 @@ use Appwrite\Network\Validator\Origin;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Request\Filters\V16 as RequestV16;
use Appwrite\Utopia\Request\Filters\V17 as RequestV17;
use Appwrite\Utopia\Request\Filters\V18 as RequestV18;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filters\V16 as ResponseV16;
use Appwrite\Utopia\Response\Filters\V17 as ResponseV17;
use Appwrite\Utopia\Response\Filters\V18 as ResponseV18;
use Appwrite\Utopia\View;
use Executor\Executor;
use MaxMind\Db\Reader;
@@ -163,7 +166,15 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
throw new AppwriteException(AppwriteException::USER_UNAUTHORIZED, 'To execute function using domain, execute permissions must include "any" or "guests"');
}
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$headers = \array_merge([], $requestHeaders);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = 'http';
$headers['x-appwrite-user-id'] = '';
$headers['x-appwrite-user-jwt'] = '';
@@ -242,14 +253,21 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
@@ -272,6 +290,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
method: $method,
headers: $headers,
runtimeEntrypoint: $command,
logging: $function->getAttribute('logging', true),
requestTimeout: 30
);
@@ -283,7 +302,7 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
}
/** Update execution status */
$status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed';
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
$execution->setAttribute('status', $status);
$execution->setAttribute('responseStatusCode', $executionResponse['statusCode']);
$execution->setAttribute('responseHeaders', $headersFiltered);
@@ -335,13 +354,6 @@ function router(App $utopia, Database $dbForConsole, callable $getProjectDB, Swo
$body = $execution['responseBody'] ?? '';
$encodingKey = \array_search('x-open-runtimes-encoding', \array_column($execution['responseHeaders'], 'name'));
if ($encodingKey !== false) {
if (($execution['responseHeaders'][$encodingKey]['value'] ?? '') === 'base64') {
$body = \base64_decode($body);
}
}
$contentType = 'text/plain';
foreach ($execution['responseHeaders'] as $header) {
if (\strtolower($header['name']) === 'content-type') {
@@ -443,6 +455,9 @@ App::init()
if (version_compare($requestFormat, '1.5.0', '<')) {
$request->addFilter(new RequestV17());
}
if (version_compare($requestFormat, '1.6.0', '<')) {
$request->addFilter(new RequestV18());
}
}
$domain = $request->getHostname();
@@ -559,6 +574,9 @@ App::init()
if (version_compare($responseFormat, '1.5.0', '<')) {
$response->addFilter(new ResponseV17());
}
if (version_compare($responseFormat, '1.6.0', '<')) {
$response->addFilter(new ResponseV18());
}
if (version_compare($responseFormat, APP_VERSION_STABLE, '>')) {
$response->addHeader('X-Appwrite-Warning', "The current SDK is built for Appwrite " . $responseFormat . ". However, the current Appwrite server version is ". APP_VERSION_STABLE . ". Please downgrade your SDK to match the Appwrite version: https://appwrite.io/docs/sdks");
}
@@ -904,7 +922,7 @@ App::get('/robots.txt')
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($host === $mainDomain) {
if ($host === $mainDomain || $host === 'localhost') {
$template = new View(__DIR__ . '/../views/general/robots.phtml');
$response->text($template->render(false));
} else {
@@ -929,7 +947,7 @@ App::get('/humans.txt')
$host = $request->getHostname() ?? '';
$mainDomain = System::getEnv('_APP_DOMAIN', '');
if ($host === $mainDomain) {
if ($host === $mainDomain || $host === 'localhost') {
$template = new View(__DIR__ . '/../views/general/humans.phtml');
$response->text($template->render(false));
} else {
+50
View File
@@ -6,6 +6,7 @@ use Appwrite\Extend\Exception;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\App;
use Utopia\Config\Config;
use Utopia\Database\Database;
use Utopia\Database\Document;
use Utopia\Database\Helpers\ID;
@@ -154,6 +155,55 @@ App::patch('/v1/mock/functions-v2')
$response->noContent();
});
App::post('/v1/mock/api-key-unprefixed')
->desc('Create API Key (without standard prefix)')
->groups(['mock', 'api', 'projects'])
->label('scope', 'projects.write')
->label('docs', false)
->param('projectId', '', new UID(), 'Project ID.')
->inject('response')
->inject('dbForConsole')
->action(function (string $projectId, Response $response, Database $dbForConsole) {
$isDevelopment = System::getEnv('_APP_ENV', 'development') === 'development';
if (!$isDevelopment) {
throw new Exception(Exception::GENERAL_NOT_IMPLEMENTED);
}
$project = $dbForConsole->getDocument('projects', $projectId);
if ($project->isEmpty()) {
throw new Exception(Exception::PROJECT_NOT_FOUND);
}
$scopes = array_keys(Config::getParam('scopes'));
$key = new Document([
'$id' => ID::unique(),
'$permissions' => [
Permission::read(Role::any()),
Permission::update(Role::any()),
Permission::delete(Role::any()),
],
'projectInternalId' => $project->getInternalId(),
'projectId' => $project->getId(),
'name' => 'Outdated key',
'scopes' => $scopes,
'expire' => null,
'sdks' => [],
'accessedAt' => null,
'secret' => \bin2hex(\random_bytes(128)),
]);
$key = $dbForConsole->createDocument('keys', $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
$response
->setStatusCode(Response::STATUS_CODE_CREATED)
->dynamic($key, Response::MODEL_KEY);
});
App::get('/v1/mock/github/callback')
->desc('Create installation document using GitHub installation id')
->groups(['mock', 'api', 'vcs'])
+93 -36
View File
@@ -1,5 +1,7 @@
<?php
use Ahc\Jwt\JWT;
use Ahc\Jwt\JWTException;
use Appwrite\Auth\Auth;
use Appwrite\Auth\MFA\Type\TOTP;
use Appwrite\Event\Audit;
@@ -16,7 +18,7 @@ use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Request;
use Appwrite\Utopia\Response;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\Database\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
@@ -195,56 +197,100 @@ App::init()
$scope = $route->getLabel('scope', 'none'); // Allowed scope for chosen route
$scopes = $roles[$role]['scopes']; // Allowed scopes for user role
$authKey = $request->getHeader('x-appwrite-key', '');
$apiKey = $request->getHeader('x-appwrite-key', '');
if (!empty($authKey)) { // API Key authentication
// API Key authentication
if (!empty($apiKey)) {
// Do not allow API key and session to be set at the same time
if (!$user->isEmpty()) {
throw new Exception(Exception::USER_API_KEY_AND_SESSION_SET);
}
// Check if given key match project API keys
$key = $project->find('secret', $authKey, 'keys');
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
// Remove after migration
if (!\str_contains($apiKey, '_')) {
$keyType = API_KEY_STANDARD;
$authKey = $apiKey;
} else {
[ $keyType, $authKey ] = \explode('_', $apiKey, 2);
}
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
if ($keyType === API_KEY_DYNAMIC) {
// Dynamic key
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwtObj->decode($authKey);
} catch (JWTException $error) {
throw new Exception(Exception::API_KEY_EXPIRED);
}
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$projectId = $payload['projectId'] ?? '';
$tokenScopes = $payload['scopes'] ?? [];
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
// JWT includes project ID for better security
if ($projectId === $project->getId()) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $tokenScopes);
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
}
} elseif ($keyType === API_KEY_STANDARD) {
// No underline means no prefix. Backwards compatibility.
// Regular key
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
// Check if given key match project API keys
$key = $project->find('secret', $apiKey, 'keys');
if ($key) {
$user = new Document([
'$id' => '',
'status' => true,
'email' => 'app.' . $project->getId() . '@service.' . $request->getHostname(),
'password' => '',
'name' => $project->getAttribute('name', 'Untitled'),
]);
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$role = Auth::USER_ROLE_APPS;
$scopes = \array_merge($roles[$role]['scopes'], $key->getAttribute('scopes', []));
$expire = $key->getAttribute('expire');
if (!empty($expire) && $expire < DateTime::formatTz(DateTime::now())) {
throw new Exception(Exception::PROJECT_KEY_EXPIRED);
}
Authorization::setRole(Auth::USER_ROLE_APPS);
Authorization::setDefaultStatus(false); // Cancel security segmentation for API keys.
$accessedAt = $key->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_KEY_ACCESS)) > $accessedAt) {
$key->setAttribute('accessedAt', DateTime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
$sdkValidator = new WhiteList($servers, true);
$sdk = $request->getHeader('x-sdk-name', 'UNKNOWN');
if ($sdkValidator->isValid($sdk)) {
$sdks = $key->getAttribute('sdks', []);
if (!in_array($sdk, $sdks)) {
array_push($sdks, $sdk);
$key->setAttribute('sdks', $sdks);
/** Update access time as well */
$key->setAttribute('accessedAt', Datetime::now());
$dbForConsole->updateDocument('keys', $key->getId(), $key);
$dbForConsole->purgeCachedDocument('projects', $project->getId());
}
}
}
}
}
@@ -334,7 +380,7 @@ App::init()
foreach ($abuseKeyLabel as $abuseKey) {
$start = $request->getContentRangeStart();
$end = $request->getContentRangeEnd();
$timeLimit = new TimeLimit($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit = new AbuseDatabase($abuseKey, $route->getLabel('abuse-limit', 0), $route->getLabel('abuse-time', 3600), $dbForProject);
$timeLimit
->setParam('{projectId}', $project->getId())
->setParam('{userId}', $user->getId())
@@ -721,12 +767,23 @@ App::shutdown()
->trigger();
}
/**
* Update project last activity
*/
if (!$project->isEmpty() && $project->getId() !== 'console') {
$accessedAt = $project->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_PROJECT_ACCESS)) > $accessedAt) {
$project->setAttribute('accessedAt', DateTime::now());
Authorization::skip(fn () => $dbForConsole->updateDocument('projects', $project->getId(), $project));
}
}
/**
* Update user last activity
*/
if (!$user->isEmpty()) {
$accessedAt = $user->getAttribute('accessedAt', '');
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCCESS)) > $accessedAt) {
if (DateTime::formatTz(DateTime::addSeconds(new \DateTime(), -APP_USER_ACCESS)) > $accessedAt) {
$user->setAttribute('accessedAt', DateTime::now());
if (APP_MODE_ADMIN !== $mode) {
+9 -42
View File
@@ -16,8 +16,7 @@ App::init()
;
});
App::get('/console/*')
->alias('/')
App::get('/')
->alias('auth/*')
->alias('/invite')
->alias('/login')
@@ -31,45 +30,13 @@ App::get('/console/*')
->inject('request')
->inject('response')
->action(function (Request $request, Response $response) {
$fallback = file_get_contents(__DIR__ . '/../../../console/index.html');
// Card SSR
if (\str_starts_with($request->getURI(), '/card')) {
$urlCunks = \explode('/', $request->getURI());
$userId = $urlCunks[\count($urlCunks) - 1] ?? '';
$domain = $request->getProtocol() . '://' . $request->getHostname();
if (!empty($userId)) {
$ogImageUrl = $domain . '/v1/cards/cloud-og?userId=' . $userId;
} else {
$ogImageUrl = $domain . '/v1/cards/cloud-og?mock=normal';
}
$ogTags = [
'<title>Appwrite Cloud Card</title>',
'<meta name="description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:url" content="' . $domain . $request->getURI() . '">',
'<meta name="og:image:type" content="image/png">',
'<meta name="og:image:width" content="1008">',
'<meta name="og:image:height" content="1008">',
'<meta property="og:type" content="website">',
'<meta property="og:title" content="Appwrite Cloud Card">',
'<meta property="og:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta property="og:image" content="' . $ogImageUrl . '">',
'<meta name="twitter:card" content="summary_large_image">',
'<meta property="twitter:domain" content="' . $request->getHostname() . '">',
'<meta property="twitter:url" content="' . $domain . $request->getURI() . '">',
'<meta name="twitter:title" content="Appwrite Cloud Card">',
'<meta name="twitter:image:type" content="image/png">',
'<meta name="twitter:image:width" content="1008">',
'<meta name="twitter:image:height" content="1008">',
'<meta name="twitter:description" content="Appwrite Cloud is now LIVE! Share your Cloud card for a chance to win an exclusive Cloud hoodie!">',
'<meta name="twitter:image" content="' . $ogImageUrl . '">',
];
$fallback = \str_replace('<!-- {{CLOUD_OG}} -->', \implode('', $ogTags), $fallback);
$url = parse_url($request->getURI());
$target = "/console{$url['path']}";
if ($url['query'] ?? false) {
$target .= "?{$url['query']}";
}
$response->html($fallback);
if ($url['fragment'] ?? false) {
$target .= "#{$url['fragment']}";
}
$response->redirect($target);
});
+3 -5
View File
@@ -9,7 +9,7 @@ use Swoole\Http\Request as SwooleRequest;
use Swoole\Http\Response as SwooleResponse;
use Swoole\Http\Server;
use Swoole\Process;
use Utopia\Abuse\Adapters\Database\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Audit\Audit;
use Utopia\CLI\Console;
@@ -57,8 +57,6 @@ $http->on(Constant::EVENT_AFTER_RELOAD, function ($server, $workerId) {
Console::success('Reload completed...');
});
Files::load(__DIR__ . '/../console');
include __DIR__ . '/controllers/general.php';
$http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $register) {
@@ -103,8 +101,8 @@ $http->on(Constant::EVENT_START, function (Server $http) use ($payloadSize, $reg
$audit->setup();
}
if ($dbForConsole->getCollection(TimeLimit::COLLECTION)->isEmpty()) {
$adapter = new TimeLimit("", 0, 1, $dbForConsole);
if ($dbForConsole->getCollection(AbuseDatabase::COLLECTION)->isEmpty()) {
$adapter = new AbuseDatabase("", 0, 1, $dbForConsole);
$adapter->setup();
}
+68 -23
View File
@@ -62,6 +62,10 @@ use Utopia\Database\Validator\Structure;
use Utopia\Domains\Validator\PublicDomain;
use Utopia\DSN\DSN;
use Utopia\Locale\Locale;
use Utopia\Logger\Adapter\AppSignal;
use Utopia\Logger\Adapter\LogOwl;
use Utopia\Logger\Adapter\Raygun;
use Utopia\Logger\Adapter\Sentry;
use Utopia\Logger\Log;
use Utopia\Logger\Logger;
use Utopia\Pools\Group;
@@ -109,11 +113,12 @@ const APP_LIMIT_SUBSCRIBERS_SUBQUERY = 1_000_000;
const APP_LIMIT_WRITE_RATE_DEFAULT = 60; // Default maximum write rate per rate period
const APP_LIMIT_WRITE_RATE_PERIOD_DEFAULT = 60; // Default maximum write rate period in seconds
const APP_LIMIT_LIST_DEFAULT = 25; // Default maximum number of items to return in list API calls
const APP_KEY_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCCESS = 24 * 60 * 60; // 24 hours
const APP_KEY_ACCESS = 24 * 60 * 60; // 24 hours
const APP_USER_ACCESS = 24 * 60 * 60; // 24 hours
const APP_PROJECT_ACCESS = 24 * 60 * 60; // 24 hours
const APP_CACHE_UPDATE = 24 * 60 * 60; // 24 hours
const APP_CACHE_BUSTER = 4330;
const APP_VERSION_STABLE = '1.5.10';
const APP_CACHE_BUSTER = 4315;
const APP_VERSION_STABLE = '1.6.0';
const APP_DATABASE_ATTRIBUTE_EMAIL = 'email';
const APP_DATABASE_ATTRIBUTE_ENUM = 'enum';
const APP_DATABASE_ATTRIBUTE_IP = 'ip';
@@ -167,7 +172,7 @@ const DELETE_TYPE_PROJECTS = 'projects';
const DELETE_TYPE_FUNCTIONS = 'functions';
const DELETE_TYPE_DEPLOYMENTS = 'deployments';
const DELETE_TYPE_USERS = 'users';
const DELETE_TYPE_TEAMS = 'teams';
const DELETE_TYPE_TEAM_PROJECTS = 'teams_projects';
const DELETE_TYPE_EXECUTIONS = 'executions';
const DELETE_TYPE_AUDIT = 'audit';
const DELETE_TYPE_ABUSE = 'abuse';
@@ -208,6 +213,9 @@ const FUNCTION_ALLOWLIST_HEADERS_RESPONSE = ['content-type', 'content-length'];
const MESSAGE_TYPE_EMAIL = 'email';
const MESSAGE_TYPE_SMS = 'sms';
const MESSAGE_TYPE_PUSH = 'push';
// API key types
const API_KEY_STANDARD = 'standard';
const API_KEY_DYNAMIC = 'dynamic';
// Usage metrics
const METRIC_TEAMS = 'teams';
const METRIC_USERS = 'users';
@@ -229,12 +237,20 @@ const METRIC_FUNCTIONS = 'functions';
const METRIC_DEPLOYMENTS = 'deployments';
const METRIC_DEPLOYMENTS_STORAGE = 'deployments.storage';
const METRIC_BUILDS = 'builds';
const METRIC_BUILDS_SUCCESS = 'builds.success';
const METRIC_BUILDS_FAILED = 'builds.failed';
const METRIC_BUILDS_STORAGE = 'builds.storage';
const METRIC_BUILDS_COMPUTE = 'builds.compute';
const METRIC_BUILDS_COMPUTE_SUCCESS = 'builds.compute.success';
const METRIC_BUILDS_COMPUTE_FAILED = 'builds.compute.failed';
const METRIC_BUILDS_MB_SECONDS = 'builds.mbSeconds';
const METRIC_FUNCTION_ID_BUILDS = '{functionInternalId}.builds';
const METRIC_FUNCTION_ID_BUILDS_SUCCESS = '{functionInternalId}.builds.success';
const METRIC_FUNCTION_ID_BUILDS_FAILED = '{functionInternalId}.builds.failed';
const METRIC_FUNCTION_ID_BUILDS_STORAGE = '{functionInternalId}.builds.storage';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE = '{functionInternalId}.builds.compute';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS = '{functionInternalId}.builds.compute.success';
const METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED = '{functionInternalId}.builds.compute.failed';
const METRIC_FUNCTION_ID_DEPLOYMENTS = '{resourceType}.{resourceInternalId}.deployments';
const METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE = '{resourceType}.{resourceInternalId}.deployments.storage';
const METRIC_FUNCTION_ID_BUILDS_MB_SECONDS = '{functionInternalId}.builds.mbSeconds';
@@ -291,6 +307,7 @@ Config::load('storage-logos', __DIR__ . '/config/storage/logos.php');
Config::load('storage-mimes', __DIR__ . '/config/storage/mimes.php');
Config::load('storage-inputs', __DIR__ . '/config/storage/inputs.php');
Config::load('storage-outputs', __DIR__ . '/config/storage/outputs.php');
Config::load('function-templates', __DIR__ . '/config/function-templates.php');
/**
* New DB Filters
@@ -345,8 +362,7 @@ Database::addFilter(
if (isset($formatOptions['min']) || isset($formatOptions['max'])) {
$attribute
->setAttribute('min', $formatOptions['min'])
->setAttribute('max', $formatOptions['max'])
;
->setAttribute('max', $formatOptions['max']);
}
return $value;
@@ -729,6 +745,26 @@ $register->set('logger', function () {
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
try {
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
$providerConfig = match ($providerName) {
'sentry' => ['key' => $loggingProvider->getPassword(), 'projectId' => $loggingProvider->getUser() ?? '', 'host' => 'https://' . $loggingProvider->getHost()],
'logowl' => ['ticket' => $loggingProvider->getUser() ?? '', 'host' => $loggingProvider->getHost()],
default => ['key' => $loggingProvider->getHost()],
};
} catch (Throwable) {
// Fallback for older Appwrite versions up to 1.5.x that use _APP_LOGGING_PROVIDER and _APP_LOGGING_CONFIG environment variables
$configChunks = \explode(";", $providerConfig);
$providerConfig = match ($providerName) {
'sentry' => [ 'key' => $configChunks[0], 'projectId' => $configChunks[1] ?? '', 'host' => '',],
'logowl' => ['ticket' => $configChunks[0] ?? '', 'host' => ''],
default => ['key' => $providerConfig],
};
}
if (empty($providerName) || empty($providerConfig)) {
return;
}
@@ -737,20 +773,26 @@ $register->set('logger', function () {
throw new Exception(Exception::GENERAL_SERVER_ERROR, "Logging provider not supported. Logging is disabled");
}
// Old Sentry Format conversion. Fallback until the old syntax is completely deprecated.
if (str_contains($providerConfig, ';') && strtolower($providerName) == 'sentry') {
$configChunks = \explode(";", $providerConfig);
try {
$adapter = match ($providerName) {
'sentry' => new Sentry($providerConfig['projectId'], $providerConfig['key'], $providerConfig['host']),
'logowl' => new LogOwl($providerConfig['ticket'], $providerConfig['host']),
'raygun' => new Raygun($providerConfig['key']),
'appsignal' => new AppSignal($providerConfig['key']),
default => null
};
} catch (Throwable $th) {
$adapter = null;
}
$sentryKey = $configChunks[0];
$projectId = $configChunks[1];
$providerConfig = 'https://' . $sentryKey . '@sentry.io/' . $projectId;
if ($adapter === null) {
Console::error("Logging provider not supported. Logging is disabled");
return;
}
$classname = '\\Utopia\\Logger\\Adapter\\' . \ucfirst($providerName);
$adapter = new $classname($providerConfig);
return new Logger($adapter);
});
$register->set('pools', function () {
$group = new Group();
@@ -970,7 +1012,7 @@ $register->set('smtp', function () {
return $mail;
});
$register->set('geodb', function () {
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-02.mmdb');
return new Reader(__DIR__ . '/assets/dbip/dbip-country-lite-2024-08.mmdb');
});
$register->set('passwordsDictionary', function () {
$content = \file_get_contents(__DIR__ . '/assets/security/10k-common-passwords');
@@ -1205,7 +1247,7 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
$authJWT = $request->getHeader('x-appwrite-jwt', '');
if (!empty($authJWT) && !$project->isEmpty()) { // JWT authentication
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 900, 10); // Instantiate with key, algo, maxAge and leeway.
$jwt = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', 3600, 0);
try {
$payload = $jwt->decode($authJWT);
@@ -1214,14 +1256,15 @@ App::setResource('user', function ($mode, $project, $console, $request, $respons
}
$jwtUserId = $payload['userId'] ?? '';
$jwtSessionId = $payload['sessionId'] ?? '';
if ($jwtUserId && $jwtSessionId) {
if (!empty($jwtUserId)) {
$user = $dbForProject->getDocument('users', $jwtUserId);
}
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
$jwtSessionId = $payload['sessionId'] ?? '';
if (!empty($jwtSessionId)) {
if (empty($user->find('$id', $jwtSessionId, 'sessions'))) { // Match JWT to active token
$user = new Document([]);
}
}
}
@@ -1294,9 +1337,11 @@ App::setResource('console', function () {
'legalAddress' => '',
'legalTaxId' => '',
'auths' => [
'mockNumbers' => [],
'invites' => System::getEnv('_APP_CONSOLE_INVITES', 'enabled') === 'enabled',
'limit' => (System::getEnv('_APP_CONSOLE_WHITELIST_ROOT', 'enabled') === 'enabled') ? 1 : 0, // limit signup to 1 user
'duration' => Auth::TOKEN_EXPIRATION_LOGIN_LONG, // 1 Year in seconds
'sessionAlerts' => System::getEnv('_APP_CONSOLE_SESSION_ALERTS', 'disabled') === 'enabled'
],
'authWhitelistEmails' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_EMAILS', null)) : [],
'authWhitelistIPs' => (!empty(System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null))) ? \explode(',', System::getEnv('_APP_CONSOLE_WHITELIST_IPS', null)) : [],
+3 -3
View File
@@ -13,7 +13,7 @@ use Swoole\Runtime;
use Swoole\Table;
use Swoole\Timer;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\Database\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\App;
use Utopia\Cache\Adapter\Sharding;
use Utopia\Cache\Cache;
@@ -463,7 +463,7 @@ $server->onOpen(function (int $connection, SwooleRequest $request) use ($server,
*
* Abuse limits are connecting 128 times per minute and ip address.
*/
$timeLimit = new TimeLimit('url:{url},ip:{ip}', 128, 60, $dbForProject);
$timeLimit = new AbuseDatabase('url:{url},ip:{ip}', 128, 60, $dbForProject);
$timeLimit
->setParam('{ip}', $request->getIP())
->setParam('{url}', $request->getURI());
@@ -563,7 +563,7 @@ $server->onMessage(function (int $connection, string $message) use ($server, $re
*
* Abuse limits are sending 32 times per minute and connection.
*/
$timeLimit = new TimeLimit('url:{url},connection:{connection}', 32, 60, $database);
$timeLimit = new AbuseDatabase('url:{url},connection:{connection}', 32, 60, $database);
$timeLimit
->setParam('{connection}', $connection)
+51 -16
View File
@@ -75,6 +75,7 @@ $image = $this->getParam('image', '');
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
@@ -138,7 +139,6 @@ $image = $this->getParam('image', '');
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_DELAY
@@ -163,6 +163,28 @@ $image = $this->getParam('image', '');
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
- _APP_ASSISTANT_OPENAI_API_KEY
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: <?php echo $organization; ?>/console:appwrite/console:5.0.0-rc16
restart: unless-stopped
networks:
- appwrite
labels:
- "traefik.enable=true"
- "traefik.constraint-label-stack=appwrite"
- "traefik.docker.network=appwrite"
- "traefik.http.services.appwrite_console.loadbalancer.server.port=80"
#ws
- traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web
- traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_http.service=appwrite_console
# wss
- traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure
- traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_https.service=appwrite_console
- traefik.http.routers.appwrite_console_https.tls=true
appwrite-realtime:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: realtime
@@ -204,7 +226,6 @@ $image = $this->getParam('image', '');
- _APP_DB_USER
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-audits:
@@ -231,7 +252,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-webhooks:
@@ -260,7 +280,6 @@ $image = $this->getParam('image', '');
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-deletes:
@@ -314,7 +333,6 @@ $image = $this->getParam('image', '');
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -343,7 +361,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-builds:
@@ -375,7 +392,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_VCS_GITHUB_APP_NAME
- _APP_VCS_GITHUB_PRIVATE_KEY
@@ -441,7 +457,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-functions:
@@ -460,6 +475,8 @@ $image = $this->getParam('image', '');
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -479,7 +496,6 @@ $image = $this->getParam('image', '');
- _APP_DOCKER_HUB_USERNAME
- _APP_DOCKER_HUB_PASSWORD
- _APP_LOGGING_CONFIG
- _APP_LOGGING_PROVIDER
appwrite-worker-mails:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
@@ -511,7 +527,6 @@ $image = $this->getParam('image', '');
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
appwrite-worker-messaging:
@@ -539,7 +554,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_SMS_FROM
- _APP_SMS_PROVIDER
@@ -591,7 +605,6 @@ $image = $this->getParam('image', '');
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
@@ -655,7 +668,6 @@ $image = $this->getParam('image', '');
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
@@ -684,7 +696,6 @@ $image = $this->getParam('image', '');
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
@@ -713,6 +724,31 @@ $image = $this->getParam('image', '');
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-executions:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule-executions
container_name: appwrite-task-scheduler-executions
<<: *x-logging
restart: unless-stopped
networks:
- appwrite
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-messages:
image: <?php echo $organization; ?>/<?php echo $image; ?>:<?php echo $version."\n"; ?>
entrypoint: schedule-messages
@@ -754,7 +790,7 @@ $image = $this->getParam('image', '');
<<: *x-logging
restart: unless-stopped
stop_signal: SIGINT
image: openruntimes/executor:0.5.7
image: openruntimes/executor:0.6.5
networks:
- appwrite
- runtimes
@@ -774,7 +810,6 @@ $image = $this->getParam('image', '');
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
+3
View File
@@ -0,0 +1,3 @@
#!/bin/sh
php /usr/src/code/app/cli.php schedule-executions $@
+3 -3
View File
@@ -42,7 +42,7 @@
"ext-openssl": "*",
"ext-zlib": "*",
"ext-sockets": "*",
"appwrite/php-runtimes": "0.13.*",
"appwrite/php-runtimes": "0.14.*",
"appwrite/php-clamav": "2.0.*",
"utopia-php/abuse": "0.42.*",
"utopia-php/analytics": "0.10.*",
@@ -57,7 +57,7 @@
"utopia-php/fetch": "0.2.*",
"utopia-php/image": "0.6.*",
"utopia-php/locale": "0.4.*",
"utopia-php/logger": "0.5.*",
"utopia-php/logger": "0.6.*",
"utopia-php/messaging": "0.12.*",
"utopia-php/migration": "0.5.*",
"utopia-php/orchestration": "0.9.*",
@@ -82,7 +82,7 @@
},
"require-dev": {
"ext-fileinfo": "*",
"appwrite/sdk-generator": "0.38.*",
"appwrite/sdk-generator": "0.39.*",
"phpunit/phpunit": "9.5.20",
"swoole/ide-helper": "5.1.2",
"textalk/websocket": "1.5.7",
Generated
+276 -125
View File
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "341116bfee981d2d1d6f3e611c3474aa",
"content-hash": "16a461f3a269cfe0e450e04c905c2104",
"packages": [
{
"name": "adhocore/jwt",
@@ -156,16 +156,16 @@
},
{
"name": "appwrite/php-runtimes",
"version": "0.13.5",
"version": "0.14.0",
"source": {
"type": "git",
"url": "https://github.com/appwrite/runtimes.git",
"reference": "ba24c3a163f1a1da6cd355db92def508d05e59f7"
"reference": "9bae5bebe7c7cb3e4bf3a05a1d36c9d6ce3a2d3e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/ba24c3a163f1a1da6cd355db92def508d05e59f7",
"reference": "ba24c3a163f1a1da6cd355db92def508d05e59f7",
"url": "https://api.github.com/repos/appwrite/runtimes/zipball/9bae5bebe7c7cb3e4bf3a05a1d36c9d6ce3a2d3e",
"reference": "9bae5bebe7c7cb3e4bf3a05a1d36c9d6ce3a2d3e",
"shasum": ""
},
"require": {
@@ -173,8 +173,9 @@
"utopia-php/system": "0.8.*"
},
"require-dev": {
"phpunit/phpunit": "^9.3",
"vimeo/psalm": "4.0.1"
"laravel/pint": "^1.15",
"phpstan/phpstan": "^1.10",
"phpunit/phpunit": "^9.3"
},
"type": "library",
"autoload": {
@@ -204,9 +205,9 @@
],
"support": {
"issues": "https://github.com/appwrite/runtimes/issues",
"source": "https://github.com/appwrite/runtimes/tree/0.13.5"
"source": "https://github.com/appwrite/runtimes/tree/0.14.0"
},
"time": "2024-04-01T10:35:02+00:00"
"time": "2024-07-03T10:18:32+00:00"
},
{
"name": "beberlei/assert",
@@ -1129,20 +1130,20 @@
},
{
"name": "symfony/polyfill-mbstring",
"version": "v1.30.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-mbstring.git",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c"
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"reference": "fd22ab50000ef01661e2a31d850ebaa297f8e03c",
"url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341",
"reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341",
"shasum": ""
},
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"provide": {
"ext-mbstring": "*"
@@ -1189,7 +1190,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.30.0"
"source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0"
},
"funding": [
{
@@ -1205,24 +1206,24 @@
"type": "tidelift"
}
],
"time": "2024-06-19T12:30:46+00:00"
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php80",
"version": "v1.30.0",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php80.git",
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433"
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/77fa7995ac1b21ab60769b7323d600a991a90433",
"reference": "77fa7995ac1b21ab60769b7323d600a991a90433",
"url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8",
"shasum": ""
},
"require": {
"php": ">=7.1"
"php": ">=7.2"
},
"type": "library",
"extra": {
@@ -1269,7 +1270,7 @@
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php80/tree/v1.30.0"
"source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0"
},
"funding": [
{
@@ -1285,7 +1286,7 @@
"type": "tidelift"
}
],
"time": "2024-05-31T15:07:36+00:00"
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "thecodingmachine/safe",
@@ -1722,16 +1723,16 @@
},
{
"name": "utopia-php/database",
"version": "0.52.1",
"version": "0.52.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/database.git",
"reference": "d22a316a010699c5ebb4768c1020167c192b9c6b"
"reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/database/zipball/d22a316a010699c5ebb4768c1020167c192b9c6b",
"reference": "d22a316a010699c5ebb4768c1020167c192b9c6b",
"url": "https://api.github.com/repos/utopia-php/database/zipball/24b29bcac7eb7a8b81698a80bb75fc5909f4975e",
"reference": "24b29bcac7eb7a8b81698a80bb75fc5909f4975e",
"shasum": ""
},
"require": {
@@ -1772,9 +1773,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/database/issues",
"source": "https://github.com/utopia-php/database/tree/0.52.1"
"source": "https://github.com/utopia-php/database/tree/0.52.2"
},
"time": "2024-08-23T02:43:43+00:00"
"time": "2024-09-02T06:28:50+00:00"
},
{
"name": "utopia-php/domains",
@@ -1924,16 +1925,16 @@
},
{
"name": "utopia-php/framework",
"version": "0.33.6",
"version": "0.33.8",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/http.git",
"reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6"
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/http/zipball/8fe57da0cecd57e3b17cd395b4a666a24f4c07a6",
"reference": "8fe57da0cecd57e3b17cd395b4a666a24f4c07a6",
"url": "https://api.github.com/repos/utopia-php/http/zipball/a7f577540a25cb90896fef2b64767bf8d700f3c5",
"reference": "a7f577540a25cb90896fef2b64767bf8d700f3c5",
"shasum": ""
},
"require": {
@@ -1963,9 +1964,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/http/issues",
"source": "https://github.com/utopia-php/http/tree/0.33.6"
"source": "https://github.com/utopia-php/http/tree/0.33.8"
},
"time": "2024-03-21T18:10:57+00:00"
"time": "2024-08-15T14:10:09+00:00"
},
{
"name": "utopia-php/image",
@@ -2068,16 +2069,16 @@
},
{
"name": "utopia-php/logger",
"version": "0.5.2",
"version": "0.6.0",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/logger.git",
"reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba"
"reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/c6dfdb672e41364c309b0c30dc03bc6d45446dba",
"reference": "c6dfdb672e41364c309b0c30dc03bc6d45446dba",
"url": "https://api.github.com/repos/utopia-php/logger/zipball/a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9",
"reference": "a2d1daeeb8f61fdec6d851950d9a021a3d05c9f9",
"shasum": ""
},
"require": {
@@ -2116,9 +2117,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/logger/issues",
"source": "https://github.com/utopia-php/logger/tree/0.5.2"
"source": "https://github.com/utopia-php/logger/tree/0.6.0"
},
"time": "2024-05-17T09:32:59+00:00"
"time": "2024-05-23T13:37:54+00:00"
},
{
"name": "utopia-php/messaging",
@@ -2598,16 +2599,16 @@
},
{
"name": "utopia-php/storage",
"version": "0.18.4",
"version": "0.18.5",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/storage.git",
"reference": "94ab8758fabcefee5c5fa723616e45719833f922"
"reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/94ab8758fabcefee5c5fa723616e45719833f922",
"reference": "94ab8758fabcefee5c5fa723616e45719833f922",
"url": "https://api.github.com/repos/utopia-php/storage/zipball/7d355c5e3ccc8ecebc0266f8ddd30088a43be919",
"reference": "7d355c5e3ccc8ecebc0266f8ddd30088a43be919",
"shasum": ""
},
"require": {
@@ -2647,9 +2648,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/storage/issues",
"source": "https://github.com/utopia-php/storage/tree/0.18.4"
"source": "https://github.com/utopia-php/storage/tree/0.18.5"
},
"time": "2024-04-02T08:24:09+00:00"
"time": "2024-09-04T08:57:27+00:00"
},
{
"name": "utopia-php/swoole",
@@ -2760,16 +2761,16 @@
},
{
"name": "utopia-php/vcs",
"version": "0.8.1",
"version": "0.8.2",
"source": {
"type": "git",
"url": "https://github.com/utopia-php/vcs.git",
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596"
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/3084aa93d24ed1e70f01e75f4318fc0d07f12596",
"reference": "3084aa93d24ed1e70f01e75f4318fc0d07f12596",
"url": "https://api.github.com/repos/utopia-php/vcs/zipball/eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
"reference": "eb9b7eade1a46a4f660e0d5a6304f7fa26ec9d18",
"shasum": ""
},
"require": {
@@ -2803,9 +2804,9 @@
],
"support": {
"issues": "https://github.com/utopia-php/vcs/issues",
"source": "https://github.com/utopia-php/vcs/tree/0.8.1"
"source": "https://github.com/utopia-php/vcs/tree/0.8.2"
},
"time": "2024-07-29T20:49:09+00:00"
"time": "2024-08-13T14:36:30+00:00"
},
{
"name": "utopia-php/websocket",
@@ -2992,16 +2993,16 @@
"packages-dev": [
{
"name": "appwrite/sdk-generator",
"version": "0.38.8",
"version": "0.39.21",
"source": {
"type": "git",
"url": "https://github.com/appwrite/sdk-generator.git",
"reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef"
"reference": "9754b190d33aaad56fdb8defc94f90248184c5ac"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef",
"reference": "6367c57ddbcf7b88cacb900c4fe7ef3f28bf38ef",
"url": "https://api.github.com/repos/appwrite/sdk-generator/zipball/9754b190d33aaad56fdb8defc94f90248184c5ac",
"reference": "9754b190d33aaad56fdb8defc94f90248184c5ac",
"shasum": ""
},
"require": {
@@ -3010,12 +3011,12 @@
"ext-mbstring": "*",
"matthiasmullie/minify": "1.3.*",
"php": ">=8.0",
"twig/twig": "v3.8.*"
"twig/twig": "3.14.*"
},
"require-dev": {
"brianium/paratest": "v7.4.*",
"phpunit/phpunit": "10.5.*",
"squizlabs/php_codesniffer": "3.9.*"
"brianium/paratest": "7.*",
"phpunit/phpunit": "11.*",
"squizlabs/php_codesniffer": "3.*"
},
"type": "library",
"autoload": {
@@ -3037,9 +3038,9 @@
"description": "Appwrite PHP library for generating API SDKs for multiple programming languages and platforms",
"support": {
"issues": "https://github.com/appwrite/sdk-generator/issues",
"source": "https://github.com/appwrite/sdk-generator/tree/0.38.8"
"source": "https://github.com/appwrite/sdk-generator/tree/0.39.21"
},
"time": "2024-06-17T00:42:27+00:00"
"time": "2024-09-10T08:49:29+00:00"
},
{
"name": "doctrine/deprecations",
@@ -3160,16 +3161,16 @@
},
{
"name": "laravel/pint",
"version": "v1.17.0",
"version": "v1.17.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/pint.git",
"reference": "4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5"
"reference": "9d77be916e145864f10788bb94531d03e1f7b482"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/pint/zipball/4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5",
"reference": "4dba80c1de4b81dc4c4fb10ea6f4781495eb29f5",
"url": "https://api.github.com/repos/laravel/pint/zipball/9d77be916e145864f10788bb94531d03e1f7b482",
"reference": "9d77be916e145864f10788bb94531d03e1f7b482",
"shasum": ""
},
"require": {
@@ -3180,13 +3181,13 @@
"php": "^8.1.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.59.3",
"illuminate/view": "^10.48.12",
"larastan/larastan": "^2.9.7",
"friendsofphp/php-cs-fixer": "^3.64.0",
"illuminate/view": "^10.48.20",
"larastan/larastan": "^2.9.8",
"laravel-zero/framework": "^10.4.0",
"mockery/mockery": "^1.6.12",
"nunomaduro/termwind": "^1.15.1",
"pestphp/pest": "^2.34.8"
"pestphp/pest": "^2.35.1"
},
"bin": [
"builds/pint"
@@ -3222,7 +3223,7 @@
"issues": "https://github.com/laravel/pint/issues",
"source": "https://github.com/laravel/pint"
},
"time": "2024-07-23T16:40:20+00:00"
"time": "2024-09-03T15:00:28+00:00"
},
{
"name": "matthiasmullie/minify",
@@ -3410,16 +3411,16 @@
},
{
"name": "nikic/php-parser",
"version": "v5.1.0",
"version": "v5.2.0",
"source": {
"type": "git",
"url": "https://github.com/nikic/PHP-Parser.git",
"reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1"
"reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/683130c2ff8c2739f4822ff7ac5c873ec529abd1",
"reference": "683130c2ff8c2739f4822ff7ac5c873ec529abd1",
"url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb",
"reference": "23c79fbbfb725fb92af9bcf41065c8e9a0d49ddb",
"shasum": ""
},
"require": {
@@ -3462,9 +3463,9 @@
],
"support": {
"issues": "https://github.com/nikic/PHP-Parser/issues",
"source": "https://github.com/nikic/PHP-Parser/tree/v5.1.0"
"source": "https://github.com/nikic/PHP-Parser/tree/v5.2.0"
},
"time": "2024-07-01T20:03:41+00:00"
"time": "2024-09-15T16:40:33+00:00"
},
{
"name": "phar-io/manifest",
@@ -3830,16 +3831,16 @@
},
{
"name": "phpstan/phpdoc-parser",
"version": "1.29.1",
"version": "1.30.1",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpdoc-parser.git",
"reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4"
"reference": "51b95ec8670af41009e2b2b56873bad96682413e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/fcaefacf2d5c417e928405b71b400d4ce10daaf4",
"reference": "fcaefacf2d5c417e928405b71b400d4ce10daaf4",
"url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/51b95ec8670af41009e2b2b56873bad96682413e",
"reference": "51b95ec8670af41009e2b2b56873bad96682413e",
"shasum": ""
},
"require": {
@@ -3871,41 +3872,41 @@
"description": "PHPDoc parser with support for nullable, intersection and generic types",
"support": {
"issues": "https://github.com/phpstan/phpdoc-parser/issues",
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.29.1"
"source": "https://github.com/phpstan/phpdoc-parser/tree/1.30.1"
},
"time": "2024-05-31T08:52:43+00:00"
"time": "2024-09-07T20:13:05+00:00"
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.31",
"version": "9.2.32",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "48c34b5d8d983006bd2adc2d0de92963b9155965"
"reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/48c34b5d8d983006bd2adc2d0de92963b9155965",
"reference": "48c34b5d8d983006bd2adc2d0de92963b9155965",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/85402a822d1ecf1db1096959413d35e1c37cf1a5",
"reference": "85402a822d1ecf1db1096959413d35e1c37cf1a5",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.18 || ^5.0",
"nikic/php-parser": "^4.19.1 || ^5.1.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
"sebastian/code-unit-reverse-lookup": "^2.0.2",
"sebastian/complexity": "^2.0",
"sebastian/environment": "^5.1.2",
"sebastian/lines-of-code": "^1.0.3",
"sebastian/version": "^3.0.1",
"theseer/tokenizer": "^1.2.0"
"phpunit/php-file-iterator": "^3.0.6",
"phpunit/php-text-template": "^2.0.4",
"sebastian/code-unit-reverse-lookup": "^2.0.3",
"sebastian/complexity": "^2.0.3",
"sebastian/environment": "^5.1.5",
"sebastian/lines-of-code": "^1.0.4",
"sebastian/version": "^3.0.2",
"theseer/tokenizer": "^1.2.3"
},
"require-dev": {
"phpunit/phpunit": "^9.3"
"phpunit/phpunit": "^9.6"
},
"suggest": {
"ext-pcov": "PHP extension that provides line coverage",
@@ -3914,7 +3915,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "9.2-dev"
"dev-main": "9.2.x-dev"
}
},
"autoload": {
@@ -3943,7 +3944,7 @@
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.31"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.32"
},
"funding": [
{
@@ -3951,7 +3952,7 @@
"type": "github"
}
],
"time": "2024-03-02T06:37:42+00:00"
"time": "2024-08-22T04:23:01+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -4299,16 +4300,16 @@
},
{
"name": "psr/log",
"version": "3.0.0",
"version": "3.0.2",
"source": {
"type": "git",
"url": "https://github.com/php-fig/log.git",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001"
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001",
"reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001",
"url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
"reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3",
"shasum": ""
},
"require": {
@@ -4343,9 +4344,9 @@
"psr-3"
],
"support": {
"source": "https://github.com/php-fig/log/tree/3.0.0"
"source": "https://github.com/php-fig/log/tree/3.0.2"
},
"time": "2021-07-14T16:46:02+00:00"
"time": "2024-09-11T13:17:53+00:00"
},
{
"name": "sebastian/cli-parser",
@@ -5343,21 +5344,88 @@
"time": "2024-02-01T22:28:11+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.30.0",
"name": "symfony/deprecation-contracts",
"version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540"
"url": "https://github.com/symfony/deprecation-contracts.git",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/0424dff1c58f028c451efff2045f5d92410bd540",
"reference": "0424dff1c58f028c451efff2045f5d92410bd540",
"url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"php": ">=7.1"
"php": ">=8.1"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"url": "https://github.com/symfony/contracts"
}
},
"autoload": {
"files": [
"function.php"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
"source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/polyfill-ctype",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-ctype.git",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638",
"reference": "a3cc8b044a6ea513310cbd48ef7333b384945638",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"provide": {
"ext-ctype": "*"
@@ -5403,7 +5471,7 @@
"portable"
],
"support": {
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.30.0"
"source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0"
},
"funding": [
{
@@ -5419,7 +5487,83 @@
"type": "tidelift"
}
],
"time": "2024-05-31T15:07:36+00:00"
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "symfony/polyfill-php81",
"version": "v1.31.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php81.git",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c",
"shasum": ""
},
"require": {
"php": ">=7.2"
},
"type": "library",
"extra": {
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
}
},
"autoload": {
"files": [
"bootstrap.php"
],
"psr-4": {
"Symfony\\Polyfill\\Php81\\": ""
},
"classmap": [
"Resources/stubs"
]
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Nicolas Grekas",
"email": "p@tchwork.com"
},
{
"name": "Symfony Community",
"homepage": "https://symfony.com/contributors"
}
],
"description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions",
"homepage": "https://symfony.com",
"keywords": [
"compatibility",
"polyfill",
"portable",
"shim"
],
"support": {
"source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0"
},
"funding": [
{
"url": "https://symfony.com/sponsor",
"type": "custom"
},
{
"url": "https://github.com/fabpot",
"type": "github"
},
{
"url": "https://tidelift.com/funding/github/packagist/symfony/symfony",
"type": "tidelift"
}
],
"time": "2024-09-09T11:45:10+00:00"
},
{
"name": "textalk/websocket",
@@ -5522,30 +5666,37 @@
},
{
"name": "twig/twig",
"version": "v3.8.0",
"version": "v3.14.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d"
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"reference": "9d15f0ac07f44dc4217883ec6ae02fd555c6f71d",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"reference": "126b2c97818dbff0cdf3fbfc881aedb3d40aae72",
"shasum": ""
},
"require": {
"php": ">=7.2.5",
"php": ">=8.0.2",
"symfony/deprecation-contracts": "^2.5|^3",
"symfony/polyfill-ctype": "^1.8",
"symfony/polyfill-mbstring": "^1.3",
"symfony/polyfill-php80": "^1.22"
"symfony/polyfill-php81": "^1.29"
},
"require-dev": {
"psr/container": "^1.0|^2.0",
"symfony/phpunit-bridge": "^5.4.9|^6.3|^7.0"
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0"
},
"type": "library",
"autoload": {
"files": [
"src/Resources/core.php",
"src/Resources/debug.php",
"src/Resources/escaper.php",
"src/Resources/string_loader.php"
],
"psr-4": {
"Twig\\": "src/"
}
@@ -5578,7 +5729,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v3.8.0"
"source": "https://github.com/twigphp/Twig/tree/v3.14.0"
},
"funding": [
{
@@ -5590,7 +5741,7 @@
"type": "tidelift"
}
],
"time": "2023-11-21T18:54:41+00:00"
"time": "2024-09-09T17:55:12+00:00"
}
],
"aliases": [],
+57 -19
View File
@@ -40,6 +40,7 @@ services:
networks:
- gateway
- appwrite
- runtimes
appwrite:
container_name: appwrite
@@ -51,7 +52,7 @@ services:
DEBUG: false
TESTING: true
VERSION: dev
ports:
ports:
- 9501:80
networks:
- appwrite
@@ -97,6 +98,7 @@ services:
- _APP_LOCALE
- _APP_CONSOLE_WHITELIST_ROOT
- _APP_CONSOLE_WHITELIST_EMAILS
- _APP_CONSOLE_SESSION_ALERTS
- _APP_CONSOLE_WHITELIST_IPS
- _APP_CONSOLE_HOSTNAMES
- _APP_SYSTEM_EMAIL_NAME
@@ -160,7 +162,6 @@ services:
- _APP_FUNCTIONS_RUNTIMES
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MAINTENANCE_INTERVAL
- _APP_MAINTENANCE_RETENTION_EXECUTION
@@ -191,6 +192,28 @@ services:
- _APP_EXPERIMENT_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
appwrite-console:
<<: *x-logging
container_name: appwrite-console
image: appwrite/console:5.0.0-rc16
restart: unless-stopped
networks:
- appwrite
labels:
- "traefik.enable=true"
- "traefik.constraint-label-stack=appwrite"
- "traefik.docker.network=appwrite"
- "traefik.http.services.appwrite_console.loadbalancer.server.port=80"
#ws
- traefik.http.routers.appwrite_console_http.entrypoints=appwrite_web
- traefik.http.routers.appwrite_console_http.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_http.service=appwrite_console
# wss
- traefik.http.routers.appwrite_console_https.entrypoints=appwrite_websecure
- traefik.http.routers.appwrite_console_https.rule=PathPrefix(`/console`)
- traefik.http.routers.appwrite_console_https.service=appwrite_console
- traefik.http.routers.appwrite_console_https.tls=true
appwrite-realtime:
entrypoint: realtime
<<: *x-logging
@@ -237,7 +260,6 @@ services:
- _APP_DB_USER
- _APP_DB_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -267,7 +289,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -299,7 +320,6 @@ services:
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WEBHOOK_MAX_FAILED_ATTEMPTS
- _APP_DATABASE_SHARED_TABLES
@@ -356,7 +376,6 @@ services:
- _APP_STORAGE_WASABI_SECRET
- _APP_STORAGE_WASABI_REGION
- _APP_STORAGE_WASABI_BUCKET
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_EXECUTOR_SECRET
- _APP_EXECUTOR_HOST
@@ -388,7 +407,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_WORKERS_NUM
- _APP_QUEUE_NAME
@@ -424,7 +442,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_VCS_GITHUB_APP_NAME
- _APP_VCS_GITHUB_PRIVATE_KEY
@@ -492,7 +509,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DATABASE_SHARED_TABLES
@@ -514,6 +530,8 @@ services:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
@@ -565,7 +583,6 @@ services:
- _APP_SMTP_SECURE
- _APP_SMTP_USERNAME
- _APP_SMTP_PASSWORD
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_DOMAIN
- _APP_OPTIONS_FORCE_HTTPS
@@ -598,7 +615,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_SMS_FROM
- _APP_SMS_PROVIDER
@@ -656,7 +672,6 @@ services:
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_MIGRATIONS_FIREBASE_CLIENT_ID
- _APP_MIGRATIONS_FIREBASE_CLIENT_SECRET
@@ -727,7 +742,6 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
@@ -759,7 +773,6 @@ services:
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_USAGE_STATS
- _APP_LOGGING_PROVIDER
- _APP_LOGGING_CONFIG
- _APP_USAGE_AGGREGATION_INTERVAL
- _APP_DATABASE_SHARED_TABLES
@@ -792,6 +805,33 @@ services:
- _APP_DB_PASS
- _APP_DATABASE_SHARED_TABLES
appwrite-task-scheduler-executions:
entrypoint: schedule-executions
<<: *x-logging
container_name: appwrite-task-scheduler-executions
image: appwrite-dev
networks:
- appwrite
volumes:
- ./app:/usr/src/code/app
- ./src:/usr/src/code/src
depends_on:
- mariadb
- redis
environment:
- _APP_ENV
- _APP_WORKER_PER_CORE
- _APP_OPENSSL_KEY_V1
- _APP_REDIS_HOST
- _APP_REDIS_PORT
- _APP_REDIS_USER
- _APP_REDIS_PASS
- _APP_DB_HOST
- _APP_DB_PORT
- _APP_DB_SCHEMA
- _APP_DB_USER
- _APP_DB_PASS
appwrite-task-scheduler-messages:
entrypoint: schedule-messages
<<: *x-logging
@@ -833,7 +873,7 @@ services:
hostname: exc1
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/executor:0.5.7
image: openruntimes/executor:0.6.5
restart: unless-stopped
networks:
- appwrite
@@ -854,8 +894,7 @@ services:
- OPR_EXECUTOR_ENV=$_APP_ENV
- OPR_EXECUTOR_RUNTIMES=$_APP_FUNCTIONS_RUNTIMES
- OPR_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v3
- OPR_EXECUTOR_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_EXECUTOR_RUNTIME_VERSIONS=v2,v4
- OPR_EXECUTOR_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_EXECUTOR_STORAGE_DEVICE=$_APP_STORAGE_DEVICE
- OPR_EXECUTOR_STORAGE_S3_ACCESS_KEY=$_APP_STORAGE_S3_ACCESS_KEY
@@ -884,7 +923,7 @@ services:
hostname: proxy
<<: *x-logging
stop_signal: SIGINT
image: openruntimes/proxy:0.3.1
image: openruntimes/proxy:0.5.4
networks:
- appwrite
- runtimes
@@ -893,7 +932,6 @@ services:
- OPR_PROXY_ENV=$_APP_ENV
- OPR_PROXY_EXECUTOR_SECRET=$_APP_EXECUTOR_SECRET
- OPR_PROXY_SECRET=$_APP_EXECUTOR_SECRET
- OPR_PROXY_LOGGING_PROVIDER=$_APP_LOGGING_PROVIDER
- OPR_PROXY_LOGGING_CONFIG=$_APP_LOGGING_CONFIG
- OPR_PROXY_ALGORITHM=random
- OPR_PROXY_EXECUTORS=exc1
@@ -8,4 +8,4 @@ X-Appwrite-JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ...
{
"otp": "<OTP>"
}
}
@@ -1 +0,0 @@
Create a new build for an Appwrite Function deployment. This endpoint can be used to retry a failed build.
@@ -0,0 +1 @@
Delete a function execution by its unique ID.
@@ -0,0 +1 @@
Get a function template using ID. You can use template details in [createFunction](/docs/references/cloud/server-nodejs/functions#create) method.
@@ -0,0 +1 @@
List available function templates. You can use template details in [createFunction](/docs/references/cloud/server-nodejs/functions#create) method.
+1
View File
@@ -0,0 +1 @@
Use this endpoint to create a JSON Web Token for user by its unique ID. You can use the resulting JWT to authenticate on behalf of the user. The JWT secret will become invalid if the session it uses gets deleted.
@@ -0,0 +1,81 @@
<?php
namespace Appwrite\Auth\Validator;
use Utopia\Validator;
use Utopia\Validator\Text;
/**
* MockNumber.
*
* Validates if a given object represents a valid phone and OTP pair
*/
class MockNumber extends Validator
{
private $message = '';
/**
* Get Description.
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return $this->message;
}
/**
* Is valid.
*
* @param mixed $value
*
* @return bool
*/
public function isValid($value): bool
{
if (!\is_array($value) || !isset($value['phone']) || !isset($value['otp'])) {
$this->message = 'Invalid payload structure. Please check the "phone" and "otp" fields';
return false;
}
$phone = new Phone();
if (!$phone->isValid($value['phone'])) {
$this->message = $phone->getDescription();
return false;
}
$otp = new Text(6, 6, Text::NUMBERS);
if (!$otp->isValid($value['otp'])) {
$this->message = 'Invalid OTP. Please make sure the OTP is a 6 digit number';
return false;
}
return true;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_OBJECT;
}
}
+24
View File
@@ -14,6 +14,7 @@ class Func extends Event
protected string $path = '';
protected string $method = '';
protected array $headers = [];
protected ?string $functionId = null;
protected ?Document $function = null;
protected ?Document $execution = null;
@@ -49,6 +50,28 @@ class Func extends Event
return $this->function;
}
/**
* Sets function id for the function event.
*
* @param string $functionId
*/
public function setFunctionId(string $functionId): self
{
$this->functionId = $functionId;
return $this;
}
/**
* Returns set function id for the function event.
*
* @return string|null
*/
public function getFunctionId(): ?string
{
return $this->functionId;
}
/**
* Sets execution for the function event.
*
@@ -200,6 +223,7 @@ class Func extends Event
'project' => $this->project,
'user' => $this->user,
'function' => $this->function,
'functionId' => $this->functionId,
'execution' => $this->execution,
'type' => $this->type,
'jwt' => $this->jwt,
+6 -1
View File
@@ -107,6 +107,8 @@ class Exception extends \Exception
public const USER_TARGET_ALREADY_EXISTS = 'user_target_already_exists';
public const USER_API_KEY_AND_SESSION_SET = 'user_key_and_session_set';
public const API_KEY_EXPIRED = 'api_key_expired';
/** Teams */
public const TEAM_NOT_FOUND = 'team_not_found';
public const TEAM_INVITE_ALREADY_EXISTS = 'team_invite_already_exists';
@@ -154,6 +156,7 @@ class Exception extends \Exception
public const FUNCTION_RUNTIME_UNSUPPORTED = 'function_runtime_unsupported';
public const FUNCTION_ENTRYPOINT_MISSING = 'function_entrypoint_missing';
public const FUNCTION_SYNCHRONOUS_TIMEOUT = 'function_synchronous_timeout';
public const FUNCTION_TEMPLATE_NOT_FOUND = 'function_template_not_found';
/** Deployments */
public const DEPLOYMENT_NOT_FOUND = 'deployment_not_found';
@@ -162,9 +165,11 @@ class Exception extends \Exception
public const BUILD_NOT_FOUND = 'build_not_found';
public const BUILD_NOT_READY = 'build_not_ready';
public const BUILD_IN_PROGRESS = 'build_in_progress';
public const BUILD_ALREADY_COMPLETED = 'build_already_completed';
/** Execution */
public const EXECUTION_NOT_FOUND = 'execution_not_found';
public const EXECUTION_IN_PROGRESS = 'execution_in_progress';
/** Databases */
public const DATABASE_NOT_FOUND = 'database_not_found';
@@ -308,7 +313,7 @@ class Exception extends \Exception
$this->code = $code ?? $this->errors[$type]['code'];
// Mark string errors like HY001 from PDO as 500 errors
if(\is_string($this->code)) {
if (\is_string($this->code)) {
if (\is_numeric($this->code)) {
$this->code = (int) $this->code;
} else {
@@ -0,0 +1,103 @@
<?php
namespace Appwrite\Functions\Validator;
use Utopia\Validator;
/**
* Headers.
*
* Validates user provided headers
*/
class Headers extends Validator
{
protected bool $allowEmpty;
public function __construct(bool $allowEmpty = true)
{
$this->allowEmpty = $allowEmpty;
}
/**
* Get Description.
*
* Returns validator description
*
* @return string
*/
public function getDescription(): string
{
return 'Invalid header format. Header keys can only contain alphanumeric characters, underscores, and hyphens. Header keys cannot start with "x-appwrite-" prefix.';
}
/**
* Is valid.
*
* @param mixed $value
*
* @return bool
*/
public function isValid($value): bool
{
if ($this->allowEmpty && empty($value)) {
return true;
}
if (!\is_array($value)) {
return false;
}
if (\is_array($value)) {
foreach ($value as $key => $val) {
$length = \strlen($key);
// Reject non-string keys
if (!\is_string($key) || $length === 0) {
return false;
}
// Check first and last character
if (!ctype_alnum($key[0]) || !ctype_alnum($key[$length - 1])) {
return false;
}
// Check middle characters
for ($i = 1; $i < $length - 1; $i++) {
if (!ctype_alnum($key[$i]) && $key[$i] !== '-') {
return false;
}
}
// Check for x-appwrite- prefix
if (str_starts_with($key, 'x-appwrite-')) {
return false;
}
}
return true;
}
return false;
}
/**
* Is array
*
* Function will return true if object is array.
*
* @return bool
*/
public function isArray(): bool
{
return false;
}
/**
* Get Type
*
* Returns validator type.
*
* @return string
*/
public function getType(): string
{
return self::TYPE_OBJECT;
}
}
+1
View File
@@ -89,6 +89,7 @@ abstract class Migration
'1.5.8' => 'V20',
'1.5.9' => 'V20',
'1.5.10' => 'V20',
'1.6.0' => 'V21'
];
/**
+204
View File
@@ -0,0 +1,204 @@
<?php
namespace Appwrite\Migration\Version;
use Appwrite\Migration\Migration;
use Exception;
use Throwable;
use Utopia\CLI\Console;
use Utopia\Database\Database;
use Utopia\Database\DateTime;
use Utopia\Database\Document;
class V21 extends Migration
{
/**
* @throws Throwable
*/
public function execute(): void
{
/**
* Disable SubQueries for Performance.
*/
foreach (['subQueryIndexes', 'subQueryPlatforms', 'subQueryDomains', 'subQueryKeys', 'subQueryWebhooks', 'subQuerySessions', 'subQueryTokens', 'subQueryMemberships', 'subQueryVariables', 'subQueryChallenges', 'subQueryProjectVariables', 'subQueryTargets', 'subQueryTopicTargets'] as $name) {
Database::addFilter(
$name,
fn () => null,
fn () => []
);
}
Console::log('Migrating Project: ' . $this->project->getAttribute('name') . ' (' . $this->project->getId() . ')');
$this->projectDB->setNamespace("_{$this->project->getInternalId()}");
Console::info('Migrating Collections');
$this->migrateCollections();
if ($this->project->getInternalId() !== 'console') {
Console::info('Migrating Buckets');
$this->migrateBuckets();
}
Console::info('Migrating Documents');
$this->forEachDocument([$this, 'fixDocument']);
}
/**
* Migrate Collections.
*
* @return void
* @throws Exception|Throwable
*/
private function migrateCollections(): void
{
$internalProjectId = $this->project->getInternalId();
$collectionType = match ($internalProjectId) {
'console' => 'console',
default => 'projects',
};
$collections = $this->collections[$collectionType];
foreach ($collections as $collection) {
$id = $collection['$id'];
Console::log("Migrating Collection \"{$id}\"");
$this->projectDB->setNamespace("_$internalProjectId");
switch ($id) {
case 'projects':
// Create accessedAt attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'accessedAt');
} catch (Throwable $th) {
Console::warning("'accessedAt' from {$id}: {$th->getMessage()}");
}
break;
case 'schedules':
// Create data attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'data');
} catch (Throwable $th) {
Console::warning("'data' from {$id}: {$th->getMessage()}");
}
break;
case 'functions':
// Create scopes attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'scopes');
} catch (Throwable $th) {
Console::warning("'scopes' from {$id}: {$th->getMessage()}");
}
// Create specification attribute
try {
$this->createAttributeFromCollection($this->projectDB, $id, 'specification');
} catch (Throwable $th) {
Console::warning("'specification' from {$id}: {$th->getMessage()}");
}
break;
case 'executions':
// Create requestMethod index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_requestMethod');
} catch (\Throwable $th) {
Console::warning("'_key_requestMethod' from {$id}: {$th->getMessage()}");
}
// Create requestPath index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_requestPath');
} catch (\Throwable $th) {
Console::warning("'_key_requestPath' from {$id}: {$th->getMessage()}");
}
// Create deployment index
try {
$this->createIndexFromCollection($this->projectDB, $id, '_key_deployment');
} catch (\Throwable $th) {
Console::warning("'_key_deployment' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduledAt' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduledAt');
} catch (\Throwable $th) {
Console::warning("'scheduledAt' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleInternalId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleInternalId');
} catch (\Throwable $th) {
Console::warning("'scheduleInternalId' from {$id}: {$th->getMessage()}");
}
try {
/**
* Create 'scheduleId' attribute
*/
$this->createAttributeFromCollection($this->projectDB, $id, 'scheduleId');
} catch (\Throwable $th) {
Console::warning("'scheduleId' from {$id}: {$th->getMessage()}");
}
}
usleep(50000);
}
}
/**
* Fix run on each document
*
* @param Document $document
* @return Document
*/
protected function fixDocument(Document $document): Document
{
switch ($document->getCollection()) {
case 'projects':
/**
* Bump version number.
*/
$document->setAttribute('version', '1.6.0');
// Add accessedAt attribute
$document->setAttribute('accessedAt', DateTime::now());
break;
case 'functions':
// Add scopes attribute
$document->setAttribute('scopes', []);
// Add size attribute
$document->setAttribute('specification', APP_FUNCTION_BASE_SPECIFICATION);
}
return $document;
}
/**
* Migrating Buckets.
*
* @return void
*/
private function migrateBuckets()
{
foreach ($this->documentsIterator('buckets') as $bucket) {
$bucketId = 'bucket_' . $bucket['$internalId'];
try {
$this->projectDB->updateAttribute($bucketId, 'metadata', size: 75000);
$this->projectDB->purgeCachedCollection($bucketId);
} catch (\Throwable $th) {
Console::warning("'bucketId' from {$bucketId}: {$th->getMessage()}");
}
}
}
}
+2
View File
@@ -8,6 +8,7 @@ use Appwrite\Platform\Tasks\Maintenance;
use Appwrite\Platform\Tasks\Migrate;
use Appwrite\Platform\Tasks\QueueCount;
use Appwrite\Platform\Tasks\QueueRetry;
use Appwrite\Platform\Tasks\ScheduleExecutions;
use Appwrite\Platform\Tasks\ScheduleFunctions;
use Appwrite\Platform\Tasks\ScheduleMessages;
use Appwrite\Platform\Tasks\SDKs;
@@ -33,6 +34,7 @@ class Tasks extends Service
->addAction(SDKs::getName(), new SDKs())
->addAction(SSL::getName(), new SSL())
->addAction(ScheduleFunctions::getName(), new ScheduleFunctions())
->addAction(ScheduleExecutions::getName(), new ScheduleExecutions())
->addAction(ScheduleMessages::getName(), new ScheduleMessages())
->addAction(Specs::getName(), new Specs())
->addAction(Upgrade::getName(), new Upgrade())
+13 -5
View File
@@ -7,6 +7,7 @@ use Utopia\App;
use Utopia\CLI\Console;
use Utopia\Config\Config;
use Utopia\Domains\Domain;
use Utopia\DSN\DSN;
use Utopia\Logger\Logger;
use Utopia\Platform\Action;
use Utopia\Registry\Registry;
@@ -100,13 +101,20 @@ class Doctor extends Action
Console::log('🟢 HTTPS force option is enabled for function domains');
}
$providerName = System::getEnv('_APP_LOGGING_PROVIDER', '');
$providerConfig = System::getEnv('_APP_LOGGING_CONFIG', '');
if (empty($providerName) || empty($providerConfig) || !Logger::hasProvider($providerName)) {
Console::log('🔴 Logging adapter is disabled');
} else {
Console::log('🟢 Logging adapter is enabled (' . $providerName . ')');
try {
$loggingProvider = new DSN($providerConfig ?? '');
$providerName = $loggingProvider->getScheme();
if (empty($providerName) || !Logger::hasProvider($providerName)) {
Console::log('🔴 Logging adapter is disabled');
} else {
Console::log('🟢 Logging adapter is enabled (' . $providerName . ')');
}
} catch (\Throwable $th) {
Console::log('🔴 Logging adapter is misconfigured');
}
\usleep(200 * 1000); // Sleep for 0.2 seconds
+5 -2
View File
@@ -50,7 +50,7 @@ class SDKs extends Action
$production = ($git) ? (Console::confirm('Type "Appwrite" to push code to production git repos') == 'Appwrite') : false;
$message = ($git) ? Console::confirm('Please enter your commit message:') : '';
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', 'latest'])) {
if (!in_array($version, ['0.6.x', '0.7.x', '0.8.x', '0.9.x', '0.10.x', '0.11.x', '0.12.x', '0.13.x', '0.14.x', '0.15.x', '1.0.x', '1.1.x', '1.2.x', '1.3.x', '1.4.x', '1.5.x', '1.6.x', 'latest'])) {
throw new \Exception('Unknown version given');
}
@@ -235,7 +235,7 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
->setTwitter(APP_SOCIAL_TWITTER_HANDLE)
->setDiscord(APP_SOCIAL_DISCORD_CHANNEL, APP_SOCIAL_DISCORD)
->setDefaultHeaders([
'X-Appwrite-Response-Format' => '1.5.0',
'X-Appwrite-Response-Format' => '1.6.0',
]);
// Make sure we have a clean slate.
@@ -252,6 +252,9 @@ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
$gitUrl = $language['gitUrl'];
$gitBranch = $language['gitBranch'];
// TEMPORARY
$gitBranch = '1.6.x';
if (!$production) {
$gitUrl = 'git@github.com:aw-tests/' . $language['gitRepoName'] . '.git';
}
+4 -2
View File
@@ -64,7 +64,8 @@ abstract class ScheduleBase extends Action
$collectionId = match ($schedule->getAttribute('resourceType')) {
'function' => 'functions',
'message' => 'messages'
'message' => 'messages',
'execution' => 'executions'
};
$resource = $getProjectDB($project)->getDocument(
@@ -113,7 +114,8 @@ abstract class ScheduleBase extends Action
} catch (\Throwable $th) {
$collectionId = match ($document->getAttribute('resourceType')) {
'function' => 'functions',
'message' => 'messages'
'message' => 'messages',
'execution' => 'executions'
};
Console::error("Failed to load schedule for project {$document['projectId']} {$collectionId} {$document['resourceId']}");
@@ -0,0 +1,78 @@
<?php
namespace Appwrite\Platform\Tasks;
use Appwrite\Event\Func;
use Swoole\Coroutine as Co;
use Utopia\Database\Database;
use Utopia\Pools\Group;
class ScheduleExecutions extends ScheduleBase
{
public const UPDATE_TIMER = 3; // seconds
public const ENQUEUE_TIMER = 4; // seconds
public static function getName(): string
{
return 'schedule-executions';
}
public static function getSupportedResource(): string
{
return 'execution';
}
protected function enqueueResources(Group $pools, Database $dbForConsole): void
{
$queue = $pools->get('queue')->pop();
$connection = $queue->getResource();
$queueForFunctions = new Func($connection);
$intervalEnd = (new \DateTime())->modify('+' . self::ENQUEUE_TIMER . ' seconds');
foreach ($this->schedules as $schedule) {
if (!$schedule['active']) {
$dbForConsole->deleteDocument(
'schedules',
$schedule['$id'],
);
unset($this->schedules[$schedule['resourceId']]);
continue;
}
$scheduledAt = new \DateTime($schedule['schedule']);
if ($scheduledAt <= $intervalEnd) {
continue;
}
$delay = $scheduledAt->getTimestamp() - (new \DateTime())->getTimestamp();
\go(function () use ($queueForFunctions, $schedule, $delay) {
Co::sleep($delay);
$queueForFunctions
->setType('schedule')
// Set functionId instead of function as we don't have $dbForProject
// TODO: Refactor to use function instead of functionId
->setFunctionId($schedule['resource']['functionId'])
->setExecution($schedule['resource'])
->setMethod($schedule['data']['method'] ?? 'POST')
->setPath($schedule['data']['path'] ?? '/')
->setHeaders($schedule['data']['headers'] ?? [])
->setBody($schedule['data']['body'] ?? '')
->setProject($schedule['project'])
->trigger();
});
$dbForConsole->deleteDocument(
'schedules',
$schedule['$id'],
);
unset($this->schedules[$schedule['resourceId']]);
}
$queue->reclaim();
}
}
@@ -35,7 +35,7 @@ class ScheduleMessages extends ScheduleBase
continue;
}
\go(function () use ($now, $schedule, $pools, $dbForConsole) {
\go(function () use ($schedule, $pools, $dbForConsole) {
$queue = $pools->get('queue')->pop();
$connection = $queue->getResource();
$queueForMessaging = new Messaging($connection);
+1 -1
View File
@@ -259,7 +259,7 @@ class Specs extends Action
$specs = new Specification($formatInstance);
$endpoint = System::getEnv('_APP_HOME', '[HOSTNAME]');
$email = System::getEnv('_APP_SYSTEM_EMAIL_ADDRESS', APP_EMAIL_TEAM);
$email = System::getEnv('_APP_SYSTEM_TEAM_EMAIL', APP_EMAIL_TEAM);
$formatInstance
->setParam('name', APP_NAME)
+166 -37
View File
@@ -2,12 +2,14 @@
namespace Appwrite\Platform\Workers;
use Ahc\Jwt\JWT;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
use Appwrite\Messaging\Adapter\Realtime;
use Appwrite\Utopia\Response\Model\Deployment;
use Appwrite\Vcs\Comment;
use Exception;
use Executor\Executor;
use Swoole\Coroutine as Co;
use Utopia\Cache\Cache;
@@ -156,9 +158,9 @@ class Builds extends Action
$startTime = DateTime::now();
$durationStart = \microtime(true);
$buildId = $deployment->getAttribute('buildId', '');
$build = $dbForProject->getDocument('builds', $buildId);
$isNewBuild = empty($buildId);
if ($isNewBuild) {
if ($build->isEmpty()) {
$buildId = ID::unique();
$build = $dbForProject->createDocument('builds', new Document([
'$id' => $buildId,
@@ -180,6 +182,9 @@ class Builds extends Action
$deployment->setAttribute('buildId', $build->getId());
$deployment->setAttribute('buildInternalId', $build->getInternalId());
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
} elseif ($build->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
} else {
$build = $dbForProject->getDocument('builds', $buildId);
}
@@ -202,7 +207,65 @@ class Builds extends Action
}
try {
if ($isNewBuild && $isVcsEnabled) {
if ($isNewBuild && !$isVcsEnabled) {
// Non-vcs+Template
$templateRepositoryName = $template->getAttribute('repositoryName', '');
$templateOwnerName = $template->getAttribute('ownerName', '');
$templateVersion = $template->getAttribute('version', '');
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
$stdout = '';
$stderr = '';
// Clone template repo
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '-template';
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to clone code repository: ' . $stderr);
}
// Ensure directories
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
$tmpPathFile = $tmpTemplateDirectory . '/code.tar.gz';
$localDevice = new Local();
if (substr($tmpTemplateDirectory, -1) !== '/') {
$tmpTemplateDirectory .= '/';
}
$directorySize = $localDevice->getDirectorySize($tmpTemplateDirectory);
$functionsSizeLimit = (int)System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
if ($directorySize > $functionsSizeLimit) {
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
}
$tarParamDirectory = \escapeshellarg('/tmp/builds/' . $buildId . '-template' . (empty($templateRootDirectory) ? '' : '/' . $templateRootDirectory));
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
if (!$result) {
throw new \Exception("Unable to move file");
}
Console::execute('rm -rf ' . \escapeshellarg($tmpTemplateDirectory), '', $stdout, $stderr);
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source));
}
} elseif ($isNewBuild && $isVcsEnabled) {
// VCS and VCS+Temaplte
$tmpDirectory = '/tmp/builds/' . $buildId . '/code';
$rootDirectory = $function->getAttribute('providerRootDirectory', '');
$rootDirectory = \rtrim($rootDirectory, '/');
@@ -220,7 +283,7 @@ class Builds extends Action
$cloneVersion = $branchName;
$cloneType = GitHub::CLONE_TYPE_BRANCH;
if(!empty($commitHash)) {
if (!empty($commitHash)) {
$cloneVersion = $commitHash;
$cloneType = GitHub::CLONE_TYPE_COMMIT;
}
@@ -228,33 +291,49 @@ class Builds extends Action
$gitCloneCommand = $github->generateCloneCommand($cloneOwner, $cloneRepository, $cloneVersion, $cloneType, $tmpDirectory, $rootDirectory);
$stdout = '';
$stderr = '';
Console::execute('mkdir -p /tmp/builds/' . \escapeshellcmd($buildId), '', $stdout, $stderr);
Console::execute('mkdir -p ' . \escapeshellarg('/tmp/builds/' . $buildId), '', $stdout, $stderr);
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
$exit = Console::execute($gitCloneCommand, '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to clone code repository: ' . $stderr);
}
// Local refactoring for function folder with spaces
if (str_contains($rootDirectory, ' ')) {
$rootDirectoryWithoutSpaces = str_replace(' ', '', $rootDirectory);
$from = $tmpDirectory . '/' . $rootDirectory;
$to = $tmpDirectory . '/' . $rootDirectoryWithoutSpaces;
$exit = Console::execute('mv "' . \escapeshellarg($from) . '" "' . \escapeshellarg($to) . '"', '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to move function with spaces' . $stderr);
}
$rootDirectory = $rootDirectoryWithoutSpaces;
}
// Build from template
$templateRepositoryName = $template->getAttribute('repositoryName', '');
$templateOwnerName = $template->getAttribute('ownerName', '');
$templateBranch = $template->getAttribute('branch', '');
$templateVersion = $template->getAttribute('version', '');
$templateRootDirectory = $template->getAttribute('rootDirectory', '');
$templateRootDirectory = \rtrim($templateRootDirectory, '/');
$templateRootDirectory = \ltrim($templateRootDirectory, '.');
$templateRootDirectory = \ltrim($templateRootDirectory, '/');
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateBranch)) {
if (!empty($templateRepositoryName) && !empty($templateOwnerName) && !empty($templateVersion)) {
// Clone template repo
$tmpTemplateDirectory = '/tmp/builds/' . \escapeshellcmd($buildId) . '/template';
$tmpTemplateDirectory = '/tmp/builds/' . $buildId . '/template';
$cloneType = GitHub::CLONE_TYPE_BRANCH;
if(\str_starts_with($templateBranch, '0.1.')) { // Temporary fix for 1.5. In future versions this only support tag names
$cloneType = GitHub::CLONE_TYPE_TAG;
}
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateBranch, $cloneType, $tmpTemplateDirectory, $templateRootDirectory);
$gitCloneCommandForTemplate = $github->generateCloneCommand($templateOwnerName, $templateRepositoryName, $templateVersion, GitHub::CLONE_TYPE_TAG, $tmpTemplateDirectory, $templateRootDirectory);
$exit = Console::execute($gitCloneCommandForTemplate, '', $stdout, $stderr);
if ($exit !== 0) {
@@ -262,20 +341,20 @@ class Builds extends Action
}
// Ensure directories
Console::execute('mkdir -p ' . $tmpTemplateDirectory . '/' . $templateRootDirectory, '', $stdout, $stderr);
Console::execute('mkdir -p ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
Console::execute('mkdir -p ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory), '', $stdout, $stderr);
Console::execute('mkdir -p ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
// Merge template into user repo
Console::execute('rsync -av --exclude \'.git\' ' . $tmpTemplateDirectory . '/' . $templateRootDirectory . '/ ' . $tmpDirectory . '/' . $rootDirectory, '', $stdout, $stderr);
Console::execute('rsync -av --exclude \'.git\' ' . \escapeshellarg($tmpTemplateDirectory . '/' . $templateRootDirectory . '/') . ' ' . \escapeshellarg($tmpDirectory . '/' . $rootDirectory), '', $stdout, $stderr);
// Commit and push
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . $tmpDirectory . ' && git add . && git commit -m "Create \'' . \escapeshellcmd($function->getAttribute('name', '')) . '\' function" && git push origin ' . \escapeshellcmd($branchName), '', $stdout, $stderr);
$exit = Console::execute('git config --global user.email "team@appwrite.io" && git config --global user.name "Appwrite" && cd ' . \escapeshellarg($tmpDirectory) . ' && git add . && git commit -m "Create ' . \escapeshellarg($function->getAttribute('name', '')) . ' function" && git push origin ' . \escapeshellarg($branchName), '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to push code repository: ' . $stderr);
}
$exit = Console::execute('cd ' . $tmpDirectory . ' && git rev-parse HEAD', '', $stdout, $stderr);
$exit = Console::execute('cd ' . \escapeshellarg($tmpDirectory) . ' && git rev-parse HEAD', '', $stdout, $stderr);
if ($exit !== 0) {
throw new \Exception('Unable to get vcs commit SHA: ' . $stderr);
@@ -309,7 +388,7 @@ class Builds extends Action
);
}
$tmpPath = '/tmp/builds/' . \escapeshellcmd($buildId);
$tmpPath = '/tmp/builds/' . $buildId;
$tmpPathFile = $tmpPath . '/code.tar.gz';
$localDevice = new Local();
@@ -318,28 +397,25 @@ class Builds extends Action
}
$directorySize = $localDevice->getDirectorySize($tmpDirectory);
$functionsSizeLimit = (int) System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
$functionsSizeLimit = (int)System::getEnv('_APP_FUNCTIONS_SIZE_LIMIT', '30000000');
if ($directorySize > $functionsSizeLimit) {
throw new \Exception('Repository directory size should be less than ' . number_format($functionsSizeLimit / 1048576, 2) . ' MBs.');
}
Console::execute('tar --exclude code.tar.gz -czf ' . $tmpPathFile . ' -C /tmp/builds/' . \escapeshellcmd($buildId) . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory) . ' .', '', $stdout, $stderr);
$tarParamDirectory = '/tmp/builds/' . $buildId . '/code' . (empty($rootDirectory) ? '' : '/' . $rootDirectory);
Console::execute('tar --exclude code.tar.gz -czf ' . \escapeshellarg($tmpPathFile) . ' -C ' . \escapeshellcmd($tarParamDirectory) . ' .', '', $stdout, $stderr); // TODO: Replace escapeshellcmd with escapeshellarg if we find a way that doesnt break syntax
$path = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
$result = $localDevice->transfer($tmpPathFile, $path, $deviceForFunctions);
$source = $deviceForFunctions->getPath($deployment->getId() . '.' . \pathinfo('code.tar.gz', PATHINFO_EXTENSION));
$result = $localDevice->transfer($tmpPathFile, $source, $deviceForFunctions);
if (!$result) {
throw new \Exception("Unable to move file");
}
$deployment->setAttribute('path', $path);
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment);
Console::execute('rm -rf ' . $tmpPath, '', $stdout, $stderr);
$source = $path;
Console::execute('rm -rf ' . \escapeshellarg($tmpPath), '', $stdout, $stderr);
$build = $dbForProject->updateDocument('builds', $build->getId(), $build->setAttribute('source', $source));
$deployment = $dbForProject->updateDocument('deployments', $deployment->getId(), $deployment->setAttribute('path', $source));
$this->runGitAction('processing', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
}
@@ -362,8 +438,7 @@ class Builds extends Action
->setEvent('functions.[functionId].deployments.[deploymentId].update')
->setParam('functionId', $function->getId())
->setParam('deploymentId', $deployment->getId())
->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules())))
;
->setPayload($deployment->getArrayCopy(array_keys($deploymentModel->getRules())));
$deploymentUpdate->trigger();
@@ -400,14 +475,29 @@ class Builds extends Action
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$jwtExpiry = (int)System::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_API_KEY' => API_KEY_DYNAMIC . '_' . $apiKey,
'APPWRITE_FUNCTION_ID' => $function->getId(),
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deployment->getId(),
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
$command = $deployment->getAttribute('commands', '');
@@ -415,6 +505,11 @@ class Builds extends Action
$response = null;
$err = null;
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
Co::join([
Co\go(function () use ($executor, &$response, $project, $deployment, $source, $function, $runtime, $vars, $command, &$err) {
try {
@@ -442,8 +537,9 @@ class Builds extends Action
$executor->getLogs(
deploymentId: $deployment->getId(),
projectId: $project->getId(),
callback: function ($logs) use (&$response, &$build, $dbForProject, $allEvents, $project) {
if ($response === null) {
callback: function ($logs) use (&$response, &$err, &$build, $dbForProject, $allEvents, $project) {
// If we have response or error from concurrent coroutine, we already have latest logs
if ($response === null && $err === null) {
$build = $dbForProject->getDocument('builds', $build->getId());
if ($build->isEmpty()) {
@@ -456,8 +552,8 @@ class Builds extends Action
$build = $dbForProject->updateDocument('builds', $build->getId(), $build);
/**
* Send realtime Event
*/
* Send realtime Event
*/
$target = Realtime::fromPayload(
// Pass first, most verbose event pattern
event: $allEvents[0],
@@ -483,12 +579,21 @@ class Builds extends Action
]);
if ($err) {
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
throw $err;
}
$endTime = DateTime::now();
$durationEnd = \microtime(true);
$buildSizeLimit = (int)System::getEnv('_APP_FUNCTIONS_BUILD_SIZE_LIMIT', '2000000000');
if ($response['size'] > $buildSizeLimit) {
throw new \Exception('Build size should be less than ' . number_format($buildSizeLimit / 1048576, 2) . ' MBs.');
}
/** Update the build document */
$build->setAttribute('startTime', DateTime::format((new \DateTime())->setTimestamp(floor($response['startTime']))));
$build->setAttribute('endTime', $endTime);
@@ -512,6 +617,11 @@ class Builds extends Action
$function = $dbForProject->updateDocument('functions', $function->getId(), $function);
}
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
/** Update function schedule */
// Inform scheduler if function is still active
@@ -522,12 +632,17 @@ class Builds extends Action
->setAttribute('active', !empty($function->getAttribute('schedule')) && !empty($function->getAttribute('deployment')));
Authorization::skip(fn () => $dbForConsole->updateDocument('schedules', $schedule->getId(), $schedule));
} catch (\Throwable $th) {
if ($dbForProject->getDocument('builds', $buildId)->getAttribute('status') === 'canceled') {
Console::info('Build has been canceled');
return;
}
$endTime = DateTime::now();
$durationEnd = \microtime(true);
$build->setAttribute('endTime', $endTime);
$build->setAttribute('duration', \intval(\ceil($durationEnd - $durationStart)));
$build->setAttribute('status', 'failed');
$build->setAttribute('logs', $th->getMessage() . "\n" . $th->getFile() . ':' . $th->getLine() . "\n" . $th->getTraceAsString());
$build->setAttribute('logs', $th->getMessage());
if ($isVcsEnabled) {
$this->runGitAction('failed', $github, $providerCommitHash, $owner, $repositoryName, $project, $function, $deployment->getId(), $dbForProject, $dbForConsole);
@@ -553,6 +668,20 @@ class Builds extends Action
);
/** Trigger usage queue */
if ($build->getAttribute('status') === 'ready') {
$queueForUsage
->addMetric(METRIC_BUILDS_SUCCESS, 1) // per project
->addMetric(METRIC_BUILDS_COMPUTE_SUCCESS, (int)$build->getAttribute('duration', 0) * 1000)
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_SUCCESS), 1) // per function
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS), (int)$build->getAttribute('duration', 0) * 1000);
} elseif ($build->getAttribute('status') === 'failed') {
$queueForUsage
->addMetric(METRIC_BUILDS_FAILED, 1) // per project
->addMetric(METRIC_BUILDS_COMPUTE_FAILED, (int)$build->getAttribute('duration', 0) * 1000)
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_FAILED), 1) // per function
->addMetric(str_replace('{functionInternalId}', $function->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED), (int)$build->getAttribute('duration', 0) * 1000);
}
$queueForUsage
->addMetric(METRIC_BUILDS, 1) // per project
->addMetric(METRIC_BUILDS_STORAGE, $build->getAttribute('size', 0))
+15 -11
View File
@@ -7,7 +7,7 @@ use Appwrite\Extend\Exception;
use Executor\Executor;
use Throwable;
use Utopia\Abuse\Abuse;
use Utopia\Abuse\Adapters\Database\TimeLimit;
use Utopia\Abuse\Adapters\Database as AbuseDatabase;
use Utopia\Audit\Audit;
use Utopia\Cache\Adapter\Filesystem;
use Utopia\Cache\Cache;
@@ -95,12 +95,6 @@ class Deletes extends Action
case DELETE_TYPE_USERS:
$this->deleteUser($getProjectDB, $document, $project);
break;
case DELETE_TYPE_TEAMS:
$this->deleteMemberships($getProjectDB, $document, $project);
if ($project->getId() === 'console') {
$this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $document);
}
break;
case DELETE_TYPE_BUCKETS:
$this->deleteBucket($getProjectDB, $deviceForFiles, $document, $project);
break;
@@ -115,6 +109,9 @@ class Deletes extends Action
break;
}
break;
case DELETE_TYPE_TEAM_PROJECTS:
$this->deleteProjectsByTeam($dbForConsole, $getProjectDB, $document);
break;
case DELETE_TYPE_EXECUTIONS:
$this->deleteExecutionLogs($project, $getProjectDB, $executionRetention);
break;
@@ -419,7 +416,7 @@ class Deletes extends Action
* @return void
* @throws Exception
*/
private function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void
public function deleteMemberships(callable $getProjectDB, Document $document, Document $project): void
{
$dbForProject = $getProjectDB($project);
$teamInternalId = $document->getInternalId();
@@ -447,14 +444,21 @@ class Deletes extends Action
* @throws Conflict
* @throws Restricted
* @throws Structure
* @throws Exception
*/
private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, Device $deviceForFiles, Device $deviceForFunctions, Device $deviceForBuilds, Device $deviceForCache, Document $document): void
private function deleteProjectsByTeam(Database $dbForConsole, callable $getProjectDB, Document $document): void
{
$projects = $dbForConsole->find('projects', [
Query::equal('teamInternalId', [$document->getInternalId()])
]);
foreach ($projects as $project) {
$deviceForFiles = getDevice(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
$deviceForFunctions = getDevice(APP_STORAGE_FUNCTIONS . '/app-' . $project->getId());
$deviceForBuilds = getDevice(APP_STORAGE_BUILDS . '/app-' . $project->getId());
$deviceForCache = getDevice(APP_STORAGE_CACHE . '/app-' . $project->getId());
$this->deleteProject($dbForConsole, $getProjectDB, $deviceForFiles, $deviceForFunctions, $deviceForBuilds, $deviceForCache, $project);
$dbForConsole->deleteDocument('projects', $project->getId());
}
@@ -489,7 +493,7 @@ class Deletes extends Action
$projectCollectionIds = [
...\array_keys(Config::getParam('collections', [])['projects']),
Audit::COLLECTION,
TimeLimit::COLLECTION,
AbuseDatabase::COLLECTION,
];
$limit = \count($projectCollectionIds) + 25;
@@ -682,7 +686,7 @@ class Deletes extends Action
{
$projectId = $project->getId();
$dbForProject = $getProjectDB($project);
$timeLimit = new TimeLimit("", 0, 1, $dbForProject);
$timeLimit = new AbuseDatabase("", 0, 1, $dbForProject);
$abuse = new Abuse($timeLimit);
try {
+34 -12
View File
@@ -2,6 +2,7 @@
namespace Appwrite\Platform\Workers;
use Ahc\Jwt\JWT;
use Appwrite\Event\Event;
use Appwrite\Event\Func;
use Appwrite\Event\Usage;
@@ -82,6 +83,7 @@ class Functions extends Action
$eventData = $payload['payload'] ?? '';
$project = new Document($payload['project'] ?? []);
$function = new Document($payload['function'] ?? []);
$functionId = $payload['functionId'] ?? '';
$user = new Document($payload['user'] ?? []);
$method = $payload['method'] ?? 'POST';
$headers = $payload['headers'] ?? [];
@@ -91,6 +93,10 @@ class Functions extends Action
return;
}
if ($function->isEmpty() && !empty($functionId)) {
$function = $dbForProject->getDocument('functions', $functionId);
}
$log->addTag('functionId', $function->getId());
$log->addTag('projectId', $project->getId());
$log->addTag('type', $type);
@@ -175,6 +181,7 @@ class Functions extends Action
);
break;
case 'schedule':
$execution = new Document($payload['execution'] ?? []);
$this->execute(
log: $log,
dbForProject: $dbForProject,
@@ -192,7 +199,7 @@ class Functions extends Action
jwt: null,
event: null,
eventData: null,
executionId: null,
executionId: $execution->getId() ?? null
);
break;
}
@@ -354,10 +361,21 @@ class Functions extends Action
$runtime = $runtimes[$function->getAttribute('runtime')];
$jwtExpiry = $function->getAttribute('timeout', 900);
$jwtObj = new JWT(System::getEnv('_APP_OPENSSL_KEY_V1'), 'HS256', $jwtExpiry, 0);
$apiKey = $jwtObj->encode([
'projectId' => $project->getId(),
'scopes' => $function->getAttribute('scopes', [])
]);
$headers['x-appwrite-key'] = API_KEY_DYNAMIC . '_' . $apiKey;
$headers['x-appwrite-trigger'] = $trigger;
$headers['x-appwrite-event'] = $event ?? '';
$headers['x-appwrite-user-id'] = $user->getId() ?? '';
$headers['x-appwrite-user-jwt'] = $jwt ?? '';
$headers['x-appwrite-country-code'] = '';
$headers['x-appwrite-continent-code'] = '';
$headers['x-appwrite-continent-eu'] = 'false';
/** Create execution or update execution status */
$execution = $dbForProject->getDocument('executions', $executionId ?? '');
@@ -390,9 +408,7 @@ class Functions extends Action
'search' => implode(' ', [$functionId, $executionId]),
]);
if ($function->getAttribute('logging')) {
$execution = $dbForProject->createDocument('executions', $execution);
}
$execution = $dbForProject->createDocument('executions', $execution);
// TODO: @Meldiron Trigger executions.create event here
@@ -404,9 +420,7 @@ class Functions extends Action
if ($execution->getAttribute('status') !== 'processing') {
$execution->setAttribute('status', 'processing');
if ($function->getAttribute('logging')) {
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
}
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
}
$durationStart = \microtime(true);
@@ -440,14 +454,21 @@ class Functions extends Action
$vars[$var->getAttribute('key')] = $var->getAttribute('value', '');
}
$protocol = System::getEnv('_APP_OPTIONS_FORCE_HTTPS') == 'disabled' ? 'http' : 'https';
$hostname = System::getEnv('_APP_DOMAIN');
$endpoint = $protocol . '://' . $hostname . "/v1";
// Appwrite vars
$vars = \array_merge($vars, [
'APPWRITE_FUNCTION_API_ENDPOINT' => $endpoint,
'APPWRITE_FUNCTION_ID' => $functionId,
'APPWRITE_FUNCTION_NAME' => $function->getAttribute('name'),
'APPWRITE_FUNCTION_DEPLOYMENT' => $deploymentId,
'APPWRITE_FUNCTION_PROJECT_ID' => $project->getId(),
'APPWRITE_FUNCTION_RUNTIME_NAME' => $runtime['name'] ?? '',
'APPWRITE_FUNCTION_RUNTIME_VERSION' => $runtime['version'] ?? '',
'APPWRITE_VERSION' => APP_VERSION_STABLE,
'APPWRITE_REGION' => $project->getAttribute('region'),
]);
/** Execute function */
@@ -469,10 +490,11 @@ class Functions extends Action
path: $path,
method: $method,
headers: $headers,
runtimeEntrypoint: $command
runtimeEntrypoint: $command,
logging: $function->getAttribute('logging', true),
);
$status = $executionResponse['statusCode'] >= 400 ? 'failed' : 'completed';
$status = $executionResponse['statusCode'] >= 500 ? 'failed' : 'completed';
$headersFiltered = [];
foreach ($executionResponse['headers'] as $key => $value) {
@@ -513,9 +535,9 @@ class Functions extends Action
;
}
if ($function->getAttribute('logging')) {
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
}
$execution = $dbForProject->updateDocument('executions', $executionId, $execution);
/** Trigger Webhook */
$executionModel = new Execution();
$queueForEvents
+1 -1
View File
@@ -669,7 +669,7 @@ class Messaging extends Action
private function getLocalDevice($project): Local
{
if($this->localDevice === null) {
if ($this->localDevice === null) {
$this->localDevice = new Local(APP_STORAGE_UPLOADS . '/app-' . $project->getId());
}
+32
View File
@@ -184,8 +184,12 @@ class Usage extends Action
$deployments = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS)));
$deploymentsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace(['{resourceType}', '{resourceInternalId}'], ['functions', $document->getInternalId()], METRIC_FUNCTION_ID_DEPLOYMENTS_STORAGE)));
$builds = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS)));
$buildsSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_SUCCESS)));
$buildsFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_FAILED)));
$buildsStorage = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_STORAGE)));
$buildsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE)));
$buildsComputeSuccess = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_SUCCESS)));
$buildsComputeFailed = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_BUILDS_COMPUTE_FAILED)));
$executions = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS)));
$executionsCompute = $dbForProject->getDocument('stats', md5(self::INFINITY_PERIOD . str_replace('{functionInternalId}', $document->getInternalId(), METRIC_FUNCTION_ID_EXECUTIONS_COMPUTE)));
@@ -210,6 +214,20 @@ class Usage extends Action
];
}
if (!empty($buildsSuccess['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_SUCCESS,
'value' => ($buildsSuccess['value'] * -1),
];
}
if (!empty($buildsFailed['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_FAILED,
'value' => ($buildsFailed['value'] * -1),
];
}
if (!empty($buildsStorage['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_STORAGE,
@@ -224,6 +242,20 @@ class Usage extends Action
];
}
if (!empty($buildsComputeSuccess['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_COMPUTE_SUCCESS,
'value' => ($buildsComputeSuccess['value'] * -1),
];
}
if (!empty($buildsComputeFailed['value'])) {
$metrics[] = [
'key' => METRIC_BUILDS_COMPUTE_FAILED,
'value' => ($buildsComputeFailed['value'] * -1),
];
}
if (!empty($executions['value'])) {
$metrics[] = [
'key' => METRIC_EXECUTIONS,
@@ -286,7 +286,13 @@ class Swagger2 extends Format
$validator = $validator->getValidator();
}
switch ((!empty($validator)) ? \get_class($validator) : '') {
$validatorClass = (!empty($validator)) ? \get_class($validator) : '';
if ($validatorClass === 'Utopia\Validator\AnyOf') {
$validator = $param['validator']->getValidators()[0];
$validatorClass = \get_class($validator);
}
switch ($validatorClass) {
case 'Utopia\Validator\Text':
case 'Utopia\Database\Validator\UID':
$node['type'] = $validator->getType();
@@ -9,7 +9,9 @@ class Deployments extends Base
'buildId',
'activate',
'entrypoint',
'commands'
'commands',
'type',
'size'
];
/**
@@ -8,7 +8,10 @@ class Executions extends Base
'trigger',
'status',
'responseStatusCode',
'duration'
'duration',
'requestMethod',
'requestPath',
'deploymentId'
];
/**
+151
View File
@@ -0,0 +1,151 @@
<?php
namespace Appwrite\Utopia\Fetch;
class BodyMultipart
{
/**
* @var array<string, mixed> $parts
*/
private array $parts = [];
private string $boundary = "";
public function __construct(string $boundary = null)
{
if (is_null($boundary)) {
$this->boundary = self::generateBoundary();
} else {
$this->boundary = $boundary;
}
}
public static function generateBoundary(): string
{
return '-----------------------------' . \uniqid();
}
public function load(string $body): self
{
$eol = "\r\n";
$sections = \explode('--' . $this->boundary, $body);
foreach ($sections as $section) {
if (empty($section)) {
continue;
}
if (strpos($section, $eol) === 0) {
$section = substr($section, \strlen($eol));
}
if (substr($section, -2) === $eol) {
$section = substr($section, 0, -1 * \strlen($eol));
}
if ($section == '--') {
continue;
}
$partChunks = \explode($eol . $eol, $section, 2);
if (\count($partChunks) < 2) {
continue; // Broken part
}
[ $partHeaders, $partBody ] = $partChunks;
$partHeaders = \explode($eol, $partHeaders);
$partName = "";
foreach ($partHeaders as $partHeader) {
if (!empty($partName)) {
break;
}
$partHeaderArray = \explode(':', $partHeader, 2);
$partHeaderName = \strtolower($partHeaderArray[0] ?? '');
$partHeaderValue = $partHeaderArray[1] ?? '';
if ($partHeaderName == "content-disposition") {
$dispositionChunks = \explode("; ", $partHeaderValue);
foreach ($dispositionChunks as $dispositionChunk) {
$dispositionChunkValues = \explode("=", $dispositionChunk, 2);
if (\count($dispositionChunkValues) >= 2) {
if ($dispositionChunkValues[0] === "name") {
$partName = \trim($dispositionChunkValues[1], "\"");
break;
}
}
}
}
}
if (!empty($partName)) {
$this->parts[$partName] = $partBody;
}
}
return $this;
}
/**
* @return array<string, mixed>
*/
public function getParts(): array
{
return $this->parts ?? [];
}
public function getPart(string $key, mixed $default = ''): mixed
{
return $this->parts[$key] ?? $default;
}
public function setPart(string $key, mixed $value): self
{
$this->parts[$key] = $value;
return $this;
}
public function getBoundary(): string
{
return $this->boundary;
}
public function setBoundary(string $boundary): self
{
$this->boundary = $boundary;
return $this;
}
public function exportHeader(): string
{
return 'multipart/form-data; boundary=' . $this->boundary;
}
public function exportBody(): string
{
$eol = "\r\n";
$query = '--' . $this->boundary;
foreach ($this->parts as $key => $value) {
$query .= $eol . 'Content-Disposition: form-data; name="' . $key . '"';
if (\is_array($value)) {
$query .= $eol . 'Content-Type: application/json';
$value = \json_encode($value);
}
$query .= $eol . $eol;
if ($value === false) {
$query .= 0 . $eol;
} else {
$query .= $value . $eol;
}
$query .= '--' . $this->boundary;
}
$query .= "--" . $eol;
return $query;
}
}
@@ -0,0 +1,24 @@
<?php
namespace Appwrite\Utopia\Request\Filters;
use Appwrite\Utopia\Request\Filter;
class V18 extends Filter
{
// Convert 1.5 params to 1.6
public function parse(array $content, string $model): array
{
switch ($model) {
case 'account.deleteMfaAuthenticator':
unset($content['otp']);
break;
case 'functions.create':
$content['templateVersion'] = $content['templateBranch'] ?? "";
unset($content['templateBranch']);
break;
}
return $content;
}
}
+75 -1
View File
@@ -2,6 +2,7 @@
namespace Appwrite\Utopia;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Appwrite\Utopia\Response\Filter;
use Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response\Model\Account;
@@ -72,6 +73,7 @@ use Appwrite\Utopia\Response\Model\Migration;
use Appwrite\Utopia\Response\Model\MigrationFirebaseProject;
use Appwrite\Utopia\Response\Model\MigrationReport;
use Appwrite\Utopia\Response\Model\Mock;
use Appwrite\Utopia\Response\Model\MockNumber;
use Appwrite\Utopia\Response\Model\None;
use Appwrite\Utopia\Response\Model\Phone;
use Appwrite\Utopia\Response\Model\Platform;
@@ -86,7 +88,10 @@ use Appwrite\Utopia\Response\Model\Subscriber;
use Appwrite\Utopia\Response\Model\Target;
use Appwrite\Utopia\Response\Model\Team;
use Appwrite\Utopia\Response\Model\TemplateEmail;
use Appwrite\Utopia\Response\Model\TemplateFunction;
use Appwrite\Utopia\Response\Model\TemplateRuntime;
use Appwrite\Utopia\Response\Model\TemplateSMS;
use Appwrite\Utopia\Response\Model\TemplateVariable;
use Appwrite\Utopia\Response\Model\Token;
use Appwrite\Utopia\Response\Model\Topic;
use Appwrite\Utopia\Response\Model\UsageBuckets;
@@ -100,8 +105,10 @@ use Appwrite\Utopia\Response\Model\UsageStorage;
use Appwrite\Utopia\Response\Model\UsageUsers;
use Appwrite\Utopia\Response\Model\User;
use Appwrite\Utopia\Response\Model\Variable;
use Appwrite\Utopia\Response\Model\VcsContent;
use Appwrite\Utopia\Response\Model\Webhook;
use Exception;
use JsonException;
use Swoole\Http\Response as SwooleHTTPResponse;
// Keep last
use Utopia\Database\Document;
@@ -233,6 +240,8 @@ class Response extends SwooleResponse
public const MODEL_BRANCH = 'branch';
public const MODEL_BRANCH_LIST = 'branchList';
public const MODEL_DETECTION = 'detection';
public const MODEL_VCS_CONTENT = 'vcsContent';
public const MODEL_VCS_CONTENT_LIST = 'vcsContentList';
// Functions
public const MODEL_FUNCTION = 'function';
@@ -247,6 +256,10 @@ class Response extends SwooleResponse
public const MODEL_BUILD_LIST = 'buildList'; // Not used anywhere yet
public const MODEL_FUNC_PERMISSIONS = 'funcPermissions';
public const MODEL_HEADERS = 'headers';
public const MODEL_TEMPLATE_FUNCTION = 'templateFunction';
public const MODEL_TEMPLATE_FUNCTION_LIST = 'templateFunctionList';
public const MODEL_TEMPLATE_RUNTIME = 'templateRuntime';
public const MODEL_TEMPLATE_VARIABLE = 'templateVariable';
// Proxy
public const MODEL_PROXY_RULE = 'proxyRule';
@@ -266,6 +279,7 @@ class Response extends SwooleResponse
public const MODEL_WEBHOOK_LIST = 'webhookList';
public const MODEL_KEY = 'key';
public const MODEL_KEY_LIST = 'keyList';
public const MODEL_MOCK_NUMBER = 'mockNumber';
public const MODEL_AUTH_PROVIDER = 'authProvider';
public const MODEL_AUTH_PROVIDER_LIST = 'authProviderList';
public const MODEL_PLATFORM = 'platform';
@@ -335,6 +349,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Teams List', self::MODEL_TEAM_LIST, 'teams', self::MODEL_TEAM))
->setModel(new BaseList('Memberships List', self::MODEL_MEMBERSHIP_LIST, 'memberships', self::MODEL_MEMBERSHIP))
->setModel(new BaseList('Functions List', self::MODEL_FUNCTION_LIST, 'functions', self::MODEL_FUNCTION))
->setModel(new BaseList('Function Templates List', self::MODEL_TEMPLATE_FUNCTION_LIST, 'templates', self::MODEL_TEMPLATE_FUNCTION))
->setModel(new BaseList('Installations List', self::MODEL_INSTALLATION_LIST, 'installations', self::MODEL_INSTALLATION))
->setModel(new BaseList('Provider Repositories List', self::MODEL_PROVIDER_REPOSITORY_LIST, 'providerRepositories', self::MODEL_PROVIDER_REPOSITORY))
->setModel(new BaseList('Branches List', self::MODEL_BRANCH_LIST, 'branches', self::MODEL_BRANCH))
@@ -364,6 +379,7 @@ class Response extends SwooleResponse
->setModel(new BaseList('Target list', self::MODEL_TARGET_LIST, 'targets', self::MODEL_TARGET))
->setModel(new BaseList('Migrations List', self::MODEL_MIGRATION_LIST, 'migrations', self::MODEL_MIGRATION))
->setModel(new BaseList('Migrations Firebase Projects List', self::MODEL_MIGRATION_FIREBASE_PROJECT_LIST, 'projects', self::MODEL_MIGRATION_FIREBASE_PROJECT))
->setModel(new BaseList('VCS Content List', self::MODEL_VCS_CONTENT_LIST, 'contents', self::MODEL_VCS_CONTENT))
// Entities
->setModel(new Database())
->setModel(new Collection())
@@ -403,9 +419,13 @@ class Response extends SwooleResponse
->setModel(new Team())
->setModel(new Membership())
->setModel(new Func())
->setModel(new TemplateFunction())
->setModel(new TemplateRuntime())
->setModel(new TemplateVariable())
->setModel(new Installation())
->setModel(new ProviderRepository())
->setModel(new Detection())
->setModel(new VcsContent())
->setModel(new Branch())
->setModel(new Runtime())
->setModel(new Deployment())
@@ -414,6 +434,7 @@ class Response extends SwooleResponse
->setModel(new Project())
->setModel(new Webhook())
->setModel(new Key())
->setModel(new MockNumber())
->setModel(new AuthProvider())
->setModel(new Platform())
->setModel(new Variable())
@@ -467,6 +488,7 @@ class Response extends SwooleResponse
*/
public const CONTENT_TYPE_YAML = 'application/x-yaml';
public const CONTENT_TYPE_NULL = 'null';
public const CONTENT_TYPE_MULTIPART = 'multipart/form-data';
/**
* List of defined output objects
@@ -537,7 +559,11 @@ class Response extends SwooleResponse
switch ($this->getContentType()) {
case self::CONTENT_TYPE_JSON:
$this->json(!empty($output) ? $output : new \stdClass());
try {
$this->json(!empty($output) ? $output : new \stdClass());
} catch (JsonException $e) {
throw new Exception('Failed to parse response: ' . $e->getMessage(), 400);
}
break;
case self::CONTENT_TYPE_YAML:
@@ -547,6 +573,10 @@ class Response extends SwooleResponse
case self::CONTENT_TYPE_NULL:
break;
case self::CONTENT_TYPE_MULTIPART:
$this->multipart(!empty($output) ? $output : new \stdClass());
break;
default:
if ($model === self::MODEL_NONE) {
$this->noContent();
@@ -678,6 +708,50 @@ class Response extends SwooleResponse
->send(\yaml_emit($data, YAML_UTF8_ENCODING));
}
/**
* Multipart
*
* This helper is for sending multipart/form-data HTTP response.
* It sets relevant content type header ('multipart/form-data') and convert a PHP array ($data) to valid Multipart using BodyMultipart
*
* @param array $data
*
* @return void
*/
public function multipart(array $data): void
{
$multipart = new BodyMultipart();
foreach ($data as $key => $value) {
$multipart->setPart($key, $value);
}
$this
->setContentType($multipart->exportHeader())
->send($multipart->exportBody());
}
/**
* JSON
*
* This helper is for sending JSON HTTP response.
* It sets relevant content type header ('application/json') and convert a PHP array ($data) to valid JSON using native json_encode
*
* @see http://en.wikipedia.org/wiki/JSON
*
* @param mixed $data
* @return void
*/
public function json($data): void
{
if (!is_array($data) && !$data instanceof \stdClass) {
throw new \Exception('Response body is not a valid JSON object.');
}
$this
->setContentType(Response::CONTENT_TYPE_JSON, self::CHARSET_UTF8)
->send(\json_encode($data, JSON_UNESCAPED_UNICODE | JSON_THROW_ON_ERROR));
}
/**
* @return array
*/
@@ -0,0 +1,49 @@
<?php
namespace Appwrite\Utopia\Response\Filters;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Filter;
class V18 extends Filter
{
// Convert 1.6 Data format to 1.5 format
public function parse(array $content, string $model): array
{
$parsedResponse = $content;
$parsedResponse = match($model) {
Response::MODEL_FUNCTION => $this->parseFunction($content),
Response::MODEL_EXECUTION => $this->parseExecution($content),
Response::MODEL_PROJECT => $this->parseProject($content),
default => $parsedResponse,
};
return $parsedResponse;
}
protected function parseExecution(array $content)
{
if (!empty($content['status']) && !empty($content['statusCode'])) {
if ($content['status'] === 'completed' && $content['statusCode'] >= 400 && $content['statusCode'] < 500) {
$content['status'] = 'failed';
}
}
unset($content['scheduledAt']);
return $content;
}
protected function parseFunction(array $content)
{
unset($content['scopes']);
return $content;
}
protected function parseProject(array $content)
{
unset($content['authMockNumbers']);
unset($content['authSessionAlerts']);
return $content;
}
}
@@ -4,6 +4,7 @@ namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
use Utopia\Database\DateTime;
use Utopia\Database\Helpers\Role;
class Execution extends Model
@@ -110,6 +111,13 @@ class Execution extends Model
'default' => 0,
'example' => 0.400,
])
->addRule('scheduledAt', [
'type' => self::TYPE_DATETIME,
'description' => 'The scheduled time for execution. If left empty, execution will be queued immediately.',
'required' => false,
'default' => DateTime::now(),
'example' => self::TYPE_DATETIME_EXAMPLE,
])
;
}
+8 -1
View File
@@ -71,6 +71,13 @@ class Func extends Model
'default' => '',
'example' => '5e5ea5c16897e',
])
->addRule('scopes', [
'type' => self::TYPE_STRING,
'description' => 'Allowed permission scopes.',
'default' => [],
'example' => 'users.read',
'array' => true,
])
->addRule('vars', [
'type' => Response::MODEL_VARIABLE,
'description' => 'Function variables.',
@@ -112,7 +119,7 @@ class Func extends Model
->addRule('version', [
'type' => self::TYPE_STRING,
'description' => 'Version of Open Runtimes used for the function.',
'default' => 'v3',
'default' => 'v4',
'example' => 'v2',
])
->addRule('installationId', [
+1 -1
View File
@@ -60,7 +60,7 @@ class Key extends Model
])
->addRule('accessedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_KEY_ACCCESS / 60 / 60 . ' hours.',
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_KEY_ACCESS / 60 / 60 . ' hours.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE
])
@@ -0,0 +1,47 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class MockNumber extends Model
{
public function __construct()
{
$this
->addRule('phone', [
'type' => self::TYPE_STRING,
'description' => 'Mock phone number for testing phone authentication. Useful for testing phone authentication without sending an SMS.',
'default' => '',
'example' => '+1612842323',
])
->addRule('otp', [
'type' => self::TYPE_STRING,
'description' => 'Mock OTP for the number. ',
'default' => '',
'example' => '123456',
])
;
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Mock Number';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_MOCK_NUMBER;
}
}
@@ -138,6 +138,19 @@ class Project extends Model
'default' => false,
'example' => true,
])
->addRule('authMockNumbers', [
'type' => Response::MODEL_MOCK_NUMBER,
'description' => 'An array of mock numbers and their corresponding verification codes (OTPs).',
'default' => [],
'array' => true,
'example' => [new \stdClass()],
])
->addRule('authSessionAlerts', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Whether or not to send session alert emails to users.',
'default' => false,
'example' => true,
])
->addRule('oAuthProviders', [
'type' => Response::MODEL_AUTH_PROVIDER,
'description' => 'List of Auth Providers.',
@@ -321,6 +334,8 @@ class Project extends Model
$document->setAttribute('authPasswordHistory', $authValues['passwordHistory'] ?? 0);
$document->setAttribute('authPasswordDictionary', $authValues['passwordDictionary'] ?? false);
$document->setAttribute('authPersonalDataCheck', $authValues['personalDataCheck'] ?? false);
$document->setAttribute('authMockNumbers', $authValues['mockNumbers'] ?? []);
$document->setAttribute('authSessionAlerts', $authValues['sessionAlerts'] ?? false);
foreach ($auth as $index => $method) {
$key = $method['key'];
@@ -16,6 +16,12 @@ class Runtime extends Model
'default' => '',
'example' => 'python-3.8',
])
->addRule('key', [
'type' => self::TYPE_STRING,
'description' => 'Parent runtime key.',
'default' => '',
'example' => 'python',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Runtime Name.',
@@ -0,0 +1,142 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class TemplateFunction extends Model
{
public function __construct()
{
$this
->addRule('icon', [
'type' => self::TYPE_STRING,
'description' => 'Function Template Icon.',
'default' => '',
'example' => 'icon-lightning-bolt',
])
->addRule('id', [
'type' => self::TYPE_STRING,
'description' => 'Function Template ID.',
'default' => '',
'example' => 'starter',
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Function Template Name.',
'default' => '',
'example' => 'Starter function',
])
->addRule('tagline', [
'type' => self::TYPE_STRING,
'description' => 'Function Template Tagline.',
'default' => '',
'example' => 'A simple function to get started.',
])
->addRule('permissions', [
'type' => self::TYPE_STRING,
'description' => 'Execution permissions.',
'default' => [],
'example' => 'any',
'array' => true,
])
->addRule('events', [
'type' => self::TYPE_STRING,
'description' => 'Function trigger events.',
'default' => [],
'example' => 'account.create',
'array' => true,
])
->addRule('cron', [
'type' => self::TYPE_STRING,
'description' => 'Function execution schedult in CRON format.',
'default' => '',
'example' => '0 0 * * *',
])
->addRule('timeout', [
'type' => self::TYPE_INTEGER,
'description' => 'Function execution timeout in seconds.',
'default' => 15,
'example' => 300,
])
->addRule('useCases', [
'type' => self::TYPE_STRING,
'description' => 'Function use cases.',
'default' => [],
'example' => 'Starter',
'array' => true,
])
->addRule('runtimes', [
'type' => Response::MODEL_TEMPLATE_RUNTIME,
'description' => 'List of runtimes that can be used with this template.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('instructions', [
'type' => self::TYPE_STRING,
'description' => 'Function Template Instructions.',
'default' => '',
'example' => 'For documentation and instructions check out <link>.',
])
->addRule('vcsProvider', [
'type' => self::TYPE_STRING,
'description' => 'VCS (Version Control System) Provider.',
'default' => '',
'example' => 'github',
])
->addRule('providerRepositoryId', [
'type' => self::TYPE_STRING,
'description' => 'VCS (Version Control System) Repository ID',
'default' => '',
'example' => 'templates',
])
->addRule('providerOwner', [
'type' => self::TYPE_STRING,
'description' => 'VCS (Version Control System) Owner.',
'default' => '',
'example' => 'appwrite',
])
->addRule('providerVersion', [
'type' => self::TYPE_STRING,
'description' => 'VCS (Version Control System) branch version (tag).',
'default' => '',
'example' => 'main',
])
->addRule('variables', [
'type' => Response::MODEL_TEMPLATE_VARIABLE,
'description' => 'Function variables.',
'default' => [],
'example' => [],
'array' => true
])
->addRule('scopes', [
'type' => self::TYPE_STRING,
'description' => 'Function scopes.',
'default' => [],
'example' => 'users.read',
'array' => true,
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Template Function';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_TEMPLATE_FUNCTION;
}
}
@@ -0,0 +1,58 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class TemplateRuntime extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Runtime Name.',
'default' => '',
'example' => 'node-19.0',
])
->addRule('commands', [
'type' => self::TYPE_STRING,
'description' => 'The build command used to build the deployment.',
'default' => '',
'example' => 'npm install',
])
->addRule('entrypoint', [
'type' => self::TYPE_STRING,
'description' => 'The entrypoint file used to execute the deployment.',
'default' => '',
'example' => 'index.js',
])
->addRule('providerRootDirectory', [
'type' => self::TYPE_STRING,
'description' => 'Path to function in VCS (Version Control System) repository',
'default' => '',
'example' => 'node/starter',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Template Runtime';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_TEMPLATE_RUNTIME;
}
}
@@ -0,0 +1,70 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class TemplateVariable extends Model
{
public function __construct()
{
$this
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Variable Name.',
'default' => '',
'example' => 'APPWRITE_DATABASE_ID',
])
->addRule('description', [
'type' => self::TYPE_STRING,
'description' => 'Variable Description.',
'default' => '',
'example' => 'The ID of the Appwrite database that contains the collection to sync.',
])
->addRule('value', [
'type' => self::TYPE_STRING,
'description' => 'Variable Value.',
'default' => '',
'example' => '512',
])
->addRule('placeholder', [
'type' => self::TYPE_STRING,
'description' => 'Variable Placeholder.',
'default' => '',
'example' => '64a55...7b912',
])
->addRule('required', [
'type' => self::TYPE_BOOLEAN,
'description' => 'Is the variable required?',
'default' => false,
'example' => false,
])
->addRule('type', [
'type' => self::TYPE_STRING,
'description' => 'Variable Type.',
'default' => '',
'example' => 'password',
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'Template Variable';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_TEMPLATE_VARIABLE;
}
}
+1 -1
View File
@@ -135,7 +135,7 @@ class User extends Model
])
->addRule('accessedAt', [
'type' => self::TYPE_DATETIME,
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_USER_ACCCESS / 60 / 60 . ' hours.',
'description' => 'Most recent access date in ISO 8601 format. This attribute is only updated again after ' . APP_USER_ACCESS / 60 / 60 . ' hours.',
'default' => '',
'example' => self::TYPE_DATETIME_EXAMPLE,
])
@@ -0,0 +1,55 @@
<?php
namespace Appwrite\Utopia\Response\Model;
use Appwrite\Utopia\Response;
use Appwrite\Utopia\Response\Model;
class VcsContent extends Model
{
public function __construct()
{
$this
->addRule('size', [
'type' => self::TYPE_INTEGER,
'description' => 'Content size in bytes. Only files have size, and for directories, 0 is returned.',
'default' => 0,
'required' => false,
'example' => 1523,
])
->addRule('isDirectory', [
'type' => self::TYPE_BOOLEAN,
'description' => 'If a content is a directory. Directories can be used to check nested contents.',
'default' => false,
'required' => false,
'example' => true
])
->addRule('name', [
'type' => self::TYPE_STRING,
'description' => 'Name of directory or file.',
'default' => "",
'example' => 'Main.java',
'array' => false,
]);
}
/**
* Get Name
*
* @return string
*/
public function getName(): string
{
return 'VcsContents';
}
/**
* Get Type
*
* @return string
*/
public function getType(): string
{
return Response::MODEL_VCS_CONTENT;
}
}
+45 -5
View File
@@ -3,6 +3,7 @@
namespace Executor;
use Appwrite\Extend\Exception as AppwriteException;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Exception;
use Utopia\System\System;
@@ -74,6 +75,12 @@ class Executor
$runtimeId = "$projectId-$deploymentId-build";
$route = "/runtimes";
$timeout = (int) System::getEnv('_APP_FUNCTIONS_BUILD_TIMEOUT', 900);
// Remove after migration
if ($version == 'v3') {
$version = 'v4';
}
$params = [
'runtimeId' => $runtimeId,
'source' => $source,
@@ -161,6 +168,7 @@ class Executor
* @param string $source
* @param string $entrypoint
* @param string $runtimeEntrypoint
* @param bool $logging
*
* @return array
*/
@@ -178,6 +186,7 @@ class Executor
string $method,
array $headers,
string $runtimeEntrypoint = null,
bool $logging,
int $requestTimeout = null
) {
if (empty($headers['host'])) {
@@ -185,11 +194,16 @@ class Executor
}
$runtimeId = "$projectId-$deploymentId";
$route = '/runtimes/' . $runtimeId . '/execution';
$route = '/runtimes/' . $runtimeId . '/executions';
// Remove after migration
if ($version == 'v3') {
$version = 'v4';
}
$params = [
'runtimeId' => $runtimeId,
'variables' => $variables,
'body' => $body,
'timeout' => $timeout,
'path' => $path,
'method' => $method,
@@ -201,15 +215,21 @@ class Executor
'memory' => $this->memory,
'version' => $version,
'runtimeEntrypoint' => $runtimeEntrypoint,
'logging' => $logging,
'restartPolicy' => 'always' // Once utopia/orchestration has it, use DockerAPI::ALWAYS (0.13+)
];
if (!empty($body)) {
$params['body'] = $body;
}
// Safety timeout. Executor has timeout, and open runtime has soft timeout.
// This one shouldn't really happen, but prevents from unexpected networking behaviours.
if ($requestTimeout == null) {
$requestTimeout = $timeout + 15;
}
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId ], $params, true, $requestTimeout);
$response = $this->call(self::METHOD_POST, $route, [ 'x-opr-runtime-id' => $runtimeId, 'content-type' => 'multipart/form-data', 'accept' => 'multipart/form-data' ], $params, true, $requestTimeout);
$status = $response['headers']['status-code'];
if ($status >= 400) {
@@ -217,6 +237,11 @@ class Executor
throw new \Exception($message, $status);
}
$response['body']['headers'] = \json_decode($response['body']['headers'] ?? '{}', true);
$response['body']['statusCode'] = \intval($response['body']['statusCode'] ?? 500);
$response['body']['duration'] = \floatval($response['body']['duration'] ?? 0);
$response['body']['startTime'] = \floatval($response['body']['startTime'] ?? \microtime(true));
return $response['body'];
}
@@ -248,7 +273,13 @@ class Executor
break;
case 'multipart/form-data':
$query = $this->flatten($params);
$multipart = new BodyMultipart();
foreach ($params as $key => $value) {
$multipart->setPart($key, $value);
}
$headers['content-type'] = $multipart->exportHeader();
$query = $multipart->exportBody();
break;
default:
@@ -315,7 +346,16 @@ class Executor
$curlErrorMessage = curl_error($ch);
if ($decode) {
switch (substr($responseType, 0, strpos($responseType, ';'))) {
$strpos = strpos($responseType, ';');
$strpos = \is_bool($strpos) ? \strlen($responseType) : $strpos;
switch (substr($responseType, 0, $strpos)) {
case 'multipart/form-data':
$boundary = \explode('boundary=', $responseHeaders['content-type'] ?? '')[1] ?? '';
$multipartResponse = new BodyMultipart($boundary);
$multipartResponse->load(\is_bool($responseBody) ? '' : $responseBody);
$responseBody = $multipartResponse->getParts();
break;
case 'application/json':
$json = json_decode($responseBody, true);
+29 -10
View File
@@ -2,6 +2,7 @@
namespace Tests\E2E;
use Appwrite\Utopia\Fetch\BodyMultipart;
use Exception;
class Client
@@ -163,7 +164,7 @@ class Client
* @return array
* @throws Exception
*/
public function call(string $method, string $path = '', array $headers = [], array $params = [], bool $decode = true): array
public function call(string $method, string $path = '', array $headers = [], mixed $params = [], bool $decode = true): array
{
$headers = array_merge($this->headers, $headers);
$ch = curl_init($this->endpoint . $path . (($method == self::METHOD_GET && !empty($params)) ? '?' . http_build_query($params) : ''));
@@ -174,6 +175,7 @@ class Client
'application/json' => json_encode($params),
'multipart/form-data' => $this->flatten($params),
'application/graphql' => $params[0],
'text/plain' => $params,
default => http_build_query($params),
};
@@ -223,18 +225,35 @@ class Client
$responseType = $responseHeaders['content-type'] ?? '';
$responseStatus = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($decode && substr($responseType, 0, strpos($responseType, ';')) == 'application/json') {
$json = json_decode($responseBody, true);
if ($decode) {
$strpos = strpos($responseType, ';');
$strpos = \is_bool($strpos) ? \strlen($responseType) : $strpos;
switch (substr($responseType, 0, $strpos)) {
case 'multipart/form-data':
$boundary = \explode('boundary=', $responseHeaders['content-type'] ?? '')[1] ?? '';
$multipartResponse = new BodyMultipart($boundary);
$multipartResponse->load(\is_bool($responseBody) ? '' : $responseBody);
if ($json === null) {
throw new Exception('Failed to parse response: ' . $responseBody);
$responseBody = $multipartResponse->getParts();
break;
case 'application/json':
if (\is_bool($responseBody)) {
throw new Exception('Response is not a valid JSON.');
}
$json = json_decode($responseBody, true);
if ($json === null) {
throw new Exception('Failed to parse response: ' . $responseBody);
}
$responseBody = $json;
$json = null;
break;
}
$responseBody = $json;
$json = null;
}
if ((curl_errno($ch))) {
if ((curl_errno($ch)/* || 200 != $responseStatus*/)) {
throw new Exception(curl_error($ch) . ' with status code ' . $responseStatus, $responseStatus);
}
@@ -243,7 +262,7 @@ class Client
$responseHeaders['status-code'] = $responseStatus;
if ($responseStatus === 500) {
echo 'Server error(' . $method . ': ' . $path . '. Params: ' . json_encode($params) . '): ' . json_encode($responseBody) . "\n";
echo 'Server error(' . $method . ': ' . $path . '. Params: ' . json_encode($params) . '): ' . json_encode($responseBody) . '\n';
}
return [
+3 -4
View File
@@ -15,7 +15,7 @@ class HTTPTest extends Scope
public function setUp(): void
{
parent::setUp();
$this->client->setEndpoint('http://localhost');
$this->client->setEndpoint('http://traefik');
}
public function testOptions()
@@ -131,7 +131,6 @@ class HTTPTest extends Scope
'content-type' => 'application/json',
], json_decode(file_get_contents($directory . $file), true));
$response['body'] = json_decode($response['body'], true);
$this->assertEquals(200, $response['headers']['status-code']);
// looks like recent change in the validator
$this->assertTrue(empty($response['body']['schemaValidationMessages']));
@@ -163,11 +162,11 @@ class HTTPTest extends Scope
public function testDefaultOAuth2()
{
$response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/success', $this->getHeaders());
$response = $this->client->call(Client::METHOD_GET, '/console/auth/oauth2/success', $this->getHeaders());
$this->assertEquals(200, $response['headers']['status-code']);
$response = $this->client->call(Client::METHOD_GET, '/auth/oauth2/failure', $this->getHeaders());
$response = $this->client->call(Client::METHOD_GET, '/console/auth/oauth2/failure', $this->getHeaders());
$this->assertEquals(200, $response['headers']['status-code']);
}
+1 -1
View File
@@ -17,7 +17,7 @@ class HooksTest extends Scope
public function setUp(): void
{
parent::setUp();
$this->client->setEndpoint('http://localhost');
$this->client->setEndpoint('http://traefik');
}
public function testProjectHooks()

Some files were not shown because too many files have changed in this diff Show More