diff --git a/.env.example b/.env.example index 897876704..06149b167 100644 --- a/.env.example +++ b/.env.example @@ -1,3 +1,4 @@ VITE_APPWRITE_ENDPOINT= -VITE_GOOGLE_ANALYTICS= -VITE_SENTRY_DSN= \ No newline at end of file +VITE_GA_PROJECT= +VITE_SENTRY_DSN= +VITE_CONSOLE_MODE=self-hosted \ No newline at end of file diff --git a/README.md b/README.md index eceed7586..0dd094894 100644 --- a/README.md +++ b/README.md @@ -4,10 +4,6 @@ [![Twitter Account](https://img.shields.io/twitter/follow/appwrite?color=00acee&label=twitter&style=flat-square)](https://twitter.com/appwrite) [![appwrite.io](https://img.shields.io/badge/appwrite-.io-f02e65?style=flat-square)](https://appwrite.io) -

-Console 2.0 - Backend as a Service, reimagined by Appwrite | Product Hunt -

- ![Appwrite](github.png) [Appwrite](https://appwrite.io) Console is the Graphical User Interface that developers interact with when accessing their Appwrite instance in the web browser. diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 1f77d0cc3..d169790ac 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -3,12 +3,13 @@ import googleAnalytics from '@analytics/google-analytics'; import { get } from 'svelte/store'; import { page } from '$app/stores'; import { user } from '$lib/stores/user'; +import { Mode } from '$lib/constants'; const analytics = Analytics({ app: 'appwrite', plugins: [ googleAnalytics({ - measurementIds: ['G-R4YJ9JN8L4'] + measurementIds: [import.meta.env.VITE_GA_PROJECT?.toString() || 'G-R4YJ9JN8L4'] }) ] }); @@ -48,6 +49,7 @@ function sendEventToGrowth(event: string, path: string, data: object = null): vo action: event, label: event, url: window.location.origin + path, + account: import.meta.env.VITE_CONSOLE_MODE?.toString() || Mode.SELF_HOSTED, data: { email, name, diff --git a/src/lib/constants.ts b/src/lib/constants.ts index b4942bebc..721c3db12 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -2,6 +2,11 @@ export const PAGE_LIMIT = 12; // default page limit export const CARD_LIMIT = 6; // default card limit export const INTERVAL = 5 * 60000; // default interval to check for feedback +export enum Mode { + CLOUD = 'mode:cloud', + SELF_HOSTED = 'mode:self-hosted' +} + export enum Dependencies { ORGANIZATION = 'dependency:organization', PROJECT = 'dependency:project', diff --git a/src/lib/helpers/array.ts b/src/lib/helpers/array.ts index cd7a923fb..12cde8c4b 100644 --- a/src/lib/helpers/array.ts +++ b/src/lib/helpers/array.ts @@ -3,6 +3,7 @@ export function intersection(arr1: unknown[], arr2: unknown[]) { const intersection = new Set(arr1.filter((elem) => set.has(elem))); return Array.from(intersection); } + export function difference(arr1: unknown[], arr2: unknown[]) { const set = new Set(arr2); const intersection = new Set(arr1.filter((elem) => !set.has(elem))); @@ -12,3 +13,17 @@ export function difference(arr1: unknown[], arr2: unknown[]) { export function symmetricDifference(arr1: unknown[], arr2: unknown[]) { return difference(arr1, arr2).concat(difference(arr2, arr1)); } + +/** + * Removes the element at the specified index from the array, and returns a new array. + * + * @export + * @template T + * @param {T[]} arr + * @param {number} index + * @returns {T[]} + */ +export function remove(arr: T[], index: number): T[] { + // Remove the element at the given index, return a new array + return [...arr.slice(0, index), ...arr.slice(index + 1)]; +} diff --git a/src/lib/stores/oauth-providers.ts b/src/lib/stores/oauth-providers.ts index 36100ed16..4ed398e67 100644 --- a/src/lib/stores/oauth-providers.ts +++ b/src/lib/stores/oauth-providers.ts @@ -5,6 +5,8 @@ import Apple from '../../routes/console/project-[project]/auth/appleOAuth.svelte import Microsoft from '../../routes/console/project-[project]/auth/microsoftOAuth.svelte'; import Okta from '../../routes/console/project-[project]/auth/oktaOAuth.svelte'; import Auth0 from '../../routes/console/project-[project]/auth/auth0OAuth.svelte'; +import Authentik from '../../routes/console/project-[project]/auth/authentikOAuth.svelte'; +import GitLab from '../../routes/console/project-[project]/auth/gitlabOAuth.svelte'; import Main from '../../routes/console/project-[project]/auth/mainOAuth.svelte'; export type Provider = Models.Provider & { @@ -36,6 +38,13 @@ const setProviders = (project: Models.Project): Provider[] => { docs = 'https://auth0.com/developers'; component = Auth0; break; + case 'authentik': + docs = 'https://goauthentik.io/integrations/sources/oauth/'; + component = Authentik; + break; + case 'autodesk': + docs = 'https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/'; + break; case 'bitbucket': docs = 'https://developer.atlassian.com/bitbucket'; break; @@ -45,12 +54,21 @@ const setProviders = (project: Models.Project): Provider[] => { case 'box': docs = 'https://developer.box.com/reference/'; break; + case 'dailymotion': + docs = 'https://developers.dailymotion.com/api/'; + break; case 'discord': docs = 'https://discordapp.com/developers/docs/topics/oauth2'; break; + case 'disqus': + docs = 'https://disqus.com/api/docs/auth/'; + break; case 'dropbox': docs = 'https://www.dropbox.com/developers/documentation'; break; + case 'etsy': + docs = 'https://developers.etsy.com/'; + break; case 'facebook': docs = 'https://developers.facebook.com/'; break; @@ -59,6 +77,7 @@ const setProviders = (project: Models.Project): Provider[] => { break; case 'gitlab': docs = 'https://docs.gitlab.com/ee/api/'; + component = GitLab; break; case 'google': docs = 'https://support.google.com/googleapi/answer/6158849'; @@ -84,6 +103,9 @@ const setProviders = (project: Models.Project): Provider[] => { icon = 'paypal'; docs = 'https://developer.paypal.com/docs/api/overview/'; break; + case 'podio': + docs = 'https://developers.podio.com/doc/oauth-authorization'; + break; case 'salesforce': docs = 'https://developer.salesforce.com/docs/'; break; diff --git a/src/routes/+layout.ts b/src/routes/+layout.ts index 4544b5beb..84e164569 100644 --- a/src/routes/+layout.ts +++ b/src/routes/+layout.ts @@ -8,9 +8,6 @@ import type { LayoutLoad } from './$types'; export const ssr = false; export const load: LayoutLoad = async ({ depends, url }) => { - if (url.pathname.startsWith('/auth')) { - return; - } depends(Dependencies.ACCOUNT); try { const account = await sdkForConsole.account.get(); @@ -20,7 +17,15 @@ export const load: LayoutLoad = async ({ depends, url }) => { organizations: sdkForConsole.teams.list() }; } catch (error) { - const acceptedRoutes = ['/login', '/register', '/recover', '/invite']; + const acceptedRoutes = [ + '/login', + '/register', + '/recover', + '/invite', + '/auth/magic-url', + '/auth/oauth2/success', + '/auth/oauth2/failure' + ]; if (!acceptedRoutes.includes(url.pathname)) { throw redirect(303, '/login'); diff --git a/src/routes/auth/oauth/failure/+page.svelte b/src/routes/auth/oauth2/failure/+page.svelte similarity index 100% rename from src/routes/auth/oauth/failure/+page.svelte rename to src/routes/auth/oauth2/failure/+page.svelte diff --git a/src/routes/auth/oauth/success/+page.svelte b/src/routes/auth/oauth2/success/+page.svelte similarity index 100% rename from src/routes/auth/oauth/success/+page.svelte rename to src/routes/auth/oauth2/success/+page.svelte diff --git a/src/routes/console/account/delete.svelte b/src/routes/console/account/delete.svelte index a64b1321f..bf1a7d2a3 100644 --- a/src/routes/console/account/delete.svelte +++ b/src/routes/console/account/delete.svelte @@ -1,17 +1,19 @@ + + + {provider.name} OAuth2 Settings + +

+ To use {provider.name} authentication in your application, first fill in this form. For more + info you can + visit the docs. +

+ + + + + + To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration. + +
+

URI

+ +
+
+ + + + +
diff --git a/src/routes/console/project-[project]/auth/gitlabOAuth.svelte b/src/routes/console/project-[project]/auth/gitlabOAuth.svelte new file mode 100644 index 000000000..f179f74bc --- /dev/null +++ b/src/routes/console/project-[project]/auth/gitlabOAuth.svelte @@ -0,0 +1,106 @@ + + + + {provider.name} OAuth2 Settings + +

+ To use {provider.name} authentication in your application, first fill in this form. For more + info you can + visit the docs. +

+ + + + + + To complete set up, add this OAuth2 redirect URI to your {provider.name} app configuration. + +
+

URI

+ +
+
+ + + + +
diff --git a/src/routes/console/project-[project]/auth/mainOAuth.svelte b/src/routes/console/project-[project]/auth/mainOAuth.svelte index 43efe29dd..b3be6f62a 100644 --- a/src/routes/console/project-[project]/auth/mainOAuth.svelte +++ b/src/routes/console/project-[project]/auth/mainOAuth.svelte @@ -84,7 +84,7 @@ + }/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} /> diff --git a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte b/src/routes/console/project-[project]/auth/microsoftOAuth.svelte index caeffe743..fc0b38d0a 100644 --- a/src/routes/console/project-[project]/auth/microsoftOAuth.svelte +++ b/src/routes/console/project-[project]/auth/microsoftOAuth.svelte @@ -93,7 +93,7 @@ + }/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} /> diff --git a/src/routes/console/project-[project]/auth/oktaOAuth.svelte b/src/routes/console/project-[project]/auth/oktaOAuth.svelte index c8ff2593a..2fa6dbcb8 100644 --- a/src/routes/console/project-[project]/auth/oktaOAuth.svelte +++ b/src/routes/console/project-[project]/auth/oktaOAuth.svelte @@ -35,7 +35,8 @@ projectId, provider.name.toLowerCase(), appId, - secret + secret, + enabled ); addNotification({ type: 'success', @@ -102,7 +103,7 @@ + }/account/sessions/oauth2/callback/${provider.name.toLocaleLowerCase()}/${projectId}`} /> diff --git a/src/routes/console/project-[project]/auth/user-[user]/+page.svelte b/src/routes/console/project-[project]/auth/user-[user]/+page.svelte index eabcc4125..b48becb71 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/+page.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/+page.svelte @@ -1,500 +1,20 @@ - -
- {#if $user.email || $user.phone} - {#if $user.name} - - {$user.name} - {:else} -
-
- {/if} - {:else} -
-
- {/if} -
- -
-
- {#if $user.email} -

{$user.email}

- {/if} - {#if $user.phone} -

{$user.phone}

- {/if} -

Joined: {toLocaleDateTime($user.registration)}

-
- {#if !$user.status} - blocked - {:else if $user.email && $user.phone} - - {$user.emailVerification && $user.phoneVerification - ? 'verified' - : $user.emailVerification - ? 'verified email' - : $user.phoneVerification - ? 'verified phone' - : 'unverified'} - - {:else} - - {$user.emailVerification - ? 'verified ' - : $user.phoneVerification - ? 'verified ' - : 'unverified'} - - {/if} -
-
- - - - {#if $user.status} - {#if $user.phone && $user.email} - - - - updateVerificationEmail()}> - {$user.emailVerification ? 'Unverify' : 'Verify'} email - - updateVerificationPhone()}> - {$user.phoneVerification ? 'Unverify' : 'Verify'} phone - - - - {:else if $user.email} - - {:else if $user.phone} - - {/if} - {/if} - -
- -
- - Update Name - - -
    - -
-
- - - - -
-
- -
- - Update Email - -
    - -
-
- - - - -
-
- -
- - Update Phone - -
    - -
-
- - - - -
-
- -
- -
- Update Password -
- -

- Enter a new password. A password must contain at least 8 characters. -

- -
    - -
-
- - - - -
-
- -
- - User Preferences -

- You can update your user preferences by storing information on the user's objects so - they can easily be shared across devices and sessions. -

- - -
    - {#if prefs} - {#each prefs as [key, value], index} -
  • -
    - -
    - -
    -
    -
    - -
    - -
    -
    -
    - -
    -
  • - {/each} - {/if} -
- - -
- - - - -
- - - -
- Danger Zone -
-

- The user will be permanently deleted, including all data associated with this user. This - action is irreversible. -

- - - - {#if $user.email || $user.phone} - {#if $user.name} - - {:else} -
-
- {/if} - {:else} -
-
- {/if} -
- -
- {$user.name || $user.email || $user.phone || 'Anonymous'} -
-
-

- {$user.email && $user.phone - ? [$user.email, $user.phone].join(',') - : $user.email || $user.phone} -

-
-
- - - - -
+ + + + + + +
- - diff --git a/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte b/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte new file mode 100644 index 000000000..207f1badd --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/dangerZone.svelte @@ -0,0 +1,53 @@ + + + +
+ Danger Zone +
+

+ The user will be permanently deleted, including all data associated with this user. This + action is irreversible. +

+ + + + {#if $user.email || $user.phone} + {#if $user.name} + + {:else} +
+
+ {/if} + {:else} +
+
+ {/if} +
+ +
+ {$user.name || $user.email || $user.phone || 'Anonymous'} +
+
+

+ {$user.email && $user.phone + ? [$user.email, $user.phone].join(',') + : $user.email || $user.phone} +

+
+
+ + + + +
+ + diff --git a/src/routes/console/project-[project]/auth/user-[user]/memberships/[[page]]/+page.svelte b/src/routes/console/project-[project]/auth/user-[user]/memberships/[[page]]/+page.svelte index a5f8b2fc6..b822f838a 100644 --- a/src/routes/console/project-[project]/auth/user-[user]/memberships/[[page]]/+page.svelte +++ b/src/routes/console/project-[project]/auth/user-[user]/memberships/[[page]]/+page.svelte @@ -72,7 +72,7 @@ diff --git a/src/routes/console/project-[project]/auth/user-[user]/updateEmail.svelte b/src/routes/console/project-[project]/auth/user-[user]/updateEmail.svelte new file mode 100644 index 000000000..5eecbbbed --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updateEmail.svelte @@ -0,0 +1,53 @@ + + +
+ + Update Email + +
    + +
+
+ + + + +
+
diff --git a/src/routes/console/project-[project]/auth/user-[user]/updateName.svelte b/src/routes/console/project-[project]/auth/user-[user]/updateName.svelte new file mode 100644 index 000000000..1b4119579 --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updateName.svelte @@ -0,0 +1,54 @@ + + +
+ + Update Name + + +
    + +
+
+ + + + +
+
diff --git a/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte b/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte new file mode 100644 index 000000000..66116f2c6 --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updatePassword.svelte @@ -0,0 +1,55 @@ + + +
+ +
+ Update Password +
+ +

+ Enter a new password. A password must contain at least 8 characters. +

+ +
    + +
+
+ + + + +
+
diff --git a/src/routes/console/project-[project]/auth/user-[user]/updatePhone.svelte b/src/routes/console/project-[project]/auth/user-[user]/updatePhone.svelte new file mode 100644 index 000000000..d77cd778b --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updatePhone.svelte @@ -0,0 +1,53 @@ + + +
+ + Update Phone + +
    + +
+
+ + + + +
+
diff --git a/src/routes/console/project-[project]/auth/user-[user]/updatePrefs.svelte b/src/routes/console/project-[project]/auth/user-[user]/updatePrefs.svelte new file mode 100644 index 000000000..c000793bc --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updatePrefs.svelte @@ -0,0 +1,129 @@ + + +
+ + User Preferences +

+ You can update your user preferences by storing information on the user's objects so + they can easily be shared across devices and sessions. +

+ + +
    + {#if prefs} + {#each prefs as [key, value], index} +
  • +
    + +
    + +
    +
    +
    + +
    + +
    +
    +
    + +
    +
  • + {/each} + {/if} +
+ + +
+ + + + +
+ diff --git a/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte b/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte new file mode 100644 index 000000000..3f40a875d --- /dev/null +++ b/src/routes/console/project-[project]/auth/user-[user]/updateStatus.svelte @@ -0,0 +1,157 @@ + + + +
+ {#if $user.email || $user.phone} + {#if $user.name} + + {$user.name} + {:else} +
+
+ {/if} + {:else} +
+
+ {/if} +
+ +
+
+ {#if $user.email} +

{$user.email}

+ {/if} + {#if $user.phone} +

{$user.phone}

+ {/if} +

Joined: {toLocaleDateTime($user.registration)}

+
+ {#if !$user.status} + blocked + {:else if $user.email && $user.phone} + + {$user.emailVerification && $user.phoneVerification + ? 'verified' + : $user.emailVerification + ? 'verified email' + : $user.phoneVerification + ? 'verified phone' + : 'unverified'} + + {:else} + + {$user.emailVerification + ? 'verified ' + : $user.phoneVerification + ? 'verified ' + : 'unverified'} + + {/if} +
+
+ + + + {#if $user.status} + {#if $user.phone && $user.email} + + + + updateVerificationEmail()}> + {$user.emailVerification ? 'Unverify' : 'Verify'} email + + updateVerificationPhone()}> + {$user.phoneVerification ? 'Unverify' : 'Verify'} phone + + + + {:else if $user.email} + + {:else if $user.phone} + + {/if} + {/if} + +
diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte index 259f157b5..d819bf935 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/createDocument.svelte @@ -12,6 +12,7 @@ import { createDocument } from './wizard/store'; import type { WizardStepsType } from '$lib/layout/wizard.svelte'; import { Dependencies } from '$lib/constants'; + import { trackEvent } from '$lib/actions/analytics'; const databaseId = $page.params.database; const collectionId = $page.params.collection; @@ -42,6 +43,7 @@ message: 'Document has been created', type: 'success' }); + trackEvent('submit_document_create'); invalidate(Dependencies.DOCUMENTS); wizard.hide(); } catch (error) { diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte index b5dab3650..56f36a8bf 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/document-[document]/document.svelte @@ -12,6 +12,7 @@ import { Dependencies } from '$lib/constants'; import { invalidate } from '$app/navigation'; import Attribute from './attribute.svelte'; + import { trackEvent } from '$lib/actions/analytics'; let disableUpdate = true; let currentDoc: string; @@ -61,6 +62,9 @@ currentDoc = JSON.stringify($work); invalidate(Dependencies.DOCUMENT); + trackEvent('submit_document_update', { + type: 'android' + }); disableUpdate = true; addNotification({ message: 'Document was updated!', diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte index eb3ebecd4..5e7ef6481 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/indexes/createIndex.svelte @@ -11,6 +11,7 @@ import { Dependencies } from '$lib/constants'; import Select from './select.svelte'; import { trackEvent } from '$lib/actions/analytics'; + import { remove } from '$lib/helpers/array'; export let showCreateIndex = false; export let externalAttribute: Attributes = null; @@ -24,115 +25,77 @@ { value: 'unique', label: 'Unique' }, { value: 'fulltext', label: 'FullText' } ]; - let newAttr = false; let selectedType = 'key'; - $: attributeOptions = $collection.attributes.map((attribute: Attributes) => ({ + + let attributeOptions = $collection.attributes.map((attribute: Attributes) => ({ value: attribute.key, label: attribute.key })); - $: attributeList = []; + let attributeList = [{ value: '', order: '' }]; - let selectedAttribute = ''; - let selectedOrder = ''; + $: addAttributeDisabled = !attributeList.at(-1)?.value || !attributeList.at(-1)?.order; onMount(() => { - if (externalAttribute) { - attributeList = [{ value: externalAttribute.key, order: 'ASC' }]; - } + if (!externalAttribute) return; + attributeList = [{ value: externalAttribute.key, order: 'ASC' }]; }); $: if (showCreateIndex) { - attributeList = []; - selectedOrder = selectedAttribute = ''; + attributeList = [{ value: '', order: '' }]; selectedType = 'key'; key = null; } - const created = async () => { - if (key && selectedAttribute && selectedOrder && selectedType) { - if (selectedAttribute && selectedOrder) { - attributeList.push({ value: selectedAttribute, order: selectedOrder }); - selectedAttribute = selectedOrder = ''; - } - try { - await sdkForProject.databases.createIndex( - databaseId, - $collection.$id, - key, - selectedType, - attributeList.map((a) => a.value), - attributeList.map((a) => a.order) - ); - invalidate(Dependencies.COLLECTION); - addNotification({ - message: 'Index has been created', - type: 'success' - }); - trackEvent('submit_index_create'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } + async function create() { + if (!(key && selectedType && !addAttributeDisabled)) { + error = 'All fields are required'; + return; + } + + try { + await sdkForProject.databases.createIndex( + databaseId, + $collection.$id, + key, + selectedType, + attributeList.map((a) => a.value), + attributeList.map((a) => a.order) + ); + invalidate(Dependencies.COLLECTION); + addNotification({ + message: 'Index has been created', + type: 'success' + }); + trackEvent('submit_index_create'); + } catch (error) { + addNotification({ + message: error.message, + type: 'error' + }); + } finally { showCreateIndex = false; - } else error = 'All fields are required'; - }; + } + } + + function addAttribute() { + if (addAttributeDisabled) return; + + // We assign instead of pushing to trigger Svelte's reactivity + attributeList = [...attributeList, { value: '', order: '' }]; + } - + Create Index - {#if attributeList?.length} - {#each attributeList as index, i} -
  • -
    - -
    -
    - -
    - -
    - -
    -
  • - {/each} - {/if} - {#if !attributeList?.length || newAttr} + {#each attributeList as attribute, i}
  • -
    - + @@ -141,9 +104,7 @@ {#each attributeOptions as option} - {/each} @@ -151,36 +112,28 @@
    - +
    +
  • - {/if} - diff --git a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/+page.svelte b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/+page.svelte index d64073937..0ba34d889 100644 --- a/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/+page.svelte +++ b/src/routes/console/project-[project]/databases/database-[database]/collection-[collection]/settings/+page.svelte @@ -77,7 +77,9 @@ databaseId, $collection.$id, collectionName, - $collection.$permissions + $collection.$permissions, + $collection.documentSecurity, + $collection.enabled ); invalidate(Dependencies.COLLECTION); showError = false; @@ -96,7 +98,9 @@ databaseId, $collection.$id, $collection.name, - collectionPermissions + collectionPermissions, + $collection.documentSecurity, + $collection.enabled ); invalidate(Dependencies.COLLECTION); arePermsDisabled = true; @@ -120,7 +124,8 @@ $collection.$id, $collection.name, $collection.$permissions, - collectionDocumentSecurity + collectionDocumentSecurity, + $collection.enabled ); invalidate(Dependencies.COLLECTION); arePermsDisabled = true; diff --git a/src/routes/console/project-[project]/functions/createVariable.svelte b/src/routes/console/project-[project]/functions/createVariable.svelte index 2654e31c6..0fa4a6706 100644 --- a/src/routes/console/project-[project]/functions/createVariable.svelte +++ b/src/routes/console/project-[project]/functions/createVariable.svelte @@ -16,14 +16,19 @@ const dispatch = createEventDispatcher(); - const handleVariable = () => { + function close() { + showCreate = false; + selectedVar = null; + } + + function handleVariable() { if (selectedVar) { dispatch('updated', pair); } else { dispatch('created', pair); } - showCreate = false; - }; + close(); + } @@ -48,7 +53,7 @@ required />
    - +
    diff --git a/src/routes/console/project-[project]/functions/function-[function]/settings/+page.svelte b/src/routes/console/project-[project]/functions/function-[function]/settings/+page.svelte index 7a1e065d5..01ae6b3b6 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/settings/+page.svelte +++ b/src/routes/console/project-[project]/functions/function-[function]/settings/+page.svelte @@ -70,7 +70,15 @@ async function updateName() { try { - await sdkForProject.functions.update(functionId, functionName, $func.execute); + await sdkForProject.functions.update( + functionId, + functionName, + $func.execute, + $func.events, + $func.schedule, + $func.timeout, + $func.enabled + ); invalidate(Dependencies.FUNCTION); addNotification({ message: 'Name has been updated', @@ -87,7 +95,15 @@ async function updatePermissions() { try { - await sdkForProject.functions.update(functionId, $func.name, permissions); + await sdkForProject.functions.update( + functionId, + $func.name, + permissions, + $func.events, + $func.schedule, + $func.timeout, + $func.enabled + ); invalidate(Dependencies.FUNCTION); addNotification({ message: 'Permissions have been updated', @@ -108,7 +124,10 @@ functionId, $func.name, $func.execute, - Array.from($eventSet) + Array.from($eventSet), + $func.schedule, + $func.timeout, + $func.enabled ); invalidate(Dependencies.FUNCTION); addNotification({ @@ -132,7 +151,8 @@ $func.execute, $func.events, functionSchedule, - timeout + $func.timeout, + $func.enabled ); invalidate(Dependencies.FUNCTION); @@ -157,9 +177,11 @@ $func.execute, $func.events, $func.schedule, - timeout + timeout, + $func.enabled ); + invalidate(Dependencies.FUNCTION); addNotification({ type: 'success', message: 'Timeout has been updated' @@ -557,10 +579,12 @@ - +{#if showVariablesModal} + +{/if} diff --git a/src/routes/console/project-[project]/functions/function-[function]/store.ts b/src/routes/console/project-[project]/functions/function-[function]/store.ts index 726e6cd0e..0dd72124b 100644 --- a/src/routes/console/project-[project]/functions/function-[function]/store.ts +++ b/src/routes/console/project-[project]/functions/function-[function]/store.ts @@ -2,5 +2,5 @@ import { page } from '$app/stores'; import { derived, writable, type Writable } from 'svelte/store'; import type { Models } from '@aw-labs/appwrite-console'; -export const func = derived(page, ($page) => $page.data.function); +export const func = derived(page, ($page) => $page.data.function as Models.Function); export const execute: Writable = writable(); diff --git a/src/routes/console/project-[project]/overview/keys/[key]/+page@project-[project].svelte b/src/routes/console/project-[project]/overview/keys/[key]/+page@project-[project].svelte index 70d32ee50..ff3f5b24b 100644 --- a/src/routes/console/project-[project]/overview/keys/[key]/+page@project-[project].svelte +++ b/src/routes/console/project-[project]/overview/keys/[key]/+page@project-[project].svelte @@ -13,6 +13,7 @@ import { key } from './store'; import Scopes from '../scopes.svelte'; import Delete from './delete.svelte'; + import { trackEvent } from '$lib/actions/analytics'; let showDelete = false; let name: string = null; @@ -37,6 +38,7 @@ $key.expire ); invalidate(Dependencies.KEY); + trackEvent('submit_key_update_name'); addNotification({ type: 'success', message: 'API Key name has been updated' @@ -59,6 +61,7 @@ expire ); invalidate(Dependencies.KEY); + trackEvent('submit_key_update_expire'); addNotification({ type: 'success', message: 'API Key expiration has been updated' @@ -73,8 +76,15 @@ async function updateScopes() { try { - await sdkForConsole.projects.updateKey($project.$id, $key.$id, $key.name, scopes); + await sdkForConsole.projects.updateKey( + $project.$id, + $key.$id, + $key.name, + scopes, + $key.expire + ); invalidate(Dependencies.KEY); + trackEvent('submit_key_update_scopes'); addNotification({ type: 'success', message: 'API Key scopes has been updated' diff --git a/src/routes/console/project-[project]/overview/keys/wizard.svelte b/src/routes/console/project-[project]/overview/keys/wizard.svelte index 9217ccd88..e302d0b74 100644 --- a/src/routes/console/project-[project]/overview/keys/wizard.svelte +++ b/src/routes/console/project-[project]/overview/keys/wizard.svelte @@ -12,6 +12,7 @@ import { onDestroy } from 'svelte'; import { onboarding } from '../../store'; import { Dependencies } from '$lib/constants'; + import { trackEvent } from '$lib/actions/analytics'; async function onFinish() { try { @@ -24,6 +25,7 @@ if ($onboarding) { invalidate(Dependencies.PROJECT); } + trackEvent('submit_key_create'); goto(`/console/project-${$page.params.project}/overview/keys/${$id}`); } catch (error) { addNotification({ diff --git a/src/routes/console/project-[project]/overview/platforms/[platform]/+page@project-[project].svelte b/src/routes/console/project-[project]/overview/platforms/[platform]/+page@project-[project].svelte index baec0ded4..b66a71d9e 100644 --- a/src/routes/console/project-[project]/overview/platforms/[platform]/+page@project-[project].svelte +++ b/src/routes/console/project-[project]/overview/platforms/[platform]/+page@project-[project].svelte @@ -48,7 +48,14 @@ const updateName = async () => { try { - await sdkForConsole.projects.updatePlatform($project.$id, data.platform.$id, name); + await sdkForConsole.projects.updatePlatform( + $project.$id, + data.platform.$id, + name, + data.platform.key, + data.platform.store, + data.platform.hostname + ); invalidate(Dependencies.PLATFORM); addNotification({ type: 'success', diff --git a/src/routes/console/project-[project]/overview/platforms/[platform]/android.svelte b/src/routes/console/project-[project]/overview/platforms/[platform]/android.svelte index de95db47c..ebcd4ce21 100644 --- a/src/routes/console/project-[project]/overview/platforms/[platform]/android.svelte +++ b/src/routes/console/project-[project]/overview/platforms/[platform]/android.svelte @@ -1,5 +1,6 @@ diff --git a/src/routes/console/project-[project]/overview/platforms/wizard/web/step3.svelte b/src/routes/console/project-[project]/overview/platforms/wizard/web/step3.svelte index e13329c53..c9685da2e 100644 --- a/src/routes/console/project-[project]/overview/platforms/wizard/web/step3.svelte +++ b/src/routes/console/project-[project]/overview/platforms/wizard/web/step3.svelte @@ -4,9 +4,9 @@ import { sdkForProject } from '$lib/stores/sdk'; const { endpoint, project } = sdkForProject.client.config; - const code = `const appwrite = new Appwrite(); + const code = `const client = new Client(); -appwrite +client .setEndpoint('${endpoint}') .setProject('${project}');`; diff --git a/src/routes/console/project-[project]/settings/webhooks/[webhook]/+page.svelte b/src/routes/console/project-[project]/settings/webhooks/[webhook]/+page.svelte index 3a3383fa1..9b7195420 100644 --- a/src/routes/console/project-[project]/settings/webhooks/[webhook]/+page.svelte +++ b/src/routes/console/project-[project]/settings/webhooks/[webhook]/+page.svelte @@ -55,7 +55,9 @@ name, $webhook.events, $webhook.url, - $webhook.security + $webhook.security, + $webhook.httpUser, + $webhook.httpPass ); invalidate(Dependencies.WEBHOOK); addNotification({ @@ -78,7 +80,9 @@ $webhook.name, $webhook.events, url, - $webhook.security + $webhook.security, + $webhook.httpUser, + $webhook.httpPass ); invalidate(Dependencies.WEBHOOK); addNotification({ @@ -101,7 +105,9 @@ $webhook.name, Array.from($eventSet), $webhook.url, - $webhook.security + $webhook.security, + $webhook.httpUser, + $webhook.httpPass ); invalidate(Dependencies.WEBHOOK); areEventsDisabled = true; @@ -288,6 +294,7 @@ diff --git a/src/routes/console/project-[project]/storage/bucket-[bucket]/settings/+page.svelte b/src/routes/console/project-[project]/storage/bucket-[bucket]/settings/+page.svelte index 8e611b5fc..393eac3e9 100644 --- a/src/routes/console/project-[project]/storage/bucket-[bucket]/settings/+page.svelte +++ b/src/routes/console/project-[project]/storage/bucket-[bucket]/settings/+page.svelte @@ -24,6 +24,8 @@ import { Dependencies } from '$lib/constants'; import Delete from '../deleteBucket.svelte'; import { trackEvent } from '$lib/actions/analytics'; + import { writable } from 'svelte/store'; + import type { Models } from '@aw-labs/appwrite-console'; let showDelete = false; @@ -35,13 +37,14 @@ encryption: boolean = null, antivirus: boolean = null, maxSize: number; - let byteUnit: 'Bytes' | 'KB' | 'MB' | 'GB' = 'MB', - options = [ - { label: 'Bytes', value: 'Bytes' }, - { label: 'Kilobytes', value: 'KB' }, - { label: 'Megabytes', value: 'MB' }, - { label: 'Gigabytes', value: 'GB' } - ]; + let byteUnit = writable('KB'); + let sizeInBytes: number = null; + const options = [ + { label: 'Bytes', value: 'Bytes' }, + { label: 'Kilobytes', value: 'KB' }, + { label: 'Megabytes', value: 'MB' }, + { label: 'Gigabytes', value: 'GB' } + ]; let suggestedExtensions = ['jpg', 'png', 'svg', 'gif', 'html', 'pdf', 'mp4']; let extensions = $bucket.allowedFileExtensions; let isExtensionsDisabled = true; @@ -54,6 +57,7 @@ bucketPermissions ??= $bucket.$permissions; encryption ??= $bucket.encryption; antivirus ??= $bucket.antivirus; + maxSize = $bucket.maximumFileSize / 1024; }); $: if (bucketPermissions) { if (symmetricDifference(bucketPermissions, $bucket.$permissions).length) { @@ -66,78 +70,44 @@ } else isExtensionsDisabled = true; } - async function toggleBucket() { + type TUpdateBucketMisc = { + successMessage?: string; + trackEventName: string; + trackEventData?: { value: boolean }; + arePermsDisabled?: boolean; + }; + + async function updateBucket(updates: Partial, misc: TUpdateBucketMisc) { + const values = { ...$bucket, ...updates }; + try { await sdkForProject.storage.updateBucket( - $bucket.$id, - $bucket.name, - undefined, - undefined, - enabled + values.$id, + values.name, + values.$permissions, + values.fileSecurity, + values.enabled, + values.maximumFileSize, + values.allowedFileExtensions, + values.compression, + values.encryption, + values.antivirus ); + invalidate(Dependencies.BUCKET); + + if (misc.arePermsDisabled !== undefined) { + arePermsDisabled = misc.arePermsDisabled; + } + addNotification({ - message: `${$bucket.name} has been updated`, + message: misc.successMessage ?? `${$bucket.name} has been updated`, type: 'success' }); - trackEvent('submit_bucket_enable', { - value: enabled - }); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } - } - async function updateName() { - try { - await sdkForProject.storage.updateBucket($bucket.$id, bucketName); - invalidate(Dependencies.BUCKET); - addNotification({ - message: 'Name has been updated', - type: 'success' - }); - trackEvent('submit_bucket_update_name'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } - } - async function updatePermissions() { - try { - await sdkForProject.storage.updateBucket($bucket.$id, $bucket.name, bucketPermissions); - invalidate(Dependencies.BUCKET); - arePermsDisabled = true; - addNotification({ - message: 'Permissions have been updated', - type: 'success' - }); - trackEvent('submit_bucket_update_permissions'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } - } - async function updateFileSecurity() { - try { - await sdkForProject.storage.updateBucket( - $bucket.$id, - $bucket.name, - $bucket.$permissions, - bucketFileSecurity - ); - invalidate(Dependencies.BUCKET); - arePermsDisabled = true; - addNotification({ - message: 'Security has been updated', - type: 'success' - }); - trackEvent('submit_bucket_update_file_security'); + + if (misc.trackEventName) { + trackEvent(misc.trackEventName, misc.trackEventData); + } } catch (error) { addNotification({ message: error.message, @@ -146,81 +116,107 @@ } } - async function updateSecurity() { - try { - await sdkForProject.storage.updateBucket( - $bucket.$id, - $bucket.name, - undefined, - undefined, - undefined, - undefined, - undefined, - undefined, + function toggleBucket() { + updateBucket( + { + enabled + }, + { + trackEventName: 'submit_bucket_enable', + trackEventData: { + value: enabled + } + } + ); + } + + function updateName() { + updateBucket( + { + name: bucketName + }, + { + successMessage: 'Name has been updated', + trackEventName: 'submit_bucket_update_name' + } + ); + } + + function updatePermissions() { + updateBucket( + { + $permissions: bucketPermissions + }, + { + successMessage: 'Permissions have been updated', + trackEventName: 'submit_bucket_update_permissions', + arePermsDisabled: true + } + ); + } + + function updateFileSecurity() { + updateBucket( + { + fileSecurity: bucketFileSecurity + }, + { + successMessage: 'Security has been updated', + trackEventName: 'submit_bucket_update_file_security', + arePermsDisabled: true + } + ); + } + + function updateSecurity() { + updateBucket( + { encryption, antivirus - ); - invalidate(Dependencies.BUCKET); - addNotification({ - message: `${$bucket.name} has been updated`, - type: 'success' - }); - trackEvent('submit_bucket_update_security'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } - } - async function updateMaxSize() { - let size = sizeToBytes(maxSize, byteUnit); - try { - await sdkForProject.storage.updateBucket( - $bucket.$id, - $bucket.name, - undefined, - undefined, - undefined, - size - ); - invalidate(Dependencies.BUCKET); - addNotification({ - message: `${$bucket.name} has been updated`, - type: 'success' - }); - trackEvent('submit_bucket_update_size'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); - } + }, + { + trackEventName: 'submit_bucket_update_security' + } + ); } - async function updateAllowedExtensions() { - try { - await sdkForProject.storage.updateBucket( - $bucket.$id, - $bucket.name, - undefined, - undefined, - undefined, - undefined, - extensions - ); - invalidate(Dependencies.BUCKET); - addNotification({ - message: `${$bucket.name} has been updated`, - type: 'success' - }); - trackEvent('submit_bucket_update_extensions'); - } catch (error) { - addNotification({ - message: error.message, - type: 'error' - }); + function updateMaxSize() { + const size = sizeToBytes(maxSize, $byteUnit); + updateBucket( + { + maximumFileSize: size + }, + { + trackEventName: 'submit_bucket_update_size' + } + ); + } + + function updateAllowedExtensions() { + updateBucket( + { + allowedFileExtensions: extensions + }, + { + trackEventName: 'submit_bucket_update_extensions' + } + ); + } + + byteUnit.subscribe((b) => { + if (b === 'Bytes') { + maxSize = sizeInBytes; + } else if (b === 'KB') { + maxSize = sizeInBytes / 1024; + } else if (b === 'MB') { + maxSize = sizeInBytes / 1024 / 1024; + } else if (b === 'GB') { + maxSize = sizeInBytes / 1024 / 1024 / 1024; } + }); + + $: if (maxSize) { + sizeInBytes = sizeToBytes(maxSize, $byteUnit); } @@ -384,7 +380,9 @@ + submit + >Update +
    @@ -398,14 +396,16 @@ - +
    - + diff --git a/static/icons/dark/color/cpp.svg b/static/icons/dark/color/cpp.svg new file mode 100644 index 000000000..e224115f3 --- /dev/null +++ b/static/icons/dark/color/cpp.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/icons/dark/color/dotnet.svg b/static/icons/dark/color/dotnet.svg new file mode 100644 index 000000000..8800d2d72 --- /dev/null +++ b/static/icons/dark/color/dotnet.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/icons/dark/grayscale/cpp.svg b/static/icons/dark/grayscale/cpp.svg new file mode 100644 index 000000000..4824c7f47 --- /dev/null +++ b/static/icons/dark/grayscale/cpp.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/icons/dark/grayscale/dotnet.svg b/static/icons/dark/grayscale/dotnet.svg new file mode 100644 index 000000000..3f8694d65 --- /dev/null +++ b/static/icons/dark/grayscale/dotnet.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/icons/light/color/cpp.svg b/static/icons/light/color/cpp.svg new file mode 100644 index 000000000..a1251ac73 --- /dev/null +++ b/static/icons/light/color/cpp.svg @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/static/icons/light/color/dotnet.svg b/static/icons/light/color/dotnet.svg new file mode 100644 index 000000000..8a366f68b --- /dev/null +++ b/static/icons/light/color/dotnet.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/icons/light/grayscale/cpp.svg b/static/icons/light/grayscale/cpp.svg new file mode 100644 index 000000000..9879612ca --- /dev/null +++ b/static/icons/light/grayscale/cpp.svg @@ -0,0 +1,3 @@ + + + diff --git a/static/icons/light/grayscale/dotnet.svg b/static/icons/light/grayscale/dotnet.svg new file mode 100644 index 000000000..f49950efb --- /dev/null +++ b/static/icons/light/grayscale/dotnet.svg @@ -0,0 +1,3 @@ + + +