diff --git a/package.json b/package.json index 48ecacba4..61db45640 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "@appwrite.io/pink-icons": "0.25.0", "@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@bd21ff7f", "@appwrite.io/pink-legacy": "^1.0.3", - "@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4", + "@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9", "@popperjs/core": "^2.11.8", "@sentry/sveltekit": "^8.38.0", "@stripe/stripe-js": "^3.5.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7307f4b8c..765f4c915 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -27,8 +27,8 @@ importers: specifier: ^1.0.3 version: 1.0.3 '@appwrite.io/pink-svelte': - specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4 - version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3) + specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9 + version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3) '@popperjs/core': specifier: ^2.11.8 version: 2.11.8 @@ -282,8 +282,8 @@ packages: peerDependencies: svelte: ^4.0.0 - '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@de27ca407599e51b378814b421fb63625cb98c10': - resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@de27ca407599e51b378814b421fb63625cb98c10} + '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@e5a77b9b25e2cb5e55587e93725d7a06df0b3d94': + resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@e5a77b9b25e2cb5e55587e93725d7a06df0b3d94} version: 1.0.0-next.7 peerDependencies: svelte: ^4.0.0 @@ -297,8 +297,8 @@ packages: '@appwrite.io/pink-legacy@1.0.3': resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==} - '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4': - resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4} + '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9': + resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9} version: 1.0.0-next.85 peerDependencies: react-dom: ^18.0.0 @@ -3836,7 +3836,7 @@ snapshots: dependencies: svelte: 5.25.3 - '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@de27ca407599e51b378814b421fb63625cb98c10(svelte@5.25.3)': + '@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@e5a77b9b25e2cb5e55587e93725d7a06df0b3d94(svelte@5.25.3)': dependencies: svelte: 5.25.3 @@ -3849,9 +3849,9 @@ snapshots: '@appwrite.io/pink-icons': 1.0.0 the-new-css-reset: 1.11.3 - '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@de27ca4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)': + '@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@e5a77b9(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)': dependencies: - '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@de27ca407599e51b378814b421fb63625cb98c10(svelte@5.25.3) + '@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@e5a77b9b25e2cb5e55587e93725d7a06df0b3d94(svelte@5.25.3) '@floating-ui/dom': 1.6.13 '@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3) '@melt-ui/svelte': 0.86.6(svelte@5.25.3) diff --git a/src/lib/actions/analytics.ts b/src/lib/actions/analytics.ts index 1655166df..8ca755571 100644 --- a/src/lib/actions/analytics.ts +++ b/src/lib/actions/analytics.ts @@ -152,6 +152,7 @@ export enum Click { DatabaseIndexDelete = 'click_index_delete', DatabaseCollectionDelete = 'click_collection_delete', DatabaseDatabaseDelete = 'click_database_delete', + DatabaseImportCsv = 'click_database_import_csv', DomainCreateClick = 'click_domain_create', DomainDeleteClick = 'click_domain_delete', DomainRetryDomainVerificationClick = 'click_domain_retry_domain_verification', @@ -161,6 +162,7 @@ export enum Click { FunctionsDeploymentDeleteClick = 'click_deployment_delete', FunctionsDeploymentCancelClick = 'click_deployment_cancel', KeyCreateClick = 'click_key_create', + DevKeyCreateClick = 'click_dev_key_create', MenuDropDownClick = 'click_menu_dropdown', MenuOverviewClick = 'click_menu_overview', ModalCloseClick = 'click_close_modal', @@ -270,6 +272,7 @@ export enum Submit { DatabaseCreate = 'submit_database_create', DatabaseDelete = 'submit_database_delete', DatabaseUpdateName = 'submit_database_update_name', + DatabaseImportCsv = 'submit_database_import_csv', AttributeCreate = 'submit_attribute_create', AttributeUpdate = 'submit_attribute_update', AttributeDelete = 'submit_attribute_delete', @@ -311,12 +314,18 @@ export enum Submit { VariableDelete = 'submit_variable_delete', VariableUpdate = 'submit_variable_update', VariableEditor = 'submit_variable_editor', + KeyCreate = 'submit_key_create', KeyDelete = 'submit_key_delete', KeyUpdateName = 'submit_key_update_name', KeyUpdateScopes = 'submit_key_update_scopes', KeyUpdateExpire = 'submit_key_update_expire', + DevKeyCreate = 'submit_dev_key_create', + DevKeyDelete = 'submit_dev_key_delete', + DevKeyUpdateName = 'submit_dev_key_update_name', + DevKeyUpdateExpire = 'submit_dev_key_update_expire', + PlatformCreate = 'submit_platform_create', PlatformDelete = 'submit_platform_delete', PlatformUpdate = 'submit_platform_update', diff --git a/src/lib/components/csvImportBox.svelte b/src/lib/components/csvImportBox.svelte new file mode 100644 index 000000000..a74c3f1f6 --- /dev/null +++ b/src/lib/components/csvImportBox.svelte @@ -0,0 +1,241 @@ + + +{#if showCsvImportBox} + +
+
+

