mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' of https://github.com/jaysomani/console into fix/dashboard-user-team-prefs-deletion
This commit is contained in:
+5
-5
@@ -22,12 +22,13 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@ai-sdk/svelte": "^1.1.24",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f",
|
||||
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755",
|
||||
"@appwrite.io/pink-icons": "0.25.0",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc",
|
||||
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f",
|
||||
"@appwrite.io/pink-legacy": "^1.0.3",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc",
|
||||
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f",
|
||||
"@faker-js/faker": "^9.9.0",
|
||||
"@plausible-analytics/tracker": "^0.4.4",
|
||||
"@popperjs/core": "^2.11.8",
|
||||
"@sentry/sveltekit": "^8.38.0",
|
||||
"@stripe/stripe-js": "^3.5.0",
|
||||
@@ -42,7 +43,6 @@
|
||||
"ignore": "^6.0.2",
|
||||
"nanoid": "^5.1.5",
|
||||
"nanotar": "^0.1.1",
|
||||
"plausible-tracker": "^0.3.9",
|
||||
"pretty-bytes": "^6.1.1",
|
||||
"prismjs": "^1.30.0",
|
||||
"remarkable": "^2.0.1",
|
||||
@@ -57,7 +57,7 @@
|
||||
"@melt-ui/svelte": "^0.86.5",
|
||||
"@playwright/test": "^1.55.1",
|
||||
"@sveltejs/adapter-static": "^3.0.8",
|
||||
"@sveltejs/kit": "^2.42.1",
|
||||
"@sveltejs/kit": "^2.49.5",
|
||||
"@sveltejs/vite-plugin-svelte": "^5.0.3",
|
||||
"@testing-library/dom": "^10.4.0",
|
||||
"@testing-library/jest-dom": "^6.6.3",
|
||||
|
||||
Generated
+68
-60
@@ -12,29 +12,32 @@ importers:
|
||||
specifier: ^1.1.24
|
||||
version: 1.1.24(svelte@5.25.3)(zod@3.24.3)
|
||||
'@appwrite.io/console':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755
|
||||
'@appwrite.io/pink-icons':
|
||||
specifier: 0.25.0
|
||||
version: 0.25.0
|
||||
'@appwrite.io/pink-icons-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f(svelte@5.25.3)
|
||||
'@appwrite.io/pink-legacy':
|
||||
specifier: ^1.0.3
|
||||
version: 1.0.3
|
||||
'@appwrite.io/pink-svelte':
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc(svelte@5.25.3)
|
||||
specifier: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f
|
||||
version: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f(svelte@5.25.3)
|
||||
'@faker-js/faker':
|
||||
specifier: ^9.9.0
|
||||
version: 9.9.0
|
||||
'@plausible-analytics/tracker':
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4
|
||||
'@popperjs/core':
|
||||
specifier: ^2.11.8
|
||||
version: 2.11.8
|
||||
'@sentry/sveltekit':
|
||||
specifier: ^8.38.0
|
||||
version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
version: 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
'@stripe/stripe-js':
|
||||
specifier: ^3.5.0
|
||||
version: 3.5.0
|
||||
@@ -71,9 +74,6 @@ importers:
|
||||
nanotar:
|
||||
specifier: ^0.1.1
|
||||
version: 0.1.1
|
||||
plausible-tracker:
|
||||
specifier: ^0.3.9
|
||||
version: 0.3.9
|
||||
pretty-bytes:
|
||||
specifier: ^6.1.1
|
||||
version: 6.1.1
|
||||
@@ -110,10 +110,10 @@ importers:
|
||||
version: 1.56.1
|
||||
'@sveltejs/adapter-static':
|
||||
specifier: ^3.0.8
|
||||
version: 3.0.8(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))
|
||||
version: 3.0.8(@sveltejs/kit@2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))
|
||||
'@sveltejs/kit':
|
||||
specifier: ^2.42.1
|
||||
version: 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
specifier: ^2.49.5
|
||||
version: 2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
'@sveltejs/vite-plugin-svelte':
|
||||
specifier: ^5.0.3
|
||||
version: 5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
@@ -272,17 +272,17 @@ packages:
|
||||
'@analytics/type-utils@0.6.2':
|
||||
resolution: {integrity: sha512-TD+xbmsBLyYy/IxFimW/YL/9L2IEnM7/EoV9Aeh56U64Ify8o27HJcKjo38XY9Tcn0uOq1AX3thkKgvtWvwFQg==}
|
||||
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f}
|
||||
version: 1.10.0
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755}
|
||||
version: 2.1.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1':
|
||||
resolution: {integrity: sha512-iLFlV55hj8mGuAbmxJGenxN5RaZMmVT4GJb9dv/MP1xBAtYibFq7JvBcxm18qV2KU8c31Rntf+Ub4GL7HwqTYg==}
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc}
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f}
|
||||
version: 2.0.0-RC.1
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -296,8 +296,8 @@ packages:
|
||||
'@appwrite.io/pink-legacy@1.0.3':
|
||||
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc}
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f':
|
||||
resolution: {tarball: https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f}
|
||||
version: 2.0.0-RC.2
|
||||
peerDependencies:
|
||||
svelte: ^4.0.0
|
||||
@@ -364,8 +364,8 @@ packages:
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
resolution: {integrity: sha512-KKBU1VGYR7ORr3At5HAtUQ+TV3SzRCXmA/8OdDZiLDBIZxVyzXuztPjfLd3BV1PRAQGCMWWSHYhL0F8d5uHBDQ==}
|
||||
'@babel/parser@7.28.6':
|
||||
resolution: {integrity: sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
hasBin: true
|
||||
|
||||
@@ -385,8 +385,8 @@ packages:
|
||||
resolution: {integrity: sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
resolution: {integrity: sha512-qQ5m48eI/MFLQ5PxQj4PFaprjyCTLI37ElWMmNs0K8Lk3dVeOdNpB3ks8jc7yM5CDmVC73eMVk/trk3fgmrUpA==}
|
||||
'@babel/types@7.28.6':
|
||||
resolution: {integrity: sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@csstools/color-helpers@5.0.2':
|
||||
@@ -1008,6 +1008,9 @@ packages:
|
||||
resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==}
|
||||
engines: {node: '>= 10.0.0'}
|
||||
|
||||
'@plausible-analytics/tracker@0.4.4':
|
||||
resolution: {integrity: sha512-fz0NOYUEYXtg1TBaPEEvtcBq3FfmLFuTe1VZw4M8sTWX129br5dguu3M15+plOQnc181ShYe67RfwhKgK89VnA==}
|
||||
|
||||
'@playwright/test@1.56.1':
|
||||
resolution: {integrity: sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -1279,18 +1282,21 @@ packages:
|
||||
peerDependencies:
|
||||
'@sveltejs/kit': ^2.0.0
|
||||
|
||||
'@sveltejs/kit@2.42.1':
|
||||
resolution: {integrity: sha512-SoWdb/OxEKHMlXZ78eNZa4pR1YdOGnUfcFj5NylJv+ZaTrnHK5xapw+7RJP2MiS1D4T+sEH25/tmMtmEl8p6VA==}
|
||||
'@sveltejs/kit@2.49.5':
|
||||
resolution: {integrity: sha512-dCYqelr2RVnWUuxc+Dk/dB/SjV/8JBndp1UovCyCZdIQezd8TRwFLNZctYkzgHxRJtaNvseCSRsuuHPeUgIN/A==}
|
||||
engines: {node: '>=18.13'}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
'@opentelemetry/api': ^1.0.0
|
||||
'@sveltejs/vite-plugin-svelte': ^3.0.0 || ^4.0.0-next.1 || ^5.0.0 || ^6.0.0-next.0
|
||||
svelte: ^4.0.0 || ^5.0.0-next.0
|
||||
typescript: ^5.3.3
|
||||
vite: ^5.0.3 || ^6.0.0 || ^7.0.0-beta.0
|
||||
peerDependenciesMeta:
|
||||
'@opentelemetry/api':
|
||||
optional: true
|
||||
typescript:
|
||||
optional: true
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@4.0.1':
|
||||
resolution: {integrity: sha512-J/Nmb2Q2y7mck2hyCX4ckVHcR5tu2J+MtBEQqpDrrgELZ2uvraQcK/ioCV61AqkdXFgriksOKIceDcQmqnGhVw==}
|
||||
@@ -1882,6 +1888,9 @@ packages:
|
||||
csstype@3.1.3:
|
||||
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
d3-array@3.2.4:
|
||||
resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==}
|
||||
engines: {node: '>=12'}
|
||||
@@ -2072,8 +2081,8 @@ packages:
|
||||
engines: {node: '>=0.10'}
|
||||
hasBin: true
|
||||
|
||||
devalue@5.3.2:
|
||||
resolution: {integrity: sha512-UDsjUbpQn9kvm68slnrs+mfxwFkIflOhkanmyabZ8zOYk8SMEIbJ3TK+88g70hSIeytu4y18f0z/hYHMTrXIWw==}
|
||||
devalue@5.6.2:
|
||||
resolution: {integrity: sha512-nPRkjWzzDQlsejL1WVifk5rvcFi/y1onBRxjaFMjZeR9mFpqu2gmAZ9xUB9/IEanEP/vBtGeGganC/GO1fmufg==}
|
||||
|
||||
devlop@1.1.0:
|
||||
resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==}
|
||||
@@ -2900,10 +2909,6 @@ packages:
|
||||
resolution: {integrity: sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
plausible-tracker@0.3.9:
|
||||
resolution: {integrity: sha512-hMhneYm3GCPyQon88SZrVJx+LlqhM1kZFQbuAgXPoh/Az2YvO1B6bitT9qlhpiTdJlsT5lsr3gPmzoVjb5CDXA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
playwright-core@1.56.1:
|
||||
resolution: {integrity: sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -3131,14 +3136,14 @@ packages:
|
||||
engines: {node: '>=10'}
|
||||
hasBin: true
|
||||
|
||||
seroval-plugins@1.3.3:
|
||||
resolution: {integrity: sha512-16OL3NnUBw8JG1jBLUoZJsLnQq0n5Ua6aHalhJK4fMQkz1lqR7Osz1sA30trBtd9VUDc2NgkuRCn8+/pBwqZ+w==}
|
||||
seroval-plugins@1.4.2:
|
||||
resolution: {integrity: sha512-X7p4MEDTi+60o2sXZ4bnDBhgsUYDSkQEvzYZuJyFqWg9jcoPsHts5nrg5O956py2wyt28lUrBxk0M0/wU8URpA==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
seroval: ^1.0
|
||||
|
||||
seroval@1.3.2:
|
||||
resolution: {integrity: sha512-RbcPH1n5cfwKrru7v7+zrZvjLurgHhGyso3HTyGtRivGWgYjbOmGuivCQaORNELjNONoK35nj28EoWul9sb1zQ==}
|
||||
seroval@1.4.2:
|
||||
resolution: {integrity: sha512-N3HEHRCZYn3cQbsC4B5ldj9j+tHdf4JZoYPlcI4rRYu0Xy4qN8MQf1Z08EibzB0WpgRG5BGK08FTrmM66eSzKQ==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
set-cookie-parser@2.7.1:
|
||||
@@ -3823,13 +3828,13 @@ snapshots:
|
||||
|
||||
'@analytics/type-utils@0.6.2': {}
|
||||
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@f21fc7f': {}
|
||||
'@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@9c55755': {}
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@2.0.0-RC.1(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@865e2fc(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@bbad65f(svelte@5.25.3)':
|
||||
dependencies:
|
||||
svelte: 5.25.3
|
||||
|
||||
@@ -3842,7 +3847,7 @@ snapshots:
|
||||
'@appwrite.io/pink-icons': 1.0.0
|
||||
the-new-css-reset: 1.11.3
|
||||
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@865e2fc(svelte@5.25.3)':
|
||||
'@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@bbad65f(svelte@5.25.3)':
|
||||
dependencies:
|
||||
'@appwrite.io/pink-icons-svelte': 2.0.0-RC.1(svelte@5.25.3)
|
||||
'@floating-ui/dom': 1.6.13
|
||||
@@ -3945,9 +3950,9 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/types': 7.27.0
|
||||
|
||||
'@babel/parser@7.28.5':
|
||||
'@babel/parser@7.28.6':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.5
|
||||
'@babel/types': 7.28.6
|
||||
|
||||
'@babel/runtime@7.27.0':
|
||||
dependencies:
|
||||
@@ -3976,7 +3981,7 @@ snapshots:
|
||||
'@babel/helper-string-parser': 7.25.9
|
||||
'@babel/helper-validator-identifier': 7.25.9
|
||||
|
||||
'@babel/types@7.28.5':
|
||||
'@babel/types@7.28.6':
|
||||
dependencies:
|
||||
'@babel/helper-string-parser': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.28.5
|
||||
@@ -4575,6 +4580,8 @@ snapshots:
|
||||
'@parcel/watcher-win32-x64': 2.5.1
|
||||
optional: true
|
||||
|
||||
'@plausible-analytics/tracker@0.4.4': {}
|
||||
|
||||
'@playwright/test@1.56.1':
|
||||
dependencies:
|
||||
playwright: 1.56.1
|
||||
@@ -4796,14 +4803,14 @@ snapshots:
|
||||
magic-string: 0.30.7
|
||||
svelte: 5.25.3
|
||||
|
||||
'@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
|
||||
'@sentry/sveltekit@8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)(@sveltejs/kit@2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
|
||||
dependencies:
|
||||
'@sentry/core': 8.55.0
|
||||
'@sentry/node': 8.55.0
|
||||
'@sentry/opentelemetry': 8.55.0(@opentelemetry/api@1.9.0)(@opentelemetry/context-async-hooks@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/core@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/instrumentation@0.57.2(@opentelemetry/api@1.9.0))(@opentelemetry/sdk-trace-base@1.30.1(@opentelemetry/api@1.9.0))(@opentelemetry/semantic-conventions@1.30.0)
|
||||
'@sentry/svelte': 8.55.0(svelte@5.25.3)
|
||||
'@sentry/vite-plugin': 2.22.6
|
||||
'@sveltejs/kit': 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
'@sveltejs/kit': 2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
magic-string: 0.30.7
|
||||
magicast: 0.2.8
|
||||
sorcery: 1.0.0
|
||||
@@ -4875,11 +4882,11 @@ snapshots:
|
||||
dependencies:
|
||||
acorn: 8.15.0
|
||||
|
||||
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))':
|
||||
'@sveltejs/adapter-static@3.0.8(@sveltejs/kit@2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))':
|
||||
dependencies:
|
||||
'@sveltejs/kit': 2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
'@sveltejs/kit': 2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))
|
||||
|
||||
'@sveltejs/kit@2.42.1(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
|
||||
'@sveltejs/kit@2.49.5(@opentelemetry/api@1.9.0)(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(typescript@5.8.2)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
|
||||
dependencies:
|
||||
'@standard-schema/spec': 1.0.0
|
||||
'@sveltejs/acorn-typescript': 1.0.5(acorn@8.15.0)
|
||||
@@ -4887,7 +4894,7 @@ snapshots:
|
||||
'@types/cookie': 0.6.0
|
||||
acorn: 8.15.0
|
||||
cookie: 0.6.0
|
||||
devalue: 5.3.2
|
||||
devalue: 5.6.2
|
||||
esm-env: 1.2.2
|
||||
kleur: 4.1.5
|
||||
magic-string: 0.30.18
|
||||
@@ -4899,6 +4906,7 @@ snapshots:
|
||||
vite: 7.0.6(@types/node@22.13.14)(sass@1.86.0)
|
||||
optionalDependencies:
|
||||
'@opentelemetry/api': 1.9.0
|
||||
typescript: 5.8.2
|
||||
|
||||
'@sveltejs/vite-plugin-svelte-inspector@4.0.1(@sveltejs/vite-plugin-svelte@5.0.3(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0)))(svelte@5.25.3)(vite@7.0.6(@types/node@22.13.14)(sass@1.86.0))':
|
||||
dependencies:
|
||||
@@ -5292,7 +5300,7 @@ snapshots:
|
||||
|
||||
'@vue/compiler-core@3.5.13':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/parser': 7.28.6
|
||||
'@vue/shared': 3.5.13
|
||||
entities: 4.5.0
|
||||
estree-walker: 2.0.2
|
||||
@@ -5305,7 +5313,7 @@ snapshots:
|
||||
|
||||
'@vue/compiler-sfc@3.5.13':
|
||||
dependencies:
|
||||
'@babel/parser': 7.28.5
|
||||
'@babel/parser': 7.28.6
|
||||
'@vue/compiler-core': 3.5.13
|
||||
'@vue/compiler-dom': 3.5.13
|
||||
'@vue/compiler-ssr': 3.5.13
|
||||
@@ -5334,7 +5342,7 @@ snapshots:
|
||||
'@vue/reactivity': 3.5.13
|
||||
'@vue/runtime-core': 3.5.13
|
||||
'@vue/shared': 3.5.13
|
||||
csstype: 3.1.3
|
||||
csstype: 3.2.3
|
||||
|
||||
'@vue/server-renderer@3.5.13(vue@3.5.13(typescript@5.8.2))':
|
||||
dependencies:
|
||||
@@ -5619,6 +5627,8 @@ snapshots:
|
||||
|
||||
csstype@3.1.3: {}
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
d3-array@3.2.4:
|
||||
dependencies:
|
||||
internmap: 2.0.3
|
||||
@@ -5836,7 +5846,7 @@ snapshots:
|
||||
detect-libc@1.0.3:
|
||||
optional: true
|
||||
|
||||
devalue@5.3.2: {}
|
||||
devalue@5.6.2: {}
|
||||
|
||||
devlop@1.1.0:
|
||||
dependencies:
|
||||
@@ -6675,8 +6685,6 @@ snapshots:
|
||||
|
||||
picomatch@4.0.3: {}
|
||||
|
||||
plausible-tracker@0.3.9: {}
|
||||
|
||||
playwright-core@1.56.1: {}
|
||||
|
||||
playwright@1.56.1:
|
||||
@@ -6903,11 +6911,11 @@ snapshots:
|
||||
|
||||
semver@7.7.1: {}
|
||||
|
||||
seroval-plugins@1.3.3(seroval@1.3.2):
|
||||
seroval-plugins@1.4.2(seroval@1.4.2):
|
||||
dependencies:
|
||||
seroval: 1.3.2
|
||||
seroval: 1.4.2
|
||||
|
||||
seroval@1.3.2: {}
|
||||
seroval@1.4.2: {}
|
||||
|
||||
set-cookie-parser@2.7.1: {}
|
||||
|
||||
@@ -6984,9 +6992,9 @@ snapshots:
|
||||
|
||||
solid-js@1.9.5:
|
||||
dependencies:
|
||||
csstype: 3.1.3
|
||||
seroval: 1.3.2
|
||||
seroval-plugins: 1.3.3(seroval@1.3.2)
|
||||
csstype: 3.2.3
|
||||
seroval: 1.4.2
|
||||
seroval-plugins: 1.4.2(seroval@1.4.2)
|
||||
|
||||
solid-swr-store@0.10.7(solid-js@1.9.5)(swr-store@0.10.6):
|
||||
dependencies:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import Analytics, { type AnalyticsPlugin } from 'analytics';
|
||||
import Plausible from 'plausible-tracker';
|
||||
import { init, track } from '@plausible-analytics/tracker';
|
||||
import { get } from 'svelte/store';
|
||||
import { page } from '$app/state';
|
||||
import { user } from '$lib/stores/user';
|
||||
@@ -11,30 +11,30 @@ import { getReferrerAndUtmSource, getTrackedQueryParams } from '$lib/helpers/utm
|
||||
function plausible(domain: string): AnalyticsPlugin {
|
||||
if (!browser) return { name: 'analytics-plugin-plausible' };
|
||||
|
||||
const instance = Plausible({
|
||||
domain
|
||||
init({
|
||||
domain,
|
||||
autoCapturePageviews: false
|
||||
});
|
||||
|
||||
return {
|
||||
name: 'analytics-plugin-plausible',
|
||||
page: ({ payload }) => {
|
||||
instance.trackPageview({
|
||||
track('pageview', {
|
||||
url: payload.properties.path,
|
||||
referrer: payload.properties.referrer,
|
||||
deviceWidth: payload.properties.width
|
||||
props: {
|
||||
referrer: payload.properties.referrer,
|
||||
deviceWidth: String(payload.properties.width)
|
||||
}
|
||||
});
|
||||
},
|
||||
track: ({ payload }) => {
|
||||
instance.trackEvent(
|
||||
payload.event,
|
||||
{
|
||||
props: payload.properties
|
||||
},
|
||||
{
|
||||
url: payload.properties.path,
|
||||
deviceWidth: payload.properties.width
|
||||
track(payload.event, {
|
||||
url: payload.properties.path,
|
||||
props: {
|
||||
...payload.properties,
|
||||
deviceWidth: String(payload.properties.width)
|
||||
}
|
||||
);
|
||||
});
|
||||
},
|
||||
loaded: () => true
|
||||
};
|
||||
@@ -247,6 +247,7 @@ export enum Submit {
|
||||
ProjectDelete = 'submit_project_delete',
|
||||
ProjectUpdateName = 'submit_project_update_name',
|
||||
ProjectUpdateTeam = 'submit_project_update_team',
|
||||
ProjectUpdateLabels = 'submit_project_update_labels',
|
||||
ProjectService = 'submit_project_service',
|
||||
ProjectUpdateSMTP = 'submit_project_update_smtp',
|
||||
MemberCreate = 'submit_member_create',
|
||||
|
||||
@@ -5,7 +5,6 @@
|
||||
import { isCloud, isSelfHosted } from '$lib/system';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { BillingPlan, Dependencies } from '$lib/constants';
|
||||
import type { BackupArchive, BackupRestoration } from '$lib/sdk/backups';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { page } from '$app/state';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
@@ -13,10 +12,11 @@
|
||||
import { getProjectId } from '$lib/helpers/project';
|
||||
import { toLocaleDate } from '$lib/helpers/date';
|
||||
import { Typography } from '@appwrite.io/pink-svelte';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
|
||||
const backupRestoreItems: {
|
||||
archives: Map<string, BackupArchive>;
|
||||
restorations: Map<string, BackupRestoration>;
|
||||
archives: Map<string, Models.BackupArchive>;
|
||||
restorations: Map<string, Models.BackupRestoration>;
|
||||
} = {
|
||||
archives: new Map(),
|
||||
restorations: new Map()
|
||||
@@ -117,7 +117,7 @@
|
||||
if (which === 'restorations') lastDatabaseRestorationId = null;
|
||||
}
|
||||
|
||||
function backupName(item: BackupArchive | BackupRestoration, key: string) {
|
||||
function backupName(item: Models.BackupArchive | Models.BackupRestoration, key: string) {
|
||||
const column = key === 'archives' ? '$createdAt' : 'startedAt';
|
||||
|
||||
return toLocaleDate(item[column]);
|
||||
|
||||
@@ -3,8 +3,15 @@
|
||||
|
||||
import { Badge, Layout, Typography, Table, InteractiveText } from '@appwrite.io/pink-svelte';
|
||||
|
||||
export let domain: string;
|
||||
export let verified = undefined;
|
||||
let {
|
||||
domain,
|
||||
verified,
|
||||
ruleStatus
|
||||
}: {
|
||||
domain: string;
|
||||
verified?: boolean;
|
||||
ruleStatus?: 'created' | 'verifying' | 'unverified' | 'verified';
|
||||
} = $props();
|
||||
|
||||
const nameserverList = $regionalConsoleVariables?._APP_DOMAINS_NAMESERVERS
|
||||
? $regionalConsoleVariables?._APP_DOMAINS_NAMESERVERS?.split(',')
|
||||
@@ -16,14 +23,24 @@
|
||||
<Typography.Text variant="l-500" color="--fgcolor-neutral-primary">
|
||||
{domain}
|
||||
</Typography.Text>
|
||||
{#if verified === true}
|
||||
<Badge variant="secondary" type="success" size="xs" content="Verified" />
|
||||
{:else if verified === false}
|
||||
<Badge variant="secondary" type="warning" size="xs" content="Verification failed" />
|
||||
{#if verified !== undefined}
|
||||
{#if ruleStatus === 'created'}
|
||||
<Badge variant="secondary" type="error" size="xs" content="Verification failed" />
|
||||
{:else if ruleStatus === 'verifying'}
|
||||
<Badge variant="secondary" size="xs" content="Generating certificate" />
|
||||
{:else if ruleStatus === 'unverified'}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
type="error"
|
||||
size="xs"
|
||||
content="Certificate generation failed" />
|
||||
{:else if verified === true}
|
||||
<Badge variant="secondary" type="success" size="xs" content="Verified" />
|
||||
{/if}
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Typography.Text variant="m-400">
|
||||
Add the following nameservers on your DNS provider. Note that changes may take up to 48
|
||||
Add the following nameservers on your DNS provider. Note that DNS changes may take up to 48
|
||||
hours to propagate fully.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
@@ -14,10 +14,10 @@
|
||||
|
||||
let {
|
||||
domain,
|
||||
verified = undefined,
|
||||
verified,
|
||||
variant,
|
||||
service = 'general',
|
||||
ruleStatus = undefined,
|
||||
ruleStatus,
|
||||
onNavigateToNameservers = () => {},
|
||||
onNavigateToA = () => {},
|
||||
onNavigateToAAAA = () => {}
|
||||
@@ -115,7 +115,9 @@
|
||||
</svelte:fragment>
|
||||
<Table.Row.Base {root}>
|
||||
<Table.Cell column="type" {root}>{variant.toUpperCase()}</Table.Cell>
|
||||
<Table.Cell column="name" {root}>{subdomain || '@'}</Table.Cell>
|
||||
<Table.Cell column="name" {root}>
|
||||
<InteractiveText variant="copy" isVisible text={subdomain || '@'} />
|
||||
</Table.Cell>
|
||||
<Table.Cell column="value" {root}>
|
||||
<InteractiveText variant="copy" isVisible text={setTarget()} />
|
||||
</Table.Cell>
|
||||
|
||||
@@ -15,21 +15,31 @@ function isBuildTimedOut(createdAt: string, status: string, timeoutSeconds: numb
|
||||
const created = new Date(createdAt);
|
||||
const elapsedSeconds = Math.floor((Date.now() - created.getTime()) / 1000);
|
||||
|
||||
return elapsedSeconds > timeoutSeconds;
|
||||
return elapsedSeconds >= timeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective status for a build, considering timeout
|
||||
*/
|
||||
export function getEffectiveBuildStatus(
|
||||
originalStatus: string,
|
||||
createdAt: string,
|
||||
deployment: Models.Deployment,
|
||||
consoleVariables: Models.ConsoleVariables | undefined
|
||||
): string {
|
||||
const originalStatus = deployment.status;
|
||||
const createdAt = deployment.$createdAt;
|
||||
|
||||
const timeoutSeconds = getBuildTimeoutSeconds(consoleVariables);
|
||||
if (isBuildTimedOut(createdAt, originalStatus, timeoutSeconds)) {
|
||||
return 'failed';
|
||||
}
|
||||
|
||||
const isReady = originalStatus === 'ready';
|
||||
const hasScreenshot = deployment.screenshotLight && deployment.screenshotDark;
|
||||
|
||||
if (isReady && !hasScreenshot) {
|
||||
return 'finalizing';
|
||||
}
|
||||
|
||||
return originalStatus;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,37 @@
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
|
||||
/**
|
||||
* Checks if an execution has exceeded the maximum execution timeout duration
|
||||
*/
|
||||
function isExecutionTimedOut(createdAt: string, status: string, timeoutSeconds: number): boolean {
|
||||
if (!['waiting', 'processing'].includes(status)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!timeoutSeconds || timeoutSeconds <= 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const created = new Date(createdAt);
|
||||
const elapsedSeconds = Math.floor((Date.now() - created.getTime()) / 1000);
|
||||
|
||||
return elapsedSeconds >= timeoutSeconds;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the effective status for an execution, considering timeout
|
||||
*/
|
||||
export function getEffectiveExecutionStatus(
|
||||
execution: Models.Execution,
|
||||
func: Models.Function
|
||||
): string {
|
||||
const originalStatus = execution.status;
|
||||
const createdAt = execution.$createdAt;
|
||||
|
||||
const timeoutSeconds = func.timeout;
|
||||
if (isExecutionTimedOut(createdAt, originalStatus, timeoutSeconds)) {
|
||||
return 'failed';
|
||||
}
|
||||
|
||||
return originalStatus;
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
.responsive-table {
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
scrollbar-width: thin;
|
||||
position: relative;
|
||||
}
|
||||
@@ -1,332 +0,0 @@
|
||||
import { AppwriteException, Client, type Payload } from '@appwrite.io/console';
|
||||
|
||||
export type BackupPolicyList = {
|
||||
total: number;
|
||||
policies: BackupPolicy[];
|
||||
};
|
||||
|
||||
export type BackupArchiveList = {
|
||||
total: number;
|
||||
archives: BackupArchive[];
|
||||
};
|
||||
|
||||
export type BackupRestorationList = {
|
||||
total: number;
|
||||
restorations: BackupRestoration[];
|
||||
};
|
||||
|
||||
export type BackupPolicy = {
|
||||
$id: string;
|
||||
name: string;
|
||||
$createdAt: string;
|
||||
$updatedAt: string;
|
||||
services: string[];
|
||||
resources: string[];
|
||||
resourceId?: string;
|
||||
resourceType?: string;
|
||||
retention: number;
|
||||
schedule: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
export type BackupArchive = {
|
||||
$id: string;
|
||||
$createdAt: string;
|
||||
$updatedAt: string;
|
||||
policyId: string;
|
||||
size: number;
|
||||
status: string;
|
||||
startedAt: string;
|
||||
migrationId: string;
|
||||
services: string[];
|
||||
resources: string[];
|
||||
resourceId?: string;
|
||||
resourceType?: string;
|
||||
};
|
||||
|
||||
export type BackupRestoration = {
|
||||
$id: string;
|
||||
$createdAt: string;
|
||||
$updatedAt: string;
|
||||
archiveId: string;
|
||||
policyId: string;
|
||||
status: string;
|
||||
startedAt: string;
|
||||
migrationId: string;
|
||||
services: string[];
|
||||
resources: string[];
|
||||
options: string;
|
||||
};
|
||||
|
||||
export class Backups {
|
||||
client: Client;
|
||||
|
||||
constructor(client: Client) {
|
||||
this.client = client;
|
||||
}
|
||||
|
||||
async createArchive(services: string[], resourceId?: string): Promise<BackupArchive> {
|
||||
if (typeof services === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "services"');
|
||||
}
|
||||
const apiPath = '/backups/archives';
|
||||
const payload: Payload = {};
|
||||
if (typeof services !== 'undefined') {
|
||||
payload['services'] = services;
|
||||
}
|
||||
if (typeof resourceId !== 'undefined') {
|
||||
payload['resourceId'] = resourceId;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('post', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async deleteArchive(archiveId: string): Promise<object> {
|
||||
if (typeof archiveId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "archiveId"');
|
||||
}
|
||||
const apiPath = '/backups/archives/{archiveId}'.replace('{archiveId}', archiveId);
|
||||
const payload: Payload = {};
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('delete', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async listArchives(queries?: string[]): Promise<BackupArchiveList> {
|
||||
const apiPath = '/backups/archives';
|
||||
const payload: Payload = {};
|
||||
if (typeof queries !== 'undefined') {
|
||||
payload['queries'] = queries;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async getArchive(archiveId: string): Promise<BackupArchive> {
|
||||
if (typeof archiveId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "archiveId"');
|
||||
}
|
||||
const apiPath = '/backups/archives/{archiveId}'.replace('{archiveId}', archiveId);
|
||||
const payload: Payload = {};
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async listPolicies(queries?: string[]): Promise<BackupPolicyList> {
|
||||
const apiPath = '/backups/policies';
|
||||
const payload: Payload = {};
|
||||
if (typeof queries !== 'undefined') {
|
||||
payload['queries'] = queries;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async createPolicy(
|
||||
policyId: string,
|
||||
services: string[],
|
||||
retention: number,
|
||||
schedule: string,
|
||||
name?: string,
|
||||
resourceId?: string,
|
||||
enabled?: boolean
|
||||
): Promise<BackupPolicy> {
|
||||
if (typeof policyId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "policyId"');
|
||||
}
|
||||
if (typeof services === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "services"');
|
||||
}
|
||||
if (typeof retention === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "retention"');
|
||||
}
|
||||
if (typeof schedule === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "schedule"');
|
||||
}
|
||||
const apiPath = '/backups/policies';
|
||||
const payload: Payload = {};
|
||||
if (typeof policyId !== 'undefined') {
|
||||
payload['policyId'] = policyId;
|
||||
}
|
||||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof services !== 'undefined') {
|
||||
payload['services'] = services;
|
||||
}
|
||||
if (typeof resourceId !== 'undefined') {
|
||||
payload['resourceId'] = resourceId;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
}
|
||||
if (typeof retention !== 'undefined') {
|
||||
payload['retention'] = retention;
|
||||
}
|
||||
if (typeof schedule !== 'undefined') {
|
||||
payload['schedule'] = schedule;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('post', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async getPolicy(policyId: string): Promise<BackupPolicy> {
|
||||
if (typeof policyId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "policyId"');
|
||||
}
|
||||
const apiPath = '/backups/policies/{policyId}'.replace('{policyId}', policyId);
|
||||
const payload: Payload = {};
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async updatePolicy(
|
||||
policyId: string,
|
||||
name?: string,
|
||||
retention?: number,
|
||||
schedule?: string,
|
||||
enabled?: boolean
|
||||
): Promise<BackupPolicy> {
|
||||
if (typeof policyId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "policyId"');
|
||||
}
|
||||
const apiPath = '/backups/policies/{policyId}'.replace('{policyId}', policyId);
|
||||
const payload: Payload = {};
|
||||
if (typeof name !== 'undefined') {
|
||||
payload['name'] = name;
|
||||
}
|
||||
if (typeof retention !== 'undefined') {
|
||||
payload['retention'] = retention;
|
||||
}
|
||||
if (typeof schedule !== 'undefined') {
|
||||
payload['schedule'] = schedule;
|
||||
}
|
||||
if (typeof enabled !== 'undefined') {
|
||||
payload['enabled'] = enabled;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('patch', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async deletePolicy(policyId: string): Promise<object> {
|
||||
if (typeof policyId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "policyId"');
|
||||
}
|
||||
const apiPath = '/backups/policies/{policyId}'.replace('{policyId}', policyId);
|
||||
const payload: Payload = {};
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('delete', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async createRestoration(
|
||||
archiveId: string,
|
||||
services: string[],
|
||||
newResourceId?: string,
|
||||
newResourceName?: string
|
||||
): Promise<BackupRestoration> {
|
||||
if (typeof archiveId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "archiveId"');
|
||||
}
|
||||
if (typeof services === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "services"');
|
||||
}
|
||||
const apiPath = '/backups/restoration';
|
||||
const payload: Payload = {};
|
||||
if (typeof archiveId !== 'undefined') {
|
||||
payload['archiveId'] = archiveId;
|
||||
}
|
||||
if (typeof services !== 'undefined') {
|
||||
payload['services'] = services;
|
||||
}
|
||||
if (typeof newResourceId !== 'undefined') {
|
||||
payload['newResourceId'] = newResourceId;
|
||||
}
|
||||
if (typeof newResourceName !== 'undefined') {
|
||||
payload['newResourceName'] = newResourceName;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('post', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async listRestorations(queries?: string[]): Promise<BackupRestorationList> {
|
||||
const apiPath = '/backups/restorations';
|
||||
const payload: Payload = {};
|
||||
if (typeof queries !== 'undefined') {
|
||||
payload['queries'] = queries;
|
||||
}
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
|
||||
async getRestoration(restorationId: string): Promise<BackupArchive> {
|
||||
if (typeof restorationId === 'undefined') {
|
||||
throw new AppwriteException('Missing required parameter: "restorationId"');
|
||||
}
|
||||
const apiPath = '/backups/restorations/{restorationId}'.replace(
|
||||
'{restorationId}',
|
||||
restorationId
|
||||
);
|
||||
const payload: Payload = {};
|
||||
const uri = new URL(this.client.config.endpoint + apiPath);
|
||||
|
||||
const apiHeaders: { [header: string]: string } = {
|
||||
'content-type': 'application/json'
|
||||
};
|
||||
|
||||
return await this.client.call('get', uri, apiHeaders, payload);
|
||||
}
|
||||
}
|
||||
@@ -27,9 +27,9 @@ export async function checkForDatabaseBackupPolicies(
|
||||
|
||||
if (isCloud && backupsEnabled) {
|
||||
try {
|
||||
const policies = await sdk
|
||||
.forProject(region, projectId)
|
||||
.backups.listPolicies([Query.limit(1), Query.equal('resourceId', database.$id)]);
|
||||
const policies = await sdk.forProject(region, projectId).backups.listPolicies({
|
||||
queries: [Query.limit(1), Query.equal('resourceId', database.$id)]
|
||||
});
|
||||
|
||||
total = policies.total;
|
||||
} catch (e) {
|
||||
|
||||
@@ -3,6 +3,7 @@ import {
|
||||
Account,
|
||||
Assistant,
|
||||
Avatars,
|
||||
Backups,
|
||||
Client,
|
||||
Console,
|
||||
Functions,
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
Organizations
|
||||
} from '@appwrite.io/console';
|
||||
import { Billing } from '../sdk/billing';
|
||||
import { Backups } from '../sdk/backups';
|
||||
import { Sources } from '$lib/sdk/sources';
|
||||
import {
|
||||
REGION_FRA,
|
||||
|
||||
@@ -18,7 +18,7 @@ export const load: LayoutLoad = async ({ depends, parent }) => {
|
||||
sdk.forConsole.account.getPrefs(),
|
||||
isCloud ? sdk.forConsole.billing.getPlansInfo() : null,
|
||||
fetch(`${endpoint}/health/version`, {
|
||||
headers: { 'X-Appwrite-Project': project }
|
||||
headers: { 'X-Appwrite-Project': project as string }
|
||||
}).then((response) => response.json() as { version?: string }),
|
||||
sdk.forConsole.console.variables()
|
||||
]);
|
||||
|
||||
@@ -170,7 +170,7 @@
|
||||
|
||||
<Container>
|
||||
<Layout.Stack direction="row" justifyContent="space-between" class="common-section">
|
||||
<SearchQuery bind:this={searchQuery} placeholder="Search by name or ID" />
|
||||
<SearchQuery bind:this={searchQuery} placeholder="Search by name, label, or ID" />
|
||||
|
||||
{#if $canWriteProjects}
|
||||
{#if projectCreationDisabled && reachedProjectLimit}
|
||||
|
||||
@@ -24,8 +24,12 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
const archivedPage =
|
||||
Number.isFinite(archivedPageRaw) && archivedPageRaw > 0 ? archivedPageRaw : 1;
|
||||
const archivedOffset = pageToOffset(archivedPage, limit);
|
||||
const common = [Query.equal('teamId', params.organization)];
|
||||
const active = isCloud
|
||||
|
||||
const searchQueries = search
|
||||
? [Query.or([Query.search('search', search), Query.contains('labels', search)])]
|
||||
: [];
|
||||
const commonQueries = [Query.equal('teamId', params.organization)];
|
||||
const activeQueries = isCloud
|
||||
? [Query.or([Query.equal('status', 'active'), Query.isNull('status')])]
|
||||
: [];
|
||||
|
||||
@@ -35,10 +39,10 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
Query.offset(offset),
|
||||
Query.limit(limit),
|
||||
Query.orderDesc(''),
|
||||
...common,
|
||||
...active
|
||||
],
|
||||
search: search || undefined
|
||||
...commonQueries,
|
||||
...searchQueries,
|
||||
...activeQueries
|
||||
]
|
||||
}),
|
||||
isCloud
|
||||
? sdk.forConsole.projects.list({
|
||||
@@ -46,20 +50,18 @@ export const load: PageLoad = async ({ params, url, route, depends, parent }) =>
|
||||
Query.offset(archivedOffset),
|
||||
Query.limit(limit),
|
||||
Query.orderDesc(''),
|
||||
...common,
|
||||
...commonQueries,
|
||||
...searchQueries,
|
||||
Query.equal('status', 'archived')
|
||||
],
|
||||
search: search || undefined
|
||||
]
|
||||
})
|
||||
: Promise.resolve({ projects: [], total: 0 }),
|
||||
sdk.forConsole.projects.list({
|
||||
queries: [...common, ...active],
|
||||
search: search || undefined
|
||||
queries: [...commonQueries, ...activeQueries, ...searchQueries]
|
||||
}),
|
||||
isCloud
|
||||
? sdk.forConsole.projects.list({
|
||||
queries: [...common, Query.equal('status', 'archived')],
|
||||
search: search || undefined
|
||||
queries: [...commonQueries, ...searchQueries, Query.equal('status', 'archived')]
|
||||
})
|
||||
: Promise.resolve({ projects: [], total: 0 })
|
||||
]);
|
||||
|
||||
@@ -93,9 +93,9 @@
|
||||
{#if !isDomainVerified(domain)}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
type="warning"
|
||||
type="error"
|
||||
content="Not verified"
|
||||
size="s" />
|
||||
size="xs" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{:else if column.id === 'registrar'}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { base } from '$app/paths';
|
||||
import { page } from '$app/state';
|
||||
import { goto } from '$app/navigation';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { InputDomain } from '$lib/elements/forms/index.js';
|
||||
import { Wizard } from '$lib/layout';
|
||||
@@ -23,7 +24,16 @@
|
||||
teamId: page.params.organization,
|
||||
domain: domainName.toLocaleLowerCase()
|
||||
});
|
||||
invalidate(Dependencies.DOMAINS);
|
||||
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
const verified = domain.nameservers.toLowerCase() === 'appwrite';
|
||||
if (verified) {
|
||||
await goto(backPage);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
|
||||
+66
-13
@@ -13,9 +13,16 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import DomainMetrics from './domainMetrics.svelte';
|
||||
import { base } from '$app/paths';
|
||||
import { isSmallViewport } from '$lib/stores/viewport';
|
||||
import { app } from '$lib/stores/app';
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { IconDownload, IconPlus, IconUpload } from '@appwrite.io/pink-icons-svelte';
|
||||
import {
|
||||
IconDownload,
|
||||
IconPlus,
|
||||
IconUpload,
|
||||
IconAdjustments
|
||||
} from '@appwrite.io/pink-icons-svelte';
|
||||
import DisplaySettingsModal from '$lib/layout/displaySettingsModal.svelte';
|
||||
import { ViewSelector } from '$lib/components';
|
||||
import { View } from '$lib/helpers/load';
|
||||
import { columns, presets } from './store';
|
||||
@@ -33,6 +40,7 @@
|
||||
let showCreate = false;
|
||||
let showPresetModal = false;
|
||||
let showImportModal = false;
|
||||
let showDisplaySettingsModal = false;
|
||||
let selectedPreset = '';
|
||||
|
||||
async function downloadRecords() {
|
||||
@@ -70,23 +78,55 @@
|
||||
|
||||
<Layout.Stack gap="l">
|
||||
{#if data.recordList.total}
|
||||
<Layout.Stack direction="row" justifyContent="space-between">
|
||||
<Layout.Stack direction="row" gap="s" inline>
|
||||
<Button secondary on:click={() => (showImportModal = true)}>
|
||||
<Icon icon={IconUpload} size="s" slot="start" />
|
||||
Import zone file
|
||||
<Layout.Stack direction="row" gap="s" alignItems="center" wrap="wrap">
|
||||
<div style:order={$isSmallViewport ? 2 : undefined}>
|
||||
<Button
|
||||
secondary
|
||||
icon={$isSmallViewport}
|
||||
ariaLabel={$isSmallViewport ? 'Import zone file' : undefined}
|
||||
on:click={() => (showImportModal = true)}>
|
||||
{#if $isSmallViewport}
|
||||
<Icon size="m" icon={IconUpload} />
|
||||
{:else}
|
||||
<Icon icon={IconUpload} size="s" slot="start" />
|
||||
Import zone file
|
||||
{/if}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div style:order={$isSmallViewport ? 3 : undefined}>
|
||||
<Tooltip>
|
||||
<PinkButton.Button variant="secondary" icon on:click={downloadRecords}>
|
||||
<Icon icon={IconDownload} size="s" />
|
||||
<Icon icon={IconDownload} size={$isSmallViewport ? 'm' : 's'} />
|
||||
</PinkButton.Button>
|
||||
<svelte:fragment slot="tooltip">Export as .txt</svelte:fragment>
|
||||
</Tooltip>
|
||||
</Layout.Stack>
|
||||
<Layout.Stack direction="row" gap="s" inline>
|
||||
<ViewSelector ui="new" view={View.Table} {columns} hideView />
|
||||
</div>
|
||||
|
||||
{#if !$isSmallViewport}
|
||||
<div style="flex: 1"></div>
|
||||
{/if}
|
||||
|
||||
<div style:order={$isSmallViewport ? 4 : undefined}>
|
||||
{#if $isSmallViewport}
|
||||
<Button
|
||||
secondary
|
||||
icon
|
||||
ariaLabel="Display settings"
|
||||
on:click={() => (showDisplaySettingsModal = true)}>
|
||||
<Icon icon={IconAdjustments} />
|
||||
</Button>
|
||||
{:else}
|
||||
<ViewSelector ui="new" view={View.Table} {columns} hideView />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div
|
||||
style:order={$isSmallViewport ? 1 : undefined}
|
||||
style:flex={$isSmallViewport ? 1 : undefined}>
|
||||
<Popover let:toggle padding="none">
|
||||
<Button secondary on:click={toggle}>Add preset</Button>
|
||||
<Button secondary fullWidth={$isSmallViewport} on:click={toggle}
|
||||
>Add preset</Button>
|
||||
<svelte:fragment slot="tooltip" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{#each presets as preset}
|
||||
@@ -100,11 +140,16 @@
|
||||
</ActionMenu.Root>
|
||||
</svelte:fragment>
|
||||
</Popover>
|
||||
<Button size="s" on:click={() => (showCreate = true)}>
|
||||
</div>
|
||||
|
||||
<div
|
||||
style:order={$isSmallViewport ? 5 : undefined}
|
||||
style:width={$isSmallViewport ? '100%' : undefined}>
|
||||
<Button fullWidth={$isSmallViewport} on:click={() => (showCreate = true)}>
|
||||
<Icon size="s" icon={IconPlus} slot="start" />
|
||||
Create record
|
||||
</Button>
|
||||
</Layout.Stack>
|
||||
</div>
|
||||
</Layout.Stack>
|
||||
<Table {data} />
|
||||
{:else}
|
||||
@@ -153,3 +198,11 @@
|
||||
{#if showRetry}
|
||||
<RetryDomainModal bind:show={showRetry} selectedDomain={data.domain} />
|
||||
{/if}
|
||||
|
||||
{#if showDisplaySettingsModal}
|
||||
<DisplaySettingsModal
|
||||
bind:show={showDisplaySettingsModal}
|
||||
{columns}
|
||||
hideView
|
||||
view={View.Table} />
|
||||
{/if}
|
||||
|
||||
+4
-1
@@ -50,7 +50,10 @@
|
||||
</script>
|
||||
|
||||
<Layout.Stack>
|
||||
<Table.Root columns={[...$columns, { id: 'actions', width: 40 }]} let:root>
|
||||
<Table.Root
|
||||
class="responsive-table"
|
||||
columns={[...$columns, { id: 'actions', width: 40 }]}
|
||||
let:root>
|
||||
<svelte:fragment slot="header" let:root>
|
||||
{#each $columns as { id, title } (id)}
|
||||
<Table.Header.Cell column={id} {root}>
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified'
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
|
||||
await goto(routeBase);
|
||||
@@ -76,7 +76,7 @@
|
||||
{#if verified === false}
|
||||
<Badge
|
||||
variant="secondary"
|
||||
type="warning"
|
||||
type="error"
|
||||
size="xs"
|
||||
content="Verification failed" />
|
||||
{:else if verified === true}
|
||||
@@ -84,8 +84,8 @@
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
<Typography.Text variant="m-400">
|
||||
Add the following nameservers on your DNS provider. Note that changes may take up to
|
||||
48 hours to propagate fully.
|
||||
Add the following nameservers on your DNS provider. Note that DNS changes may take
|
||||
up to 48 hours to propagate fully.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
|
||||
@@ -32,17 +32,20 @@
|
||||
const domain = await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: selectedDomain.$id
|
||||
});
|
||||
if (domain.nameservers.toLowerCase() === 'appwrite') {
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${selectedDomain.domain} has been verified`
|
||||
});
|
||||
} else {
|
||||
error =
|
||||
'Domain verification failed. Please check your domain settings or try again later';
|
||||
}
|
||||
|
||||
await Promise.all([invalidate(Dependencies.DOMAIN), invalidate(Dependencies.DOMAINS)]);
|
||||
|
||||
const verified = domain?.nameservers.toLowerCase() === 'appwrite';
|
||||
if (!verified) {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
trackEvent(Submit.DomainUpdateVerification);
|
||||
} catch (e) {
|
||||
error = e.message;
|
||||
@@ -64,8 +67,8 @@
|
||||
>{selectedDomain.domain}</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Typography.Text variant="m-400">
|
||||
Add the following nameservers on your DNS provider. Note that changes may take up to 48
|
||||
hours to propagate fully.
|
||||
Add the following nameservers on your DNS provider. Note that DNS changes may take up to
|
||||
48 hours to propagate fully.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
|
||||
@@ -77,7 +77,11 @@
|
||||
{#if data.view === 'grid'}
|
||||
<Grid {data} bind:showCreate />
|
||||
{:else}
|
||||
<Table {data} />
|
||||
<Table
|
||||
tables={data.tables}
|
||||
policies={data.policies}
|
||||
databases={data.databases}
|
||||
lastBackups={data.lastBackups} />
|
||||
{/if}
|
||||
|
||||
<PaginationWithLimit
|
||||
|
||||
@@ -4,7 +4,6 @@ import { sdk } from '$lib/stores/sdk';
|
||||
import { type Models, Query } from '@appwrite.io/console';
|
||||
import { timeFromNow } from '$lib/helpers/date';
|
||||
import type { PageLoad, RouteParams } from './$types';
|
||||
import type { BackupPolicy } from '$lib/sdk/backups';
|
||||
import { isSelfHosted } from '$lib/system';
|
||||
import { isCloud } from '$lib/system';
|
||||
import type { Plan } from '$lib/sdk/billing';
|
||||
@@ -71,7 +70,8 @@ async function fetchDatabasesAndBackups(
|
||||
})
|
||||
);
|
||||
|
||||
let lastBackups: Record<string, string>, policies: Record<string, BackupPolicy[]>;
|
||||
let lastBackups: Record<string, string> = {};
|
||||
let policies: Record<string, Models.BackupPolicy[]> = {};
|
||||
|
||||
if (isCloud && backupsEnabled) {
|
||||
[policies, lastBackups] = await Promise.all([
|
||||
@@ -86,19 +86,21 @@ async function fetchDatabasesAndBackups(
|
||||
async function fetchPolicies(databases: Models.DatabaseList, params: RouteParams) {
|
||||
if (isSelfHosted) return {};
|
||||
|
||||
const databasePolicies: Record<string, BackupPolicy[]> = {};
|
||||
const databasePolicies: Record<string, Models.BackupPolicy[]> = {};
|
||||
|
||||
await Promise.all(
|
||||
databases.databases.map(async (database) => {
|
||||
try {
|
||||
const { policies } = await sdk
|
||||
.forProject(params.region, params.project)
|
||||
.backups.listPolicies([
|
||||
// TODO: are all needed!?
|
||||
// Query.limit(3),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', database.$id)
|
||||
]);
|
||||
.backups.listPolicies({
|
||||
queries: [
|
||||
// TODO: are all needed!?
|
||||
// Query.limit(3),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', database.$id)
|
||||
]
|
||||
});
|
||||
|
||||
if (policies.length > 0) {
|
||||
databasePolicies[database.$id] = policies;
|
||||
@@ -122,12 +124,14 @@ async function fetchLastBackups(databases: Models.DatabaseList, params: RoutePar
|
||||
try {
|
||||
const { archives } = await sdk
|
||||
.forProject(params.region, params.project)
|
||||
.backups.listArchives([
|
||||
Query.limit(1),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', database.$id)
|
||||
]);
|
||||
.backups.listArchives({
|
||||
queries: [
|
||||
Query.limit(1),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', database.$id)
|
||||
]
|
||||
});
|
||||
|
||||
if (archives.length > 0) {
|
||||
lastBackups[database.$id] = timeFromNow(archives[0].$createdAt);
|
||||
|
||||
@@ -61,16 +61,14 @@
|
||||
const totalPoliciesPromise = totalPolicies.map((policy) => {
|
||||
cronExpression(policy);
|
||||
|
||||
return sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.createPolicy(
|
||||
ID.unique(),
|
||||
['databases'],
|
||||
policy.retained,
|
||||
policy.schedule,
|
||||
policy.label,
|
||||
resourceId
|
||||
);
|
||||
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
|
||||
policyId: ID.unique(),
|
||||
services: ['databases'],
|
||||
retention: policy.retained,
|
||||
schedule: policy.schedule,
|
||||
name: policy.label,
|
||||
resourceId
|
||||
});
|
||||
});
|
||||
|
||||
await Promise.all(totalPoliciesPromise);
|
||||
|
||||
+12
-13
@@ -66,9 +66,10 @@
|
||||
|
||||
const createManualBackup = async () => {
|
||||
try {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.createArchive(['databases'], data.database.$id);
|
||||
await sdk.forProject(page.params.region, page.params.project).backups.createArchive({
|
||||
services: ['databases'],
|
||||
resourceId: data.database.$id
|
||||
});
|
||||
await invalidate(Dependencies.BACKUPS);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
@@ -119,16 +120,14 @@
|
||||
const totalPoliciesPromise = totalPolicies.map((policy) => {
|
||||
cronExpression(policy);
|
||||
|
||||
return sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.createPolicy(
|
||||
ID.unique(),
|
||||
['databases'],
|
||||
policy.retained,
|
||||
policy.schedule,
|
||||
policy.label,
|
||||
data.database.$id
|
||||
);
|
||||
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
|
||||
policyId: ID.unique(),
|
||||
services: ['databases'],
|
||||
retention: policy.retained,
|
||||
schedule: policy.schedule,
|
||||
name: policy.label,
|
||||
resourceId: data.database.$id
|
||||
});
|
||||
});
|
||||
|
||||
try {
|
||||
|
||||
+14
-14
@@ -2,8 +2,8 @@ import { getLimit, getPage, getView, pageToOffset, View } from '$lib/helpers/loa
|
||||
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { Query } from '@appwrite.io/console';
|
||||
import type { BackupArchive, BackupArchiveList, BackupPolicyList } from '$lib/sdk/backups';
|
||||
import { isCloud } from '$lib/system';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
|
||||
export const load = async ({ params, url, route, depends, parent }) => {
|
||||
depends(Dependencies.BACKUPS);
|
||||
@@ -13,8 +13,8 @@ export const load = async ({ params, url, route, depends, parent }) => {
|
||||
const view = getView(url, route, View.Grid);
|
||||
const offset = pageToOffset(page, limit);
|
||||
|
||||
let backups: BackupArchiveList = { total: 0, archives: [] };
|
||||
let policies: BackupPolicyList = { total: 0, policies: [] };
|
||||
let backups: Models.BackupArchiveList = { total: 0, archives: [] };
|
||||
let policies: Models.BackupPolicyList = { total: 0, policies: [] };
|
||||
|
||||
// already loaded by parent.
|
||||
const { currentPlan } = await parent();
|
||||
@@ -23,23 +23,23 @@ export const load = async ({ params, url, route, depends, parent }) => {
|
||||
if (isCloud && backupsEnabled) {
|
||||
try {
|
||||
[backups, policies] = await Promise.all([
|
||||
sdk
|
||||
.forProject(params.region, params.project)
|
||||
.backups.listArchives([
|
||||
sdk.forProject(params.region, params.project).backups.listArchives({
|
||||
queries: [
|
||||
Query.limit(limit),
|
||||
Query.offset(offset),
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
]),
|
||||
]
|
||||
}),
|
||||
|
||||
sdk
|
||||
.forProject(params.region, params.project)
|
||||
.backups.listPolicies([
|
||||
sdk.forProject(params.region, params.project).backups.listPolicies({
|
||||
queries: [
|
||||
Query.orderDesc('$createdAt'),
|
||||
Query.equal('resourceType', 'database'),
|
||||
Query.equal('resourceId', params.database)
|
||||
])
|
||||
]
|
||||
})
|
||||
]);
|
||||
} catch (e) {
|
||||
// ignore
|
||||
@@ -59,17 +59,17 @@ export const load = async ({ params, url, route, depends, parent }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const groupArchivesByPolicy = (archives: BackupArchive[]) => {
|
||||
const groupArchivesByPolicy = (archives: Models.BackupArchive[]) => {
|
||||
return archives.reduce((acc, archive) => {
|
||||
if (!acc.has(archive.policyId)) {
|
||||
acc.set(archive.policyId, []);
|
||||
}
|
||||
acc.get(archive.policyId)!.push(archive);
|
||||
return acc;
|
||||
}, new Map<string, BackupArchive[]>());
|
||||
}, new Map<string, Models.BackupArchive[]>());
|
||||
};
|
||||
|
||||
const getLatestBackupForPolicies = (policyIdMap: Map<string, BackupArchive[]>) => {
|
||||
const getLatestBackupForPolicies = (policyIdMap: Map<string, Models.BackupArchive[]>) => {
|
||||
const latestBackups = new Map<string, string | null>();
|
||||
for (const [policyId, archives] of policyIdMap) {
|
||||
const latestBackup = archives.sort(
|
||||
|
||||
+6
-6
@@ -11,7 +11,6 @@
|
||||
import { toLocaleDateTime } from '$lib/helpers/date';
|
||||
import EmptyDark from '$lib/images/backups/backups-dark.png';
|
||||
import EmptyLight from '$lib/images/backups/backups-light.png';
|
||||
import type { BackupPolicy, BackupPolicyList } from '$lib/sdk/backups';
|
||||
import { backupFrequencies } from '$lib/helpers/backups';
|
||||
import { Click, trackEvent } from '$lib/actions/analytics';
|
||||
import {
|
||||
@@ -27,21 +26,22 @@
|
||||
import { Confirm } from '$lib/components/index.js';
|
||||
import Ellipse from './components/Ellipse.svelte';
|
||||
import { page } from '$app/state';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
|
||||
let showDelete = false;
|
||||
let selectedPolicy: BackupPolicy = null;
|
||||
let selectedPolicy: Models.BackupPolicy = null;
|
||||
|
||||
let showEveryPolicy = false;
|
||||
|
||||
export let showCreatePolicy = false;
|
||||
export let policies: BackupPolicyList;
|
||||
export let policies: Models.BackupPolicyList;
|
||||
export let lastBackupDates: Record<string, string>;
|
||||
|
||||
async function deletePolicy() {
|
||||
try {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.deletePolicy(selectedPolicy.$id);
|
||||
await sdk.forProject(page.params.region, page.params.project).backups.deletePolicy({
|
||||
policyId: selectedPolicy.$id
|
||||
});
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Backup policy has been deleted'
|
||||
|
||||
+15
-14
@@ -15,10 +15,9 @@
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { calculateSize } from '$lib/helpers/sizeConvertion';
|
||||
import { ID } from '@appwrite.io/console';
|
||||
import { ID, type Models } from '@appwrite.io/console';
|
||||
import { columns } from './store';
|
||||
import { database } from '../store';
|
||||
import type { BackupArchive, BackupPolicy } from '$lib/sdk/backups';
|
||||
import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { copy } from '$lib/helpers/copy';
|
||||
import { LabelCard } from '$lib/components/index.js';
|
||||
@@ -53,7 +52,7 @@
|
||||
} = $props();
|
||||
|
||||
let showDelete = $state(false);
|
||||
let selectedBackup: BackupArchive | null = $state(null);
|
||||
let selectedBackup: Models.BackupArchive | null = $state(null);
|
||||
|
||||
let showDropdown = [];
|
||||
|
||||
@@ -87,15 +86,15 @@
|
||||
);
|
||||
});
|
||||
|
||||
function getPolicyDetails(policyId: string | null): BackupPolicy | null {
|
||||
function getPolicyDetails(policyId: string | null): Models.BackupPolicy | null {
|
||||
return data.policies.policies.find((policy) => policy.$id === policyId);
|
||||
}
|
||||
|
||||
function getCleanBackupName(backup: BackupArchive): string {
|
||||
function getCleanBackupName(backup: Models.BackupArchive): string {
|
||||
return toLocaleDateTime(backup.$createdAt).replaceAll(',', '');
|
||||
}
|
||||
|
||||
function getBackupStatus(backup: BackupArchive) {
|
||||
function getBackupStatus(backup: Models.BackupArchive) {
|
||||
switch (backup.status) {
|
||||
case 'pending':
|
||||
return 'pending';
|
||||
@@ -115,7 +114,7 @@
|
||||
try {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.deleteArchive(archiveId);
|
||||
.backups.deleteArchive({ archiveId });
|
||||
|
||||
addNotification({
|
||||
type: 'success',
|
||||
@@ -135,7 +134,9 @@
|
||||
|
||||
async function deleteBackups(batchDelete: DeleteOperation): Promise<DeleteOperationState> {
|
||||
const result = await batchDelete((archiveId) =>
|
||||
sdk.forProject(page.params.region, page.params.project).backups.deleteArchive(archiveId)
|
||||
sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.deleteArchive({ archiveId })
|
||||
);
|
||||
|
||||
try {
|
||||
@@ -160,12 +161,12 @@
|
||||
try {
|
||||
await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.backups.createRestoration(
|
||||
selectedBackup.$id,
|
||||
['databases'],
|
||||
newDatabaseInfo.id ?? ID.unique(),
|
||||
newDatabaseInfo.name
|
||||
);
|
||||
.backups.createRestoration({
|
||||
archiveId: selectedBackup.$id,
|
||||
services: ['databases'],
|
||||
newResourceId: newDatabaseInfo.id ?? ID.unique(),
|
||||
newResourceName: newDatabaseInfo.name
|
||||
});
|
||||
await invalidate(Dependencies.BACKUPS);
|
||||
|
||||
addNotification({
|
||||
|
||||
+4
-4
@@ -297,7 +297,7 @@
|
||||
<Layout.Stack direction="row">
|
||||
<InputSelect
|
||||
{id}
|
||||
required
|
||||
required={column.required}
|
||||
{options}
|
||||
bind:value={relatedList[0]}
|
||||
placeholder={`Select ${column.key}`}
|
||||
@@ -316,7 +316,7 @@
|
||||
<Layout.Stack direction="row">
|
||||
<InputSelect
|
||||
{id}
|
||||
required
|
||||
required={column.required}
|
||||
autofocus={limited}
|
||||
options={getAvailableOptions(actualIndex)}
|
||||
bind:value={relatedList[actualIndex]}
|
||||
@@ -346,7 +346,7 @@
|
||||
<Layout.Stack direction="row">
|
||||
<InputSelect
|
||||
{id}
|
||||
required
|
||||
required={column.required}
|
||||
options={getAvailableOptions(i)}
|
||||
bind:value={item}
|
||||
on:change={updateRelatedList}
|
||||
@@ -376,7 +376,7 @@
|
||||
<Layout.Stack direction="row">
|
||||
<Input.ComboBox
|
||||
{id}
|
||||
required
|
||||
required={column.required}
|
||||
on:change={addNewItem}
|
||||
bind:value={newItemValue}
|
||||
options={availableOptions}
|
||||
|
||||
+14
-3
@@ -95,9 +95,20 @@
|
||||
value = newArray as string[] | number[] | boolean[];
|
||||
}
|
||||
} else {
|
||||
const parsedValue = isSpatialType(column)
|
||||
? JSON.parse(stringValue)
|
||||
: parseValue(stringValue);
|
||||
let parsedValue = parseValue(stringValue);
|
||||
|
||||
if (isSpatialType(column)) {
|
||||
if (!stringValue?.trim()) {
|
||||
parsedValue = null;
|
||||
} else {
|
||||
try {
|
||||
parsedValue = JSON.parse(stringValue);
|
||||
} catch {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (JSON.stringify(parsedValue) !== JSON.stringify(value)) {
|
||||
value = parsedValue;
|
||||
}
|
||||
|
||||
@@ -6,10 +6,19 @@
|
||||
import { columns } from './store';
|
||||
import { IconExclamation } from '@appwrite.io/pink-icons-svelte';
|
||||
import { Layout, Tooltip, Table, Icon } from '@appwrite.io/pink-svelte';
|
||||
import type { BackupPolicy } from '$lib/sdk/backups';
|
||||
import { type Models } from '@appwrite.io/console';
|
||||
|
||||
export let data;
|
||||
const tables = data.tables;
|
||||
let {
|
||||
tables,
|
||||
policies,
|
||||
databases,
|
||||
lastBackups
|
||||
}: {
|
||||
tables: Record<string, string>;
|
||||
databases: Models.DatabaseList;
|
||||
lastBackups: Record<string, string>;
|
||||
policies: Record<string, Models.BackupPolicy[]>;
|
||||
} = $props();
|
||||
|
||||
function getPolicyDescription(cron: string): string {
|
||||
const [minute, hour, dayOfMonth, , dayOfWeek] = cron.split(' ');
|
||||
@@ -19,6 +28,10 @@
|
||||
if (minute !== '*' && hour === '*') return 'Hourly';
|
||||
if (hour !== '*') return 'Daily';
|
||||
}
|
||||
|
||||
function getPoliciesDescription(policies: Models.BackupPolicy[] | null): string {
|
||||
return policies?.map((policy) => getPolicyDescription(policy.schedule)).join(', ') ?? '';
|
||||
}
|
||||
</script>
|
||||
|
||||
<Table.Root columns={$columns} let:root>
|
||||
@@ -27,7 +40,7 @@
|
||||
<Table.Header.Cell column={id} {root}>{title}</Table.Header.Cell>
|
||||
{/each}
|
||||
</svelte:fragment>
|
||||
{#each data.databases.databases as database (database.$id)}
|
||||
{#each databases.databases as database (database.$id)}
|
||||
<!-- takes directly to the spreadsheet -->
|
||||
{@const tableId = tables[database?.$id] ?? null}
|
||||
{@const tableHref = tableId ? `/table-${tableId}` : ''}
|
||||
@@ -45,18 +58,16 @@
|
||||
{:else if column.id === 'name'}
|
||||
{database.name}
|
||||
{:else if column.id === 'backup'}
|
||||
{@const policies = data.policies?.[database.$id] ?? null}
|
||||
{@const lastBackup = data.lastBackups?.[database.$id] ?? null}
|
||||
{@const description = policies
|
||||
?.map((policy: BackupPolicy) => getPolicyDescription(policy.schedule))
|
||||
.join(', ')}
|
||||
{@const backupPolicies = policies?.[database.$id] ?? null}
|
||||
{@const lastBackup = lastBackups?.[database.$id] ?? null}
|
||||
{@const description = getPoliciesDescription(backupPolicies)}
|
||||
|
||||
<Tooltip
|
||||
placement="bottom"
|
||||
disabled={!policies || !lastBackup}
|
||||
disabled={!backupPolicies || !lastBackup}
|
||||
maxWidth="fit-content">
|
||||
<span class="u-trim">
|
||||
{#if !policies}
|
||||
{#if !backupPolicies}
|
||||
<Layout.Stack direction="row" gap="xxs" alignItems="center">
|
||||
<Icon
|
||||
icon={IconExclamation}
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<Fieldset legend="Permissions">
|
||||
<Fieldset legend="Security">
|
||||
<Layout.Stack gap="l">
|
||||
<Selector.Switch
|
||||
id="execute"
|
||||
|
||||
+1
-3
@@ -38,9 +38,7 @@
|
||||
footer?: Snippet;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
let totalSize = $derived(humanFileSize(deployment?.totalSize ?? 0));
|
||||
</script>
|
||||
|
||||
|
||||
+1
-5
@@ -41,11 +41,7 @@
|
||||
let { data } = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(
|
||||
data.deployment.status,
|
||||
data.deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)
|
||||
getEffectiveBuildStatus(data.deployment, $regionalConsoleVariables)
|
||||
);
|
||||
let showDelete = $state(false);
|
||||
let showCancel = $state(false);
|
||||
|
||||
+20
-6
@@ -48,16 +48,24 @@
|
||||
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
let domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
const domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
|
||||
if (apexDomain && !domain && isCloud) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create({
|
||||
await sdk.forConsole.domains.create({
|
||||
teamId: $project.teamId,
|
||||
domain: apexDomain
|
||||
});
|
||||
} catch (error) {
|
||||
// Apex domain creation error needs to be silent.
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,12 +97,18 @@
|
||||
functionId: page.params.function
|
||||
});
|
||||
}
|
||||
if (rule?.status === 'verified') {
|
||||
|
||||
await invalidate(Dependencies.FUNCTION_DOMAINS);
|
||||
|
||||
const verified = rule?.status !== 'created';
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
await goto(routeBase);
|
||||
await invalidate(Dependencies.FUNCTION_DOMAINS);
|
||||
} else {
|
||||
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
|
||||
await invalidate(Dependencies.FUNCTION_DOMAINS);
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
+34
-39
@@ -11,7 +11,6 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -23,6 +22,7 @@
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { getApexDomain } from '$lib/helpers/tlds.js';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
);
|
||||
const showNSTab = isCloud;
|
||||
|
||||
let proxyRule = $derived(data.proxyRule);
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
let routeBase = `${base}/project-${page.params.region}-${page.params.project}/functions/function-${page.params.function}/domains`;
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
@@ -54,44 +55,35 @@
|
||||
}
|
||||
|
||||
async function verify() {
|
||||
const isNewDomain =
|
||||
data.domainsList.domains.find((rule) => rule.domain === data.proxyRule.domain) ===
|
||||
undefined;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
if (selectedTab !== 'nameserver') {
|
||||
const ruleData = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
verified = ruleData.status === 'verified';
|
||||
const apexDomain = getApexDomain(proxyRule.domain);
|
||||
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (ruleData.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
} else if (isNewDomain && isCloud) {
|
||||
const domainData = await sdk.forConsole.domains.create({
|
||||
teamId: $organization.$id,
|
||||
domain: data.proxyRule.domain
|
||||
});
|
||||
verified = domainData.nameservers.toLowerCase() === 'appwrite';
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain added successfully'
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
proxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
|
||||
await Promise.all([
|
||||
invalidate(Dependencies.DOMAINS),
|
||||
invalidate(Dependencies.FUNCTION_DOMAINS)
|
||||
]);
|
||||
await goto(routeBase);
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
await invalidate(Dependencies.FUNCTION_DOMAINS);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
verified = false;
|
||||
isSubmitting.set(false);
|
||||
@@ -108,7 +100,7 @@
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.deleteRule({ ruleId });
|
||||
}
|
||||
await goto(`${routeBase}/add-domain?domain=${data.proxyRule.domain}`);
|
||||
await goto(`${routeBase}/add-domain?domain=${proxyRule.domain}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -125,7 +117,7 @@
|
||||
<Icon icon={IconGlobeAlt} color="--fgcolor-neutral-primary" />
|
||||
|
||||
<Typography.Text variation="m-500" color="--fgcolor-neutral-primary">
|
||||
{data.proxyRule.domain}
|
||||
{proxyRule.domain}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Button secondary on:click={back}>Change</Button>
|
||||
@@ -172,14 +164,17 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={data.proxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
service="functions"
|
||||
variant={selectedTab}
|
||||
domain={data.proxyRule.domain}
|
||||
ruleStatus={data.proxyRule.status}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status}
|
||||
onNavigateToNameservers={() => (selectedTab = 'nameserver')}
|
||||
onNavigateToA={() => (selectedTab = 'a')}
|
||||
onNavigateToAAAA={() => (selectedTab = 'aaaa')} />
|
||||
|
||||
+2
-2
@@ -25,8 +25,8 @@
|
||||
<Badge variant="secondary" type="warning" content="Pending verification" />
|
||||
</Layout.Stack>
|
||||
<Typography.Text variant="m-400">
|
||||
Add the following nameservers on your DNS provider. Note that changes may take
|
||||
up to 48 hours to propagate fully.
|
||||
Add the following nameservers on your DNS provider. Note that DNS changes may
|
||||
take up to 48 hours to propagate fully.
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
|
||||
|
||||
+30
-25
@@ -13,13 +13,16 @@
|
||||
import { Divider, Tabs } from '@appwrite.io/pink-svelte';
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import { getApexDomain } from '$lib/helpers/tlds';
|
||||
|
||||
let {
|
||||
show = $bindable(false),
|
||||
selectedProxyRule
|
||||
selectedProxyRule,
|
||||
domainsList
|
||||
}: {
|
||||
show: boolean;
|
||||
selectedProxyRule: Models.ProxyRule;
|
||||
domainsList?: Models.DomainsList;
|
||||
} = $props();
|
||||
|
||||
const showCNAMETab = $derived(
|
||||
@@ -40,43 +43,42 @@
|
||||
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
let error = $state(null);
|
||||
let verified = $state(false);
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
|
||||
function getDefaultTab() {
|
||||
return showCNAMETab ? 'cname' : showATab ? 'a' : showAAAATab ? 'aaaa' : 'nameserver';
|
||||
}
|
||||
|
||||
async function retryProxyRule() {
|
||||
error = null;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
error = null;
|
||||
const proxyRule = await sdk
|
||||
const apexDomain = getApexDomain(selectedProxyRule.domain);
|
||||
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
selectedProxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
|
||||
|
||||
verified = proxyRule.status === 'verified';
|
||||
await invalidate(Dependencies.FUNCTION_DOMAINS);
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (proxyRule.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${selectedProxyRule.domain} has been verified`
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
});
|
||||
}
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
trackEvent(Submit.DomainUpdateVerification);
|
||||
} catch (e) {
|
||||
verified = false;
|
||||
error =
|
||||
e.message ??
|
||||
'Domain verification failed. Please check your domain settings or try again later';
|
||||
@@ -130,7 +132,10 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={selectedProxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={selectedProxyRule.domain}
|
||||
ruleStatus={selectedProxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
|
||||
+2
-2
@@ -7,14 +7,14 @@ export const columns = writable<Column[]>([
|
||||
title: 'Domain',
|
||||
type: 'string',
|
||||
format: 'string',
|
||||
width: { min: 300 }
|
||||
width: { min: 600 }
|
||||
},
|
||||
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target',
|
||||
type: 'string',
|
||||
width: { min: 120, max: 400 }
|
||||
width: { min: 160, max: 400 }
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@
|
||||
{/if}
|
||||
|
||||
{#if showRetry}
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
|
||||
{/if}
|
||||
|
||||
{#if showLogs}
|
||||
|
||||
+6
-3
@@ -23,6 +23,8 @@
|
||||
import { logStatusConverter } from './store';
|
||||
import { LogsRequest, LogsResponse } from '$lib/components/logs';
|
||||
import { timer } from '$lib/actions/timer';
|
||||
import { getEffectiveExecutionStatus } from '$lib/helpers/executionTimeout';
|
||||
import { func } from '../store';
|
||||
|
||||
export let selectedLogId: string;
|
||||
export let logs: Models.Execution[];
|
||||
@@ -47,6 +49,7 @@
|
||||
}
|
||||
|
||||
$: selectedLog = logs?.find((log) => log.$id === selectedLogId);
|
||||
$: effectiveStatus = selectedLog ? getEffectiveExecutionStatus(selectedLog, $func) : null;
|
||||
$: isFirstLog = logs.findIndex((log) => log.$id === selectedLogId) === 0;
|
||||
$: isLastLog = logs.findIndex((log) => log.$id === selectedLogId) === logs.length - 1;
|
||||
|
||||
@@ -113,12 +116,12 @@
|
||||
|
||||
<Tooltip
|
||||
disabled={!selectedLog?.scheduledAt ||
|
||||
selectedLog.status !== ExecutionStatus.Scheduled}
|
||||
effectiveStatus !== ExecutionStatus.Scheduled}
|
||||
maxWidth="400px">
|
||||
<div>
|
||||
<Status
|
||||
status={logStatusConverter(selectedLog.status)}
|
||||
label={capitalize(selectedLog.status)}>
|
||||
status={logStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)}>
|
||||
</Status>
|
||||
</div>
|
||||
<span slot="tooltip">
|
||||
|
||||
+6
-4
@@ -18,6 +18,7 @@
|
||||
import DualTimeView from '$lib/components/dualTimeView.svelte';
|
||||
import { func } from '../store';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { getEffectiveExecutionStatus } from '$lib/helpers/executionTimeout';
|
||||
import { page } from '$app/state';
|
||||
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
|
||||
import { invalidate } from '$app/navigation';
|
||||
@@ -66,6 +67,7 @@
|
||||
|
||||
{#snippet children(root)}
|
||||
{#each executions.executions as log (log.$id)}
|
||||
{@const effectiveStatus = getEffectiveExecutionStatus(log, $func)}
|
||||
<Table.Row.Button
|
||||
{root}
|
||||
id={log.$id}
|
||||
@@ -100,14 +102,14 @@
|
||||
{:else if column.id === 'trigger'}
|
||||
{capitalize(log.trigger)}
|
||||
{:else if column.id === 'status'}
|
||||
{@const status = log.status}
|
||||
<Tooltip
|
||||
disabled={!log?.scheduledAt || status !== ExecutionStatus.Scheduled}
|
||||
disabled={!log?.scheduledAt ||
|
||||
effectiveStatus !== ExecutionStatus.Scheduled}
|
||||
maxWidth="400px">
|
||||
<div>
|
||||
<Status
|
||||
status={logStatusConverter(status)}
|
||||
label={capitalize(status)}>
|
||||
status={logStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)}>
|
||||
</Status>
|
||||
</div>
|
||||
<span slot="tooltip">
|
||||
|
||||
+1
-2
@@ -95,8 +95,7 @@
|
||||
{#snippet children(root)}
|
||||
{#each data.deploymentList.deployments as deployment (deployment.$id)}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
deployment,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
<Table.Row.Link
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
import ChangeOrganization from './changeOrganization.svelte';
|
||||
import UpdateVariables from '../updateVariables.svelte';
|
||||
import { page } from '$app/state';
|
||||
import UpdateLabels from './updateLabels.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
@@ -84,8 +85,9 @@
|
||||
|
||||
<Container>
|
||||
{#if $project}
|
||||
<UpdateName />
|
||||
{#if $canWriteProjects}
|
||||
<UpdateName />
|
||||
<UpdateLabels />
|
||||
<UpdateServices />
|
||||
<UpdateInstallations {...data.installations} limit={data.limit} offset={data.offset} />
|
||||
<UpdateVariables
|
||||
|
||||
+20
-6
@@ -30,16 +30,24 @@
|
||||
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
let domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
const domain = data.domainsList.domains.find((d: Models.Domain) => d.domain === apexDomain);
|
||||
|
||||
if (apexDomain && !domain && isCloud) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create({
|
||||
await sdk.forConsole.domains.create({
|
||||
teamId: $project.teamId,
|
||||
domain: apexDomain
|
||||
});
|
||||
} catch (error) {
|
||||
// Apex domain creation error needs to be silent.
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,12 +55,18 @@
|
||||
const rule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.createAPIRule({ domain: domainName.toLocaleLowerCase() });
|
||||
if (rule?.status === 'verified') {
|
||||
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
|
||||
const verified = rule?.status !== 'created';
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
await goto(routeBase);
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
} else {
|
||||
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
+31
-40
@@ -11,7 +11,6 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -23,6 +22,7 @@
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { getApexDomain } from '$lib/helpers/tlds.js';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -44,8 +44,8 @@
|
||||
);
|
||||
const showNSTab = isCloud;
|
||||
|
||||
let proxyRule = $derived(data.proxyRule);
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
|
||||
const routeBase = `${base}/project-${page.params.region}-${page.params.project}/settings/domains`;
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
const isSubmitting = writable(false);
|
||||
@@ -55,43 +55,31 @@
|
||||
}
|
||||
|
||||
async function verify() {
|
||||
const isNewDomain =
|
||||
data.domainsList.domains.find((rule) => rule.domain === data.proxyRule.domain) ===
|
||||
undefined;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
if (selectedTab !== 'nameserver') {
|
||||
const ruleData = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
verified = ruleData.status === 'verified';
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (ruleData.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
} else if (isNewDomain && isCloud) {
|
||||
const domainData = await sdk.forConsole.domains.create({
|
||||
teamId: $organization.$id,
|
||||
domain: data.proxyRule.domain
|
||||
});
|
||||
verified = domainData.nameservers.toLowerCase() === 'appwrite';
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain added successfully'
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
const apexDomain = getApexDomain(proxyRule.domain);
|
||||
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
await goto(routeBase);
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
proxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
await goto(routeBase);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
verified = false;
|
||||
isSubmitting.set(false);
|
||||
@@ -108,7 +96,7 @@
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.deleteRule({ ruleId });
|
||||
}
|
||||
await goto(`${routeBase}/add-domain?domain=${data.proxyRule.domain}`);
|
||||
await goto(`${routeBase}/add-domain?domain=${proxyRule.domain}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -125,7 +113,7 @@
|
||||
<Icon icon={IconGlobeAlt} color="--fgcolor-neutral-primary" />
|
||||
|
||||
<Typography.Text variation="m-500" color="--fgcolor-neutral-primary">
|
||||
{data.proxyRule.domain}
|
||||
{proxyRule.domain}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Button secondary on:click={back}>Change</Button>
|
||||
@@ -172,14 +160,17 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={data.proxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
service="general"
|
||||
variant={selectedTab}
|
||||
domain={data.proxyRule.domain}
|
||||
ruleStatus={data.proxyRule.status}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status}
|
||||
onNavigateToNameservers={() => (selectedTab = 'nameserver')}
|
||||
onNavigateToA={() => (selectedTab = 'a')}
|
||||
onNavigateToAAAA={() => (selectedTab = 'aaaa')} />
|
||||
|
||||
+30
-25
@@ -13,13 +13,16 @@
|
||||
import { Divider, Tabs } from '@appwrite.io/pink-svelte';
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import { getApexDomain } from '$lib/helpers/tlds';
|
||||
|
||||
let {
|
||||
show = $bindable(),
|
||||
selectedProxyRule
|
||||
selectedProxyRule,
|
||||
domainsList
|
||||
}: {
|
||||
show: boolean;
|
||||
selectedProxyRule: Models.ProxyRule;
|
||||
domainsList?: Models.DomainsList;
|
||||
} = $props();
|
||||
|
||||
const showCNAMETab = $derived(
|
||||
@@ -40,43 +43,42 @@
|
||||
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
let error = $state(null);
|
||||
let verified = $state(false);
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
|
||||
function getDefaultTab() {
|
||||
return showCNAMETab ? 'cname' : showATab ? 'a' : showAAAATab ? 'aaaa' : 'nameserver';
|
||||
}
|
||||
|
||||
async function retryDomain() {
|
||||
error = null;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
error = null;
|
||||
const proxyRule = await sdk
|
||||
const apexDomain = getApexDomain(selectedProxyRule.domain);
|
||||
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
selectedProxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
|
||||
|
||||
verified = proxyRule.status === 'verified';
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (proxyRule.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${selectedProxyRule.domain} has been verified`
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
});
|
||||
}
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
trackEvent(Submit.DomainUpdateVerification);
|
||||
} catch (e) {
|
||||
verified = false;
|
||||
error =
|
||||
e.message ??
|
||||
'Domain verification failed. Please check your domain settings or try again later';
|
||||
@@ -130,7 +132,10 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={selectedProxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={selectedProxyRule.domain}
|
||||
ruleStatus={selectedProxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
title: 'Domain',
|
||||
type: 'string',
|
||||
format: 'string',
|
||||
width: { min: 300, max: 550 }
|
||||
width: { min: 600 }
|
||||
},
|
||||
{
|
||||
id: 'updated',
|
||||
@@ -227,7 +227,7 @@
|
||||
{/if}
|
||||
|
||||
{#if showRetry}
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
|
||||
{/if}
|
||||
|
||||
{#if showLogs}
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { invalidate } from '$app/navigation';
|
||||
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
|
||||
import { CardGrid } from '$lib/components';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
import { Button, Form, InputTags } from '$lib/elements/forms';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { project } from '../store';
|
||||
import { Tag, Input, Layout, Icon } from '@appwrite.io/pink-svelte';
|
||||
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
|
||||
|
||||
const alphaNumericRegExp = /^[a-zA-Z0-9]+$/;
|
||||
let suggestedLabels = ['live', 'stage', 'internal'];
|
||||
let labels: string[] = [];
|
||||
let error = '';
|
||||
|
||||
onMount(async () => {
|
||||
labels = [...$project.labels];
|
||||
});
|
||||
|
||||
async function updateLabels() {
|
||||
try {
|
||||
await sdk.forConsole.projects.updateLabels({ projectId: $project.$id, labels });
|
||||
await invalidate(Dependencies.PROJECT);
|
||||
isDisabled = true;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Project labels have been updated'
|
||||
});
|
||||
trackEvent(Submit.ProjectUpdateLabels);
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
message: error.message,
|
||||
type: 'error'
|
||||
});
|
||||
trackError(error, Submit.ProjectUpdateLabels);
|
||||
}
|
||||
}
|
||||
|
||||
$: isDisabled = !!error || !symmetricDifference(labels, $project.labels).length;
|
||||
|
||||
$: if (labels) {
|
||||
const invalidLabels = [];
|
||||
|
||||
labels.forEach((label) => {
|
||||
if (!alphaNumericRegExp.test(label)) {
|
||||
invalidLabels.push(label);
|
||||
}
|
||||
});
|
||||
|
||||
if (invalidLabels.length) {
|
||||
error = `Invalid labels: ${invalidLabels.join(', ')}`;
|
||||
} else {
|
||||
error = '';
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<Form onSubmit={updateLabels}>
|
||||
<CardGrid>
|
||||
<svelte:fragment slot="title">Labels</svelte:fragment>
|
||||
Categorize and manage your projects for easy searching based on specific criteria by assigning
|
||||
them customizable labels.
|
||||
<svelte:fragment slot="aside">
|
||||
<Layout.Stack gap="s">
|
||||
{#key labels.length}
|
||||
<InputTags
|
||||
id="project-labels"
|
||||
label="Labels"
|
||||
placeholder="Select or type project labels"
|
||||
bind:tags={labels} />
|
||||
{/key}
|
||||
<Layout.Stack direction="row">
|
||||
{#each suggestedLabels as suggestedLabel}
|
||||
<Tag
|
||||
size="s"
|
||||
selected={labels.includes(suggestedLabel)}
|
||||
on:click={() => {
|
||||
if (!labels.includes(suggestedLabel)) {
|
||||
labels = [...labels, suggestedLabel];
|
||||
} else {
|
||||
labels = labels.filter((e) => e !== suggestedLabel);
|
||||
}
|
||||
}}>
|
||||
<Icon icon={IconPlus} size="s" slot="start" />
|
||||
{suggestedLabel}
|
||||
</Tag>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
<Input.Helper state={error ? 'warning' : 'default'}
|
||||
>{error ? error : 'Only alphanumeric characters are allowed'}</Input.Helper>
|
||||
</Layout.Stack>
|
||||
</svelte:fragment>
|
||||
|
||||
<svelte:fragment slot="actions">
|
||||
<Button disabled={isDisabled} submit>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
</Form>
|
||||
+1
-2
@@ -54,8 +54,7 @@
|
||||
<svelte:fragment slot="menu" let:toggle>
|
||||
<ActionMenu.Root>
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
deployment,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{#if !inCard}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
case 'ready':
|
||||
return 'success';
|
||||
case 'building':
|
||||
case 'finalizing':
|
||||
return 'warning';
|
||||
case 'processing':
|
||||
return undefined;
|
||||
@@ -40,9 +41,7 @@
|
||||
emptyCopy?: string;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
|
||||
function setCopy() {
|
||||
if (effectiveStatus === 'failed') {
|
||||
@@ -73,7 +72,7 @@
|
||||
variant="secondary"
|
||||
type={badgeTypeDeployment(effectiveStatus)} />
|
||||
</Layout.Stack>
|
||||
<LogsTimer status={effectiveStatus} {deployment} />
|
||||
<LogsTimer {deployment} />
|
||||
</Layout.Stack>
|
||||
{/if}
|
||||
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
|
||||
import { Layout, Spinner, Typography } from '@appwrite.io/pink-svelte';
|
||||
|
||||
let { status, deployment }: { status: string; deployment: Models.Deployment } = $props();
|
||||
let { deployment }: { deployment: Models.Deployment } = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
</script>
|
||||
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
{#if ['processing', 'building'].includes(effectiveStatus)}
|
||||
{#if ['processing', 'building', 'finalizing'].includes(effectiveStatus)}
|
||||
<Typography.Code color="--fgcolor-neutral-secondary">
|
||||
<Layout.Stack direction="row" alignItems="center" inline>
|
||||
<p use:timer={{ start: deployment.$createdAt }}></p>
|
||||
|
||||
@@ -42,9 +42,7 @@
|
||||
footer?: Snippet;
|
||||
} = $props();
|
||||
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
let show = $state(false);
|
||||
|
||||
const totalSize = $derived(humanFileSize(deployment?.totalSize ?? 0));
|
||||
|
||||
+61
-17
@@ -12,32 +12,76 @@
|
||||
import { onMount } from 'svelte';
|
||||
import { getFrameworkIcon } from '$lib/stores/sites';
|
||||
import type { Models } from '@appwrite.io/console';
|
||||
import { getEffectiveBuildStatus } from '$lib/helpers/buildTimeout';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
let deployment = $state(data.deployment);
|
||||
let skipScreenshotTimeout: ReturnType<typeof setTimeout> | null = $state(null);
|
||||
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
|
||||
onMount(() => {
|
||||
return realtime.forConsole(page.params.region, 'console', async (response) => {
|
||||
if (
|
||||
response.events.includes(
|
||||
`sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
|
||||
)
|
||||
) {
|
||||
deployment = response.payload as Models.Deployment;
|
||||
if (deployment.status === 'ready') {
|
||||
const resolvedUrl = resolve(
|
||||
'/(console)/project-[region]-[project]/sites/create-site/finish',
|
||||
{
|
||||
region: page.params.region,
|
||||
project: page.params.project
|
||||
const timeoutCleanup = () => {
|
||||
if (skipScreenshotTimeout) {
|
||||
clearTimeout(skipScreenshotTimeout);
|
||||
skipScreenshotTimeout = null;
|
||||
}
|
||||
};
|
||||
|
||||
const realtimeUnsubscribe = realtime.forConsole(
|
||||
page.params.region,
|
||||
'console',
|
||||
async (response) => {
|
||||
if (
|
||||
response.events.includes(
|
||||
`sites.${data.site.$id}.deployments.${data.deployment.$id}.update`
|
||||
)
|
||||
) {
|
||||
deployment = response.payload as Models.Deployment;
|
||||
|
||||
const isReady = deployment.status === 'ready';
|
||||
|
||||
const isFinished =
|
||||
isReady && deployment.screenshotLight && deployment.screenshotDark;
|
||||
|
||||
// Fallback mechanism
|
||||
// If ready but not finished for over 30 seconds, go anyway
|
||||
if (isReady && !skipScreenshotTimeout) {
|
||||
skipScreenshotTimeout = setTimeout(async () => {
|
||||
goToFinishScreen();
|
||||
}, 30000);
|
||||
}
|
||||
|
||||
if (isReady && isFinished) {
|
||||
if (skipScreenshotTimeout) {
|
||||
clearTimeout(skipScreenshotTimeout);
|
||||
skipScreenshotTimeout = null;
|
||||
}
|
||||
);
|
||||
await goto(`${resolvedUrl}?site=${data.site.$id}`);
|
||||
|
||||
goToFinishScreen();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
);
|
||||
|
||||
return () => {
|
||||
realtimeUnsubscribe();
|
||||
timeoutCleanup();
|
||||
};
|
||||
});
|
||||
|
||||
async function goToFinishScreen() {
|
||||
const resolvedUrl = resolve(
|
||||
'/(console)/project-[region]-[project]/sites/create-site/finish',
|
||||
{
|
||||
region: page.params.region,
|
||||
project: page.params.project
|
||||
}
|
||||
);
|
||||
await goto(`${resolvedUrl}?site=${data.site.$id}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
<Wizard
|
||||
@@ -77,7 +121,7 @@
|
||||
</svelte:fragment>
|
||||
<svelte:fragment slot="footer">
|
||||
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
|
||||
{#if ['processing', 'building'].includes(data.deployment.status)}
|
||||
{#if ['processing', 'building', 'finalizing'].includes(effectiveStatus)}
|
||||
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
|
||||
Deployment will continue in the background
|
||||
</Typography.Text>
|
||||
|
||||
+2
-4
@@ -26,9 +26,7 @@
|
||||
let { data }: PageProps = $props();
|
||||
|
||||
let deployment = $derived(data.deployment);
|
||||
let effectiveStatus = $derived(
|
||||
getEffectiveBuildStatus(deployment.status, deployment.$createdAt, $regionalConsoleVariables)
|
||||
);
|
||||
let effectiveStatus = $derived(getEffectiveBuildStatus(deployment, $regionalConsoleVariables));
|
||||
|
||||
let showRedeploy = $state(false);
|
||||
let showActivate = $state(false);
|
||||
@@ -90,7 +88,7 @@
|
||||
hideDivider>
|
||||
<Logs {deployment} hideTitle fullHeight />
|
||||
<svelte:fragment slot="end">
|
||||
<LogsTimer status={effectiveStatus} {deployment} />
|
||||
<LogsTimer {deployment} />
|
||||
</svelte:fragment>
|
||||
</Accordion>
|
||||
</Card>
|
||||
|
||||
+4
-4
@@ -82,10 +82,10 @@
|
||||
{#snippet children(root)}
|
||||
{#each data.deploymentList.deployments as deployment (deployment.$id)}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment.status,
|
||||
deployment.$createdAt,
|
||||
deployment,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{@const displayStatus = effectiveStatus === 'finalizing' ? 'ready' : effectiveStatus}
|
||||
<Table.Row.Link
|
||||
{root}
|
||||
id={deployment.$id}
|
||||
@@ -101,8 +101,8 @@
|
||||
<Status status="complete" label="Active" />
|
||||
{:else}
|
||||
<Status
|
||||
status={deploymentStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
status={deploymentStatusConverter(displayStatus)}
|
||||
label={capitalize(displayStatus)} />
|
||||
{/if}
|
||||
{:else if column.id === 'type'}
|
||||
<DeploymentSource {deployment} />
|
||||
|
||||
+8
-8
@@ -66,6 +66,12 @@
|
||||
<Table.Header.Cell {root} />
|
||||
</svelte:fragment>
|
||||
{#each deploymentList?.deployments as deployment (deployment.$id)}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
deployment,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{@const displayStatus =
|
||||
effectiveStatus === 'finalizing' ? 'ready' : effectiveStatus}
|
||||
<Table.Row.Link
|
||||
{root}
|
||||
href={`${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/deployments/deployment-${deployment.$id}`}>
|
||||
@@ -73,18 +79,12 @@
|
||||
<Id value={deployment.$id}>{deployment.$id}</Id>
|
||||
</Table.Cell>
|
||||
<Table.Cell {root}>
|
||||
{@const status = deployment.status}
|
||||
{@const effectiveStatus = getEffectiveBuildStatus(
|
||||
status,
|
||||
deployment.$createdAt,
|
||||
$regionalConsoleVariables
|
||||
)}
|
||||
{#if activeDeployment?.$id === deployment?.$id}
|
||||
<Status status="complete" label="Active" />
|
||||
{:else}
|
||||
<Status
|
||||
status={deploymentStatusConverter(effectiveStatus)}
|
||||
label={capitalize(effectiveStatus)} />
|
||||
status={deploymentStatusConverter(displayStatus)}
|
||||
label={capitalize(displayStatus)} />
|
||||
{/if}
|
||||
</Table.Cell>
|
||||
|
||||
|
||||
+20
-6
@@ -58,16 +58,24 @@
|
||||
async function addDomain() {
|
||||
const apexDomain = getApexDomain(domainName);
|
||||
const isSiteDomain = domainName.endsWith($regionalConsoleVariables._APP_DOMAIN_SITES);
|
||||
let domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
|
||||
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
|
||||
|
||||
if (isCloud && apexDomain && !domain && !isSiteDomain) {
|
||||
try {
|
||||
domain = await sdk.forConsole.domains.create({
|
||||
await sdk.forConsole.domains.create({
|
||||
teamId: $project.teamId,
|
||||
domain: apexDomain
|
||||
});
|
||||
} catch (error) {
|
||||
// Apex domain creation error needs to be silent.
|
||||
// apex might already be added on organization level, skip.
|
||||
const alreadyAdded = error?.type === 'domain_already_exists';
|
||||
if (!alreadyAdded) {
|
||||
addNotification({
|
||||
type: 'error',
|
||||
message: error.message
|
||||
});
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,12 +107,18 @@
|
||||
siteId: page.params.site
|
||||
});
|
||||
}
|
||||
if (rule?.status === 'verified') {
|
||||
|
||||
await invalidate(Dependencies.SITES_DOMAINS);
|
||||
|
||||
const verified = rule?.status !== 'created';
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
await goto(routeBase);
|
||||
await invalidate(Dependencies.SITES_DOMAINS);
|
||||
} else {
|
||||
await goto(`${routeBase}/add-domain/verify-${domainName}?rule=${rule.$id}`);
|
||||
await invalidate(Dependencies.SITES_DOMAINS);
|
||||
}
|
||||
} catch (error) {
|
||||
addNotification({
|
||||
|
||||
+34
-40
@@ -11,7 +11,6 @@
|
||||
} from '@appwrite.io/pink-svelte';
|
||||
import { Button, Form } from '$lib/elements/forms';
|
||||
import { sdk } from '$lib/stores/sdk';
|
||||
import { organization } from '$lib/stores/organization';
|
||||
import { addNotification } from '$lib/stores/notifications';
|
||||
import { goto, invalidate } from '$app/navigation';
|
||||
import { Dependencies } from '$lib/constants';
|
||||
@@ -23,6 +22,7 @@
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { getApexDomain } from '$lib/helpers/tlds.js';
|
||||
|
||||
let { data } = $props();
|
||||
|
||||
@@ -44,6 +44,7 @@
|
||||
);
|
||||
const showNSTab = isCloud;
|
||||
|
||||
let proxyRule = $derived(data.proxyRule);
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
let routeBase = `${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}/domains`;
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
@@ -54,44 +55,34 @@
|
||||
}
|
||||
|
||||
async function verify() {
|
||||
const isNewDomain =
|
||||
data.domainsList.domains.findIndex((rule) => rule.domain === data.proxyRule.domain) ===
|
||||
-1;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
if (selectedTab !== 'nameserver') {
|
||||
const ruleData = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
verified = ruleData.status === 'verified';
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (ruleData.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
} else if (isNewDomain && isCloud) {
|
||||
const domainData = await sdk.forConsole.domains.create({
|
||||
teamId: $organization.$id,
|
||||
domain: data.proxyRule.domain
|
||||
});
|
||||
verified = domainData.nameservers.toLowerCase() === 'appwrite';
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain added successfully'
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
const apexDomain = getApexDomain(proxyRule.domain);
|
||||
const domain = data.domainsList.domains.find((d) => d.domain === apexDomain);
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
proxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId });
|
||||
|
||||
await Promise.all([
|
||||
invalidate(Dependencies.DOMAINS),
|
||||
invalidate(Dependencies.SITES_DOMAINS)
|
||||
]);
|
||||
await goto(routeBase);
|
||||
await invalidate(Dependencies.DOMAINS);
|
||||
await invalidate(Dependencies.SITES_DOMAINS);
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
} catch (error) {
|
||||
verified = false;
|
||||
isSubmitting.set(false);
|
||||
@@ -108,7 +99,7 @@
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.deleteRule({ ruleId });
|
||||
}
|
||||
await goto(`${routeBase}/add-domain?domain=${data.proxyRule.domain}`);
|
||||
await goto(`${routeBase}/add-domain?domain=${proxyRule.domain}`);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -125,7 +116,7 @@
|
||||
<Icon icon={IconGlobeAlt} color="--fgcolor-neutral-primary" />
|
||||
|
||||
<Typography.Text variation="m-500" color="--fgcolor-neutral-primary">
|
||||
{data.proxyRule.domain}
|
||||
{proxyRule.domain}
|
||||
</Typography.Text>
|
||||
</Layout.Stack>
|
||||
<Button secondary on:click={back}>Change</Button>
|
||||
@@ -172,14 +163,17 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={data.proxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
service="sites"
|
||||
variant={selectedTab}
|
||||
domain={data.proxyRule.domain}
|
||||
ruleStatus={data.proxyRule.status}
|
||||
domain={proxyRule.domain}
|
||||
ruleStatus={proxyRule.status}
|
||||
onNavigateToNameservers={() => (selectedTab = 'nameserver')}
|
||||
onNavigateToA={() => (selectedTab = 'a')}
|
||||
onNavigateToAAAA={() => (selectedTab = 'aaaa')} />
|
||||
|
||||
+30
-25
@@ -13,13 +13,16 @@
|
||||
import NameserverTable from '$lib/components/domains/nameserverTable.svelte';
|
||||
import RecordTable from '$lib/components/domains/recordTable.svelte';
|
||||
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
|
||||
import { getApexDomain } from '$lib/helpers/tlds';
|
||||
|
||||
let {
|
||||
show = $bindable(false),
|
||||
selectedProxyRule
|
||||
selectedProxyRule,
|
||||
domainsList
|
||||
}: {
|
||||
show: boolean;
|
||||
selectedProxyRule: Models.ProxyRule;
|
||||
domainsList?: Models.DomainsList;
|
||||
} = $props();
|
||||
|
||||
const showCNAMETab = $derived(
|
||||
@@ -40,43 +43,42 @@
|
||||
|
||||
let selectedTab = $state<'cname' | 'nameserver' | 'a' | 'aaaa'>(getDefaultTab());
|
||||
let error = $state(null);
|
||||
let verified = $state(false);
|
||||
let verified: boolean | undefined = $state(undefined);
|
||||
|
||||
function getDefaultTab() {
|
||||
return showCNAMETab ? 'cname' : showATab ? 'a' : showAAAATab ? 'aaaa' : 'nameserver';
|
||||
}
|
||||
|
||||
async function retryDomain() {
|
||||
error = null;
|
||||
verified = undefined;
|
||||
|
||||
try {
|
||||
error = null;
|
||||
const proxyRule = await sdk
|
||||
const apexDomain = getApexDomain(selectedProxyRule.domain);
|
||||
const domain = domainsList?.domains.find((d) => d.domain === apexDomain);
|
||||
if (isCloud && domain) {
|
||||
await sdk.forConsole.domains.updateNameservers({
|
||||
domainId: domain.$id
|
||||
});
|
||||
}
|
||||
} catch {
|
||||
// Ignore error
|
||||
}
|
||||
|
||||
try {
|
||||
selectedProxyRule = await sdk
|
||||
.forProject(page.params.region, page.params.project)
|
||||
.proxy.updateRuleVerification({ ruleId: selectedProxyRule.$id });
|
||||
|
||||
verified = proxyRule.status === 'verified';
|
||||
await invalidate(Dependencies.SITES_DOMAINS);
|
||||
|
||||
// This means domain verification using DNS records hasn't succeeded and the rule is still in initial state.
|
||||
if (proxyRule.status === 'created') {
|
||||
throw new Error(
|
||||
'Domain verification failed. Please check your domain settings or try again later'
|
||||
);
|
||||
}
|
||||
|
||||
if (verified) {
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: `${selectedProxyRule.domain} has been verified`
|
||||
});
|
||||
} else {
|
||||
addNotification({
|
||||
type: 'info',
|
||||
message: 'Verification in progress'
|
||||
});
|
||||
}
|
||||
show = false;
|
||||
addNotification({
|
||||
type: 'success',
|
||||
message: 'Domain verified successfully'
|
||||
});
|
||||
trackEvent(Submit.DomainUpdateVerification);
|
||||
} catch (e) {
|
||||
verified = false;
|
||||
error =
|
||||
e.message ??
|
||||
'Domain verification failed. Please check your domain settings or try again later';
|
||||
@@ -130,7 +132,10 @@
|
||||
<Divider />
|
||||
</div>
|
||||
{#if selectedTab === 'nameserver'}
|
||||
<NameserverTable domain={selectedProxyRule.domain} {verified} />
|
||||
<NameserverTable
|
||||
{verified}
|
||||
domain={selectedProxyRule.domain}
|
||||
ruleStatus={selectedProxyRule.status} />
|
||||
{:else}
|
||||
<RecordTable
|
||||
{verified}
|
||||
|
||||
@@ -7,14 +7,14 @@ export const columns = writable<Column[]>([
|
||||
title: 'Domain',
|
||||
type: 'string',
|
||||
format: 'string',
|
||||
width: { min: 300 }
|
||||
width: { min: 600 }
|
||||
},
|
||||
|
||||
{
|
||||
id: 'target',
|
||||
title: 'Target',
|
||||
type: 'string',
|
||||
width: { min: 120, max: 400 }
|
||||
width: { min: 160, max: 400 }
|
||||
},
|
||||
|
||||
{
|
||||
|
||||
+1
-1
@@ -222,7 +222,7 @@
|
||||
{/if}
|
||||
|
||||
{#if showRetry}
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} />
|
||||
<RetryDomainModal bind:show={showRetry} {selectedProxyRule} domainsList={organizationDomains} />
|
||||
{/if}
|
||||
|
||||
{#if showLogs}
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
const res = await fetch(`${endpoint}/account/invite`, {
|
||||
method: 'POST',
|
||||
headers: {
|
||||
'X-Appwrite-Project': project,
|
||||
'X-Appwrite-Project': project as string,
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify({
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
import { ThemeDark, ThemeLight, ThemeDarkCloud, ThemeLightCloud } from '../themes';
|
||||
import { isSmallViewport, updateViewport } from '$lib/stores/viewport';
|
||||
import { feedback } from '$lib/stores/feedback';
|
||||
import '$lib/profiles/css/base.css';
|
||||
|
||||
function resolveTheme(theme: AppStore['themeInUse']) {
|
||||
switch (theme) {
|
||||
|
||||
Reference in New Issue
Block a user