+ + Importing documents ({$importItems.size}) + +

+ + +
+ + {#each [...$importItems.entries()] as [key, value] (key)} +
+
    +
  • +
    +
    + + {@html text(value.status, value.collection)} + +
    +
    +
    +
    +
  • +
+
+ {/each} +
+
+{/if} + + diff --git a/src/lib/components/filePicker.svelte b/src/lib/components/filePicker.svelte index 9c16bb2e8..4b8576a50 100644 --- a/src/lib/components/filePicker.svelte +++ b/src/lib/components/filePicker.svelte @@ -1,36 +1,45 @@ -
+ - + + - - {#await buckets then response} - {#if response?.total} - {#if currentBucket} - - - {currentBucket?.name} - - {currentBucket?.$id} - - - - - - - - +
+ +
- {#if files} - {#await files} - - - Loading files... - - {:then response} - {#if response?.files?.length} - {#if view === 'grid'} - - {#each response?.files as file} - selectFile(file)} /> - {/each} - +
+ + {#if $isSmallViewport} + + {/if} + + {#await buckets then response} + {#if response?.total} + {#if currentBucket} + + {#if !$isSmallViewport} + {#key currentBucket?.$id} + + {currentBucket?.name} + + {currentBucket?.$id} + + + {/key} + {/if} + + + + {#if !$isSmallViewport} + {/if} - {#if view === 'list'} - - - - Filename - - - ID - - - Type - - - Size - - - Created - - - {#each response?.files as file} - selectFile(file)}> - -
- + + + {#if files} + {#await files} + + + Loading files... + + {:then response} + {#if response?.files?.length} + {#if view === 'grid'} + {#if $isSmallViewport} + + {#each response?.files as file} + selectFile(file)} /> + {/each} + + {:else} + + {#each response?.files as file} +
+ - {file.name} - - {file.name} - + on:click={() => + selectFile(file)} />
- - - {file.$id} - - - {file.mimeType} - - - {calculateSize(file.sizeOriginal)} - - - - - - {/each} - + {/each} +
+ {/if} + {/if} + {#if view === 'list'} + + + + Filename + + + ID + + + Type + + + Size + + + Created + + + {#each response?.files as file (file.$id)} + selectFile(file)}> + +
+ + +
+ {file.name} +
+ + + + {truncatedFilename( + file + )} + + + + {file.name} + + +
+
+ + {file.$id} + + + {file.mimeType} + + + {calculateSize(file.sizeOriginal)} + + + + +
+ {/each} +
+ {/if} + {:else if $search} + + + + {:else} + + + + + + + + {/if} - {:else if $search} - - - - {:else} - - - - {/if} - {/await} + {/await} + {/if} {/if} + {:else} + + + + + + + + + {/if} - {:else} - - - - {/if} - {/await} - + {/await} + +
+ >Select + + + diff --git a/src/lib/components/index.ts b/src/lib/components/index.ts index 16855e8bc..0e077aedb 100644 --- a/src/lib/components/index.ts +++ b/src/lib/components/index.ts @@ -83,4 +83,3 @@ export { default as Confirm } from './confirm.svelte'; export { default as UsageCard } from './usageCard.svelte'; export { default as ViewToggle } from './viewToggle.svelte'; export { default as Terminal } from './studio/terminal.svelte'; -export { default as ExpirationInput } from './expirationInput.svelte'; diff --git a/src/lib/components/menu/subMenu.svelte b/src/lib/components/menu/subMenu.svelte index 4fa080832..66186bf2a 100644 --- a/src/lib/components/menu/subMenu.svelte +++ b/src/lib/components/menu/subMenu.svelte @@ -37,7 +37,6 @@ diff --git a/src/lib/constants.ts b/src/lib/constants.ts index d5270f947..0e0d9397f 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -48,6 +48,8 @@ export enum Dependencies { PLATFORMS = 'dependency:platforms', KEY = 'dependency:key', KEYS = 'dependency:keys', + DEV_KEY = 'dependency:dev_key', + DEV_KEYS = 'dependency:dev_keys', DOMAINS = 'dependency:domains', DOMAIN = 'dependency:domains', WEBHOOK = 'dependency:webhook', diff --git a/src/lib/elements/forms/inputFilePicker.svelte b/src/lib/elements/forms/inputFilePicker.svelte index 38c90d163..c029b459a 100644 --- a/src/lib/elements/forms/inputFilePicker.svelte +++ b/src/lib/elements/forms/inputFilePicker.svelte @@ -25,7 +25,7 @@
+
+ {#if $isSmallViewport} - + {/if} {#if hasAttributes && hasValidAttributes} @@ -149,3 +202,16 @@ bind:showCreate={showCreateAttribute} bind:selectedOption={selectedAttribute} /> {/if} + +{#if showImportCSV} + + +{/if} diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/+page.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/+page.svelte index 396459809..298ea4ead 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/+page.svelte +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/+page.svelte @@ -19,7 +19,7 @@ import { isRelationship } from '../document-[document]/attributes/store'; import FailedModal from '../failedModal.svelte'; import CreateIndex from '../indexes/createIndex.svelte'; - import { attributes, type Attributes } from '../store'; + import { attributes, type Attributes, isCsvImportInProgress } from '../store'; import CreateAttributeDropdown from './createAttributeDropdown.svelte'; import Delete from './deleteAttribute.svelte'; import Edit from './edit.svelte'; @@ -32,10 +32,12 @@ IconPencil, IconPlus, IconSwitchHorizontal, - IconTrash + IconTrash, + IconViewList } from '@appwrite.io/pink-icons-svelte'; import type { ComponentProps } from 'svelte'; import { Click, trackEvent } from '$lib/actions/analytics'; + import CsvDisabled from '../csvDisabled.svelte'; const projectId = page.params.project; const databaseId = page.params.database; @@ -54,7 +56,7 @@ ip: IconLocationMarker, url: IconLink, email: IconLink, - enum: IconSwitchHorizontal + enum: IconViewList }; function getAttributeStatusBadge(status: string): ComponentProps['type'] { @@ -155,45 +157,53 @@ : '-'} - - - - { - selectedAttribute = attribute; - showEdit = true; - showDropdown[index] = false; - }}> - Update - - {#if !isRelationship(attribute)} + {#if $isCsvImportInProgress} + + + + {:else} + + + { selectedAttribute = attribute; - showCreateIndex = true; + showEdit = true; showDropdown[index] = false; }}> - Create index + Update - {/if} - {#if attribute.status !== 'processing'} - { - selectedAttribute = attribute; - showDelete = true; - showDropdown[index] = false; - trackEvent(Click.DatabaseAttributeDelete); - }}> - Delete - - {/if} - - + {#if !isRelationship(attribute)} + { + selectedAttribute = attribute; + showCreateIndex = true; + showDropdown[index] = false; + }}> + Create index + + {/if} + {#if attribute.status !== 'processing'} + { + selectedAttribute = attribute; + showDelete = true; + showDropdown[index] = false; + trackEvent(Click.DatabaseAttributeDelete); + }}> + Delete + + {/if} + + + {/if} {/each} diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/createAttributeDropdown.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/createAttributeDropdown.svelte index 650da6140..201ef960f 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/createAttributeDropdown.svelte +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/attributes/createAttributeDropdown.svelte @@ -3,28 +3,39 @@ import { ActionMenu, Icon, Popover } from '@appwrite.io/pink-svelte'; import { attributeOptions, type Option } from './store'; import { IconPlus } from '@appwrite.io/pink-icons-svelte'; + import CsvDisabled from '../csvDisabled.svelte'; + import { isCsvImportInProgress } from '../store'; export let selectedOption: Option['name'] = null; export let showCreate = false; - - - - - - {#each attributeOptions as attribute} - { - selectedOption = attribute.name; - showCreate = true; - }}> - {attribute.name} - - {/each} - - + +{:else} + + + + + + {#each attributeOptions as attribute} + { + selectedOption = attribute.name; + showCreate = true; + }}> + {attribute.name} + + {/each} + + +{/if} diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/csvDisabled.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/csvDisabled.svelte new file mode 100644 index 000000000..1ebac3183 --- /dev/null +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/csvDisabled.svelte @@ -0,0 +1,12 @@ + + + +
+ +
+ +
This action is disabled during import.
+
diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/store.ts b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/store.ts index 4408ca1ea..374adf90f 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/store.ts +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/store.ts @@ -26,3 +26,5 @@ export const attributes = derived( export const indexes = derived(page, ($page) => $page.data.collection.indexes as Models.Index[]); export const columns = writable([]); + +export const isCsvImportInProgress = writable(false); diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte index 68e5ca158..202d23389 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/subNavigation.svelte @@ -44,7 +44,7 @@ href={`${base}/project-${project}/databases/database-${databaseId}`} class="database-name u-flex u-cross-center body-text-2 u-gap-8 is-not-mobile is-selected"> - {data.database.name} + {data.database?.name}
{#if data?.allCollections?.total} diff --git a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/table.svelte b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/table.svelte index 4944beb26..3cc5262ba 100644 --- a/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/table.svelte +++ b/src/routes/(console)/project-[project]/databases/database-[database]/collection-[collection]/table.svelte @@ -3,7 +3,7 @@ import { base } from '$app/paths'; import { page } from '$app/state'; import { Submit, trackError, trackEvent } from '$lib/actions/analytics'; - import { Alert, Id, Modal } from '$lib/components'; + import { Alert, Confirm, Id } from '$lib/components'; import { Dependencies } from '$lib/constants'; import { Button as ConsoleButton, InputChoice } from '$lib/elements/forms'; import { addNotification } from '$lib/stores/notifications'; @@ -112,7 +112,6 @@ let selectedRows: string[] = []; let showDelete = false; - let deleting = false; async function handleDelete() { showDelete = false; @@ -276,11 +275,7 @@ {/if} - +
Are you sure you want to delete {selectedRows.length} {selectedRows.length > 1 ? 'documents' : 'document'}? @@ -332,12 +327,4 @@

This action is irreversible.

{/if}
- - - (showDelete = false)} disabled={deleting} - >Cancel - - Delete - - -
+ diff --git a/src/routes/(console)/project-[project]/messaging/message-[message]/pushMessage.svelte b/src/routes/(console)/project-[project]/messaging/message-[message]/pushMessage.svelte index 63c10d664..ae1438e46 100644 --- a/src/routes/(console)/project-[project]/messaging/message-[message]/pushMessage.svelte +++ b/src/routes/(console)/project-[project]/messaging/message-[message]/pushMessage.svelte @@ -89,7 +89,7 @@
- +
Message
diff --git a/src/routes/(console)/project-[project]/overview/keys/checkbox.svelte b/src/routes/(console)/project-[project]/overview/(components)/checkbox.svelte similarity index 100% rename from src/routes/(console)/project-[project]/overview/keys/checkbox.svelte rename to src/routes/(console)/project-[project]/overview/(components)/checkbox.svelte diff --git a/src/routes/(console)/project-[project]/overview/(components)/create.svelte b/src/routes/(console)/project-[project]/overview/(components)/create.svelte new file mode 100644 index 000000000..a5ccdf3db --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/create.svelte @@ -0,0 +1,101 @@ + + + + + +
+ + + + + +
+ +
+ + + Choose which permission scopes to grant your application. It is best + practice to allow only the permissions you need to meet your project goals. + + + +
+
+ + + + + + +
diff --git a/src/routes/(console)/project-[project]/overview/(components)/delete.svelte b/src/routes/(console)/project-[project]/overview/(components)/delete.svelte new file mode 100644 index 000000000..74d068bde --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/delete.svelte @@ -0,0 +1,49 @@ + + + + Are you sure you want to delete this key? + diff --git a/src/routes/(console)/project-[project]/overview/(components)/deleteBatch.svelte b/src/routes/(console)/project-[project]/overview/(components)/deleteBatch.svelte new file mode 100644 index 000000000..e7ce88b45 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/deleteBatch.svelte @@ -0,0 +1,59 @@ + + + +

+ Are you sure you want to delete {keyIds.length} + {label} key{keyIds.length > 1 ? 's' : ''}? +

+
diff --git a/src/lib/components/expirationInput.svelte b/src/routes/(console)/project-[project]/overview/(components)/expirationInput.svelte similarity index 69% rename from src/lib/components/expirationInput.svelte rename to src/routes/(console)/project-[project]/overview/(components)/expirationInput.svelte index 3e8286e04..0f92c7742 100644 --- a/src/lib/components/expirationInput.svelte +++ b/src/routes/(console)/project-[project]/overview/(components)/expirationInput.svelte @@ -19,7 +19,7 @@ return date.toISOString(); } - const options = [ + const defaultOptions = [ { label: 'Never', value: null @@ -46,13 +46,30 @@ } ]; + const limitedOptions = [ + { + label: '1 Day', + value: incrementToday(1, 'day') + }, + { + label: '7 Days', + value: incrementToday(7, 'day') + }, + { + label: '30 days', + value: incrementToday(30, 'day') + } + ]; + export let value: string | null = null; - export let dateSelectorLabel: string | undefined = undefined; - export let selectorLabel: string | undefined = 'Expiration Date'; - export let resourceType: string | 'key' | 'token' | undefined = 'key'; + export let keyType: 'api' | 'dev' = 'api'; + + const options = keyType === 'api' ? defaultOptions : limitedOptions; function initExpirationSelect() { - if (value === null || !isValidDate(value)) return null; + if (value === null || !isValidDate(value)) { + return options[0]?.value ?? null; + } let result = 'custom'; for (const option of options) { @@ -68,20 +85,27 @@ } let expirationSelect = initExpirationSelect(); let expirationCustom: string | null = value ?? null; - $: helper = - expirationSelect !== 'custom' && expirationSelect !== null - ? `Your ${resourceType} will expire in ${toLocaleDate(value)}` - : null; $: { if (!isSameDay(new Date(expirationSelect), new Date(value))) { value = expirationSelect === 'custom' ? expirationCustom : expirationSelect; } } + + $: helper = + expirationSelect !== 'custom' && expirationSelect !== null + ? `Your key will expire in ${toLocaleDate(value)}` + : undefined; - - + + {#if expirationSelect === 'custom'} - + {/if} diff --git a/src/routes/(console)/project-[project]/overview/(components)/keyDetails.svelte b/src/routes/(console)/project-[project]/overview/(components)/keyDetails.svelte new file mode 100644 index 000000000..570abf8a1 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/keyDetails.svelte @@ -0,0 +1,205 @@ + + + + {label} key - Appwrite + + + + {@const accessedAt = key.accessedAt ? toLocaleDate(key.accessedAt) : 'never'} + + Key details + + + {#each Object.entries(tiles) as [label, value]} + + + {label} + + + {#if label === 'Secret'} + + {:else} + {value} + {/if} + + + {/each} + + + + +
+ + Name + Choose any name that will help you distinguish between {label} keys. + + + + + + + + +
+ + {#if isApiKey} +
+ {@const apiKey = asApiKey(key)} + + Scopes + You can choose which permission scope to grant your application. It is a best practice + to allow only the permissions you need to meet your project goals. + + {#if scopes !== null} + + {/if} + + + + + + +
+ {/if} + + + + + Delete {label} key + The Dev key will be permanently deleted. This action is irreversible. + + +
+
+
{key.name}
+

Last accessed: {accessedAt}

+
+
+
+
+ + + + +
+
+ + diff --git a/src/routes/(console)/project-[project]/overview/(components)/table.svelte b/src/routes/(console)/project-[project]/overview/(components)/table.svelte new file mode 100644 index 000000000..e879facb8 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/table.svelte @@ -0,0 +1,127 @@ + + +{#if keys.total} + + + {#each columns as column} + {column.title} + {/each} + + {#each getKeys() as key (key.$id)} + + + {key.name} + + + {key.accessedAt ? toLocaleDate(key.accessedAt) : 'never'} + + + {@const expiration = getExpiryDetails(key)} + + {key.expire ? toLocaleDateTime(key.expire) : 'never'} + + {#if expiration.status} + + {/if} + + + {#if isApiKey} + + {asApiKey(key).scopes.length} Scopes + + {/if} + + {/each} + +{:else} + { + if (isApiKey) { + goto(`${base}/project-${page.params.project}/overview/${slug}/create`); + } else { + $showDevKeysCreateModal = true; + } + }} /> +{/if} + +{#if selectedRows.length > 0} + + + + + {capitalize(label)} + {selectedRows.length > 1 ? 'keys' : 'key'} + selected + + + + + + + +{/if} + + diff --git a/src/routes/(console)/project-[project]/overview/(components)/updateExpirationDate.svelte b/src/routes/(console)/project-[project]/overview/(components)/updateExpirationDate.svelte new file mode 100644 index 000000000..c0d21dd97 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/(components)/updateExpirationDate.svelte @@ -0,0 +1,100 @@ + + +
+ + Expiration date + Set a date after which your {label} key will expire. + + {#if isExpired} + (alertsDismissed = true)} + title="Your {label} key has expired"> + For security reasons, we recommend you delete your expired key and create a new + one. + + {:else if isExpiring} + (alertsDismissed = true)} + title="Your {label} key is about to expire"> + Update the expiration date to keep the key active + + {/if} + + + + + + + +
diff --git a/src/routes/(console)/project-[project]/overview/+layout.svelte b/src/routes/(console)/project-[project]/overview/+layout.svelte index 1d1f9e73e..69c4534ce 100644 --- a/src/routes/(console)/project-[project]/overview/+layout.svelte +++ b/src/routes/(console)/project-[project]/overview/+layout.svelte @@ -52,6 +52,10 @@ return usage.load(dates.start, dates.end, dates.period); } + function isTabSelected(key: string) { + return page.url.pathname === `${path}/${key}`; + } + $: $registerCommands([ { label: 'Add platform', @@ -67,11 +71,21 @@ label: 'Create API Key', icon: IconPlus, callback() { - goto(`${base}/project-[project]/overview/keys/create`); + goto(`${base}/project-${projectId}/overview/keys/create`); }, keys: ['c', 'k'], group: 'integrations', disabled: !$canWriteProjects + }, + { + label: 'Create Dev Key', + icon: IconPlus, + callback() { + goto(`${base}/project-${projectId}/overview/dev-keys/create`); + }, + keys: ['c', 'd', 'k'], + group: 'integrations', + disabled: !$canWriteProjects } ]); @@ -205,28 +219,31 @@ {/if} -
- - Integrations - - - Platforms - API keys - - {#if $action} - - {/if} - - + + Integrations + + + Platforms + API keys + Dev keys + + {#if $action} + + {/if} -
+ + diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/+page.svelte b/src/routes/(console)/project-[project]/overview/dev-keys/+page.svelte new file mode 100644 index 000000000..35382191b --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/+page.svelte @@ -0,0 +1,12 @@ + + + diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/+page.ts b/src/routes/(console)/project-[project]/overview/dev-keys/+page.ts new file mode 100644 index 000000000..5f7e3e623 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/+page.ts @@ -0,0 +1,13 @@ +import { Dependencies } from '$lib/constants'; +import { sdk } from '$lib/stores/sdk'; +import { selectedTab } from '../store'; +import type { PageLoad } from './$types'; + +selectedTab.set('dev-keys'); + +export const load: PageLoad = async ({ params, depends }) => { + depends(Dependencies.DEV_KEYS); + return { + devKeys: await sdk.forConsole.projects.listDevKeys(params.project) + }; +}; diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page.ts b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page.ts new file mode 100644 index 000000000..d54a142ca --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page.ts @@ -0,0 +1,22 @@ +import Header from './header.svelte'; +import Breadcrumbs from './breadcrumbs.svelte'; +import { sdk } from '$lib/stores/sdk'; +import { Dependencies } from '$lib/constants'; +import type { PageLoad } from './$types'; + +export const load: PageLoad = async ({ params, depends }) => { + depends(Dependencies.DEV_KEY); + + const key = await sdk.forConsole.projects.getDevKey(params.project, params.key); + if (key.expire) { + key.expire = new Date(key.expire).toISOString().slice(0, 23); + } else { + key.expire = undefined; + } + + return { + header: Header, + breadcrumbs: Breadcrumbs, + key + }; +}; diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page@project-[project].svelte b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page@project-[project].svelte new file mode 100644 index 000000000..9e16d83b5 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/+page@project-[project].svelte @@ -0,0 +1,6 @@ + + + diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/[key]/breadcrumbs.svelte b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/breadcrumbs.svelte new file mode 100644 index 000000000..a1a2f9f7c --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/breadcrumbs.svelte @@ -0,0 +1,28 @@ + + + diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/[key]/header.svelte b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/header.svelte new file mode 100644 index 000000000..f2a19f346 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/header.svelte @@ -0,0 +1,17 @@ + + + + + + {$devKey?.name} + + + diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/[key]/store.ts b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/store.ts new file mode 100644 index 000000000..baf2031a7 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/[key]/store.ts @@ -0,0 +1,5 @@ +import { page } from '$app/stores'; +import { derived } from 'svelte/store'; +import type { Models } from '@appwrite.io/console'; + +export const devKey = derived(page, ($page) => $page.data.key as Models.DevKey); diff --git a/src/routes/(console)/project-[project]/overview/dev-keys/action.svelte b/src/routes/(console)/project-[project]/overview/dev-keys/action.svelte new file mode 100644 index 000000000..306188b52 --- /dev/null +++ b/src/routes/(console)/project-[project]/overview/dev-keys/action.svelte @@ -0,0 +1,80 @@ + + +{#if $canWriteKeys} + + +
+ + + Test your app without rate limits and more detailed error messages. + + + + + + + + + + + + + + +{/if} diff --git a/src/routes/(console)/project-[project]/overview/keys/+page.svelte b/src/routes/(console)/project-[project]/overview/keys/+page.svelte index 561364ae7..f9f98b736 100644 --- a/src/routes/(console)/project-[project]/overview/keys/+page.svelte +++ b/src/routes/(console)/project-[project]/overview/keys/+page.svelte @@ -1,50 +1,12 @@ -{#if data.keys.total} - - - Name - Last accessed - Expiration date - Scopes - - {#each data.keys.keys as key} - - - {key.name} - - - {key.accessedAt ? toLocaleDate(key.accessedAt) : 'never'} - - - {key.expire ? toLocaleDateTime(key.expire) : 'never'} - - - {key.scopes.length} Scopes - - - {/each} - -{:else} - goto(`${base}/project-${page.params.project}/overview/keys/create`)} /> -{/if} +
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 b9dbf675e..82d29d955 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 @@ -1,165 +1,6 @@ - - API key - Appwrite - - - - {@const accessedAt = $key.accessedAt ? toLocaleDate($key.accessedAt) : 'never'} - - {$key.name} - -

- Last accessed: {accessedAt}
- Scopes granted: {$key.scopes.length} -

-
-
- - - API key secret - - - - - -
- - Name - Choose any name that will help you distinguish between API keys. - - - - - - - - - -
- - Scopes - You can choose which permission scope to grant your application. It is a best practice to - allow only the permissions you need to meet your project goals. - - {#if scopes !== null} - - {/if} - - - - - - - - - - - - Delete API key - The API key will be permanently deleted. This action is irreversible. - - -
-
-
{$key.name}
-

Last accessed: {accessedAt}

-
-
-
-
- - - - -
-
- - + diff --git a/src/routes/(console)/project-[project]/overview/keys/[key]/delete.svelte b/src/routes/(console)/project-[project]/overview/keys/[key]/delete.svelte deleted file mode 100644 index 5968bd499..000000000 --- a/src/routes/(console)/project-[project]/overview/keys/[key]/delete.svelte +++ /dev/null @@ -1,35 +0,0 @@ - - - - Are you sure you want to delete this execution? - diff --git a/src/routes/(console)/project-[project]/overview/keys/[key]/header.svelte b/src/routes/(console)/project-[project]/overview/keys/[key]/header.svelte index 9bd9f3eb0..0d7411d60 100644 --- a/src/routes/(console)/project-[project]/overview/keys/[key]/header.svelte +++ b/src/routes/(console)/project-[project]/overview/keys/[key]/header.svelte @@ -13,6 +13,5 @@ {$key?.name} - API key secret diff --git a/src/routes/(console)/project-[project]/overview/keys/[key]/store.ts b/src/routes/(console)/project-[project]/overview/keys/[key]/store.ts index dc984c86c..5a1a9f936 100644 --- a/src/routes/(console)/project-[project]/overview/keys/[key]/store.ts +++ b/src/routes/(console)/project-[project]/overview/keys/[key]/store.ts @@ -1,5 +1,5 @@ -import { derived } from 'svelte/store'; import { page } from '$app/stores'; +import { derived } from 'svelte/store'; import type { Models } from '@appwrite.io/console'; export const key = derived(page, ($page) => $page.data.key as Models.Key); diff --git a/src/routes/(console)/project-[project]/overview/keys/[key]/updateExpirationDate.svelte b/src/routes/(console)/project-[project]/overview/keys/[key]/updateExpirationDate.svelte index 8a15fa347..e69de29bb 100644 --- a/src/routes/(console)/project-[project]/overview/keys/[key]/updateExpirationDate.svelte +++ b/src/routes/(console)/project-[project]/overview/keys/[key]/updateExpirationDate.svelte @@ -1,75 +0,0 @@ - - -
- - Expiration date - Set a date after which your API key will expire. - - {#if isExpired} - (alertsDismissed = true)} - title="Your API key has expired"> - For security reasons, we recommend you delete your expired key and create a new - one. - - {:else if isExpiring} - (alertsDismissed = true)} - title="Your API key is about to expire"> - Update the expiration date to keep the key active - - {/if} - - - - - - - - diff --git a/src/routes/(console)/project-[project]/overview/keys/create/+page.svelte b/src/routes/(console)/project-[project]/overview/keys/create/+page.svelte index 68f04d424..dd2233840 100644 --- a/src/routes/(console)/project-[project]/overview/keys/create/+page.svelte +++ b/src/routes/(console)/project-[project]/overview/keys/create/+page.svelte @@ -1,98 +1,5 @@ - -
- -
- - - - -
- -
- - - Choose which permission scopes to grant your application. It is best - practice to allow only the permissions you need to meet your project goals. - - - -
-
- - - - - - -
+ diff --git a/src/routes/(console)/project-[project]/overview/keys/scopes.svelte b/src/routes/(console)/project-[project]/overview/keys/scopes.svelte index 9c4f39f0a..d3a7601e6 100644 --- a/src/routes/(console)/project-[project]/overview/keys/scopes.svelte +++ b/src/routes/(console)/project-[project]/overview/keys/scopes.svelte @@ -17,6 +17,16 @@ Other = 'Other' } + const categories = [ + Category.Auth, + Category.Database, + Category.Functions, + Category.Storage, + Category.Messaging, + Category.Sites, + Category.Other + ]; + let mounted = false; onMount(() => { @@ -77,8 +87,6 @@ } } } - - $: console.log(scopes); @@ -92,14 +100,16 @@ - {#each [Category.Auth, Category.Database, Category.Functions, Category.Storage, Category.Messaging, Category.Sites, Category.Other] as category} + {#each categories as category, index} {@const checked = categoryState(category, scopes)} + {@const isLastItem = index === categories.length - 1} {@const scopesLength = allScopes.filter( (n) => n.category === category && scopes.includes(n.scope) ).length} onCategoryChange(event, category)}> diff --git a/src/routes/(console)/project-[project]/overview/store.ts b/src/routes/(console)/project-[project]/overview/store.ts index 2c18cefa9..3adf1b9fc 100644 --- a/src/routes/(console)/project-[project]/overview/store.ts +++ b/src/routes/(console)/project-[project]/overview/store.ts @@ -1,7 +1,8 @@ import { sdk } from '$lib/stores/sdk'; import { cachedStore } from '$lib/helpers/cache'; -import { writable, type Writable } from 'svelte/store'; +import { get, readable, writable, type Writable } from 'svelte/store'; import type { Models, ProjectUsageRange } from '@appwrite.io/console'; +import type { Column } from '$lib/helpers/types'; export const usage = cachedStore< Models.UsageProject, @@ -17,4 +18,17 @@ export const usage = cachedStore< }; }); -export const selectedTab: Writable<'platforms' | 'keys'> = writable('platforms'); +export const selectedTab: Writable<'platforms' | 'keys' | 'dev-keys'> = writable('platforms'); + +export const showDevKeysCreateModal: Writable = writable(false); + +export const devKeyColumns = readable([ + { id: 'name', title: 'Name', type: 'string', width: { min: 120 } }, + { id: 'last_accessed', title: 'Last accessed', type: 'datetime', width: { min: 120 } }, + { id: 'expiration', title: 'Expiration date', type: 'datetime', width: { min: 120 } } +]); + +export const keyColumns = readable([ + ...get(devKeyColumns), + { id: 'scopes', title: 'Scopes', type: 'string', width: { min: 120 } } +]); diff --git a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/+page.svelte b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/+page.svelte index 3d01dbe24..298c6ce92 100644 --- a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/+page.svelte +++ b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/+page.svelte @@ -49,7 +49,7 @@ try { await sdk.forProject.storage.deleteFile(file.bucketId, file.$id); await invalidate(Dependencies.FILES); - uploader.removeFile(file); + await uploader.removeFile(file); trackEvent(Submit.FileDelete); } catch (error) { addNotification({ @@ -218,11 +218,11 @@ + on:click={() => { goto( `${base}/project-${page.params.project}/storage/bucket-${page.params.bucket}/create` - )} /> + ); + }} /> {/if} diff --git a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/file-[file]/deleteFile.svelte b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/file-[file]/deleteFile.svelte index a21f60728..c0e94a106 100644 --- a/src/routes/(console)/project-[project]/storage/bucket-[bucket]/file-[file]/deleteFile.svelte +++ b/src/routes/(console)/project-[project]/storage/bucket-[bucket]/file-[file]/deleteFile.svelte @@ -15,19 +15,20 @@ try { const fileName = $file.name; await sdk.forProject.storage.deleteFile($file.bucketId, $file.$id); - showDelete = false; - await goto(`${base}/project-${page.params.project}/storage/bucket-${$file.bucketId}`); addNotification({ type: 'success', message: `${fileName} has been deleted` }); trackEvent(Submit.FileDelete); + await goto(`${base}/project-${page.params.project}/storage/bucket-${$file.bucketId}`); } catch (error) { addNotification({ type: 'error', message: error.message }); trackError(error, Submit.FileDelete); + } finally { + showDelete = false; } }; diff --git a/src/routes/(public)/(guest)/login/+page.svelte b/src/routes/(public)/(guest)/login/+page.svelte index 9ad34fc01..e72b9cd1a 100644 --- a/src/routes/(public)/(guest)/login/+page.svelte +++ b/src/routes/(public)/(guest)/login/+page.svelte @@ -186,11 +186,11 @@