Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat: Dev keys #1544

Closed
wants to merge 13 commits into from
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
"e2e:ui": "playwright test tests/e2e --ui"
},
"dependencies": {
"@appwrite.io/console": "1.5.2",
"@appwrite.io/console": "https://pkg.pr.new/appwrite-labs/cloud/@appwrite.io/console@1641",
"@appwrite.io/pink": "0.25.0",
"@appwrite.io/pink-icons": "0.25.0",
"@popperjs/core": "^2.11.8",
Expand Down
11 changes: 6 additions & 5 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions src/lib/actions/analytics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,10 @@ export enum Submit {
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',
Expand Down
2 changes: 2 additions & 0 deletions src/lib/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,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',
WEBHOOK = 'dependency:webhook',
WEBHOOKS = 'dependency:webhooks',
Expand Down
15 changes: 15 additions & 0 deletions src/routes/(console)/project-[project]/overview/+layout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import type { Metric } from '$lib/sdk/usage';
import { periodToDates } from '$lib/layout/usage.svelte';
import { canWriteProjects } from '$lib/stores/roles';
import { createDevKey } from '$routes/(console)/project-[project]/overview/dev-keys/+page.svelte';

$: projectId = $page.params.project;
$: path = `${base}/project-${projectId}/overview`;
Expand Down Expand Up @@ -69,6 +70,16 @@
keys: ['c', 'k'],
group: 'integrations',
disabled: !$canWriteProjects
},
{
label: 'Create Dev Key',
icon: 'plus',
callback() {
createDevKey();
},
keys: ['c', 'd', 'k'],
group: 'integrations',
disabled: !$canWriteProjects
}
]);

Expand Down Expand Up @@ -227,6 +238,10 @@
href={`${path}/keys`}
selected={$page.url.pathname === `${path}/keys`}
event="keys">API keys</Tab>
<Tab
href={`${path}/dev-keys`}
selected={$page.url.pathname === `${path}/dev-keys`}
event="keys">Dev keys</Tab>
</ul>
</div>

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<script lang="ts">
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Modal } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { type Models } from '@appwrite.io/console';
import { project } from '../../store';

export let showDelete = false;
export let key: Models.Key | Models.DevKey;
export let resourceType: 'API' | 'Dev' = 'API';

$: isTypeApiKey = resourceType === 'API';

async function handleDelete() {
try {
const actions = {
delete: isTypeApiKey
? () => sdk.forConsole.projects.deleteKey($project.$id, key.$id)
: () => sdk.forConsole.projects.deleteDevKey($project.$id, key.$id),
invalidate: isTypeApiKey ? Dependencies.KEYS : Dependencies.DEV_KEYS,
trackDelete: isTypeApiKey ? Submit.KeyDelete : Submit.DevKeyDelete,
redirectPath: `${base}/project-${$project.$id}/overview/${isTypeApiKey ? 'keys' : 'dev-keys'}`
};

await actions.delete();
await invalidate(actions.invalidate);

showDelete = false;

addNotification({
type: 'success',
message: `${key.name} has been deleted`
});

trackEvent(actions.trackDelete);
await goto(actions.redirectPath);
} catch (error) {
addNotification({
type: 'error',
message: error.message
});

trackError(error, isTypeApiKey ? Submit.KeyDelete : Submit.DevKeyDelete);
}
}
</script>

<Modal
title="Delete {resourceType} key"
bind:show={showDelete}
onSubmit={handleDelete}
icon="exclamation"
state="warning"
headerDivider={false}>
<p>The {resourceType} key will be permanently deleted. This action is irreversible.</p>

<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
<Button secondary submit>Delete</Button>
</svelte:fragment>
</Modal>
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
return date.toISOString();
}

const options = [
const defaultOptions = [
{
label: 'Never',
value: null
Expand All @@ -46,10 +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 resourceType: 'API' | 'Dev' = 'API';

const options = resourceType === '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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<script lang="ts">
import { Alert, CardGrid, Heading } from '$lib/components';
import { Button, Form, FormList } from '$lib/elements/forms';
import { diffDays } from '$lib/helpers/date';
import ExpirationInput from './expirationInput.svelte';
import type { Models } from '@appwrite.io/console';

export let key: Models.Key | Models.DevKey;
export let resourceType: 'API' | 'Dev' = 'API';
export let onSubmit: (e: SubmitEvent) => Promise<void> | void;

let expiration = key.expire;

let alertsDismissed = false;
$: isExpiring =
!alertsDismissed && key.expire && diffDays(new Date(), new Date(key.expire)) < 14;
$: isExpired = !alertsDismissed && key.expire !== null && new Date(key.expire) < new Date();
</script>

<Form {onSubmit}>
<CardGrid>
<Heading tag="h6" size="7">Expiration date</Heading>
<p class="text">Set a date after which your {resourceType} key will expire.</p>
<svelte:fragment slot="aside">
{#if isExpired}
<Alert type="error" dismissible on:dismiss={() => (alertsDismissed = true)}>
<span slot="title">Your {resourceType} key has expired</span>
<p>
For security reasons, we recommend you delete your expired key and create a
new one.
</p>
</Alert>
{:else if isExpiring}
<Alert type="warning" dismissible on:dismiss={() => (alertsDismissed = true)}>
<span slot="title">Your {resourceType} key is about to expire</span>
<p>Update the expiration date to keep the key active</p>
</Alert>
{/if}
<FormList>
<ExpirationInput bind:value={key.expire} {resourceType} />
</FormList>
</svelte:fragment>

<svelte:fragment slot="actions">
<Button disabled={expiration === key.expire} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<script lang="ts" context="module">
export function createDevKey() {
if (!get(canWriteKeys)) {
return;
}
wizard.start(Wizard);
}
</script>

<script lang="ts">
import { Empty, Heading } from '$lib/components';
import { Button } from '$lib/elements/forms';
import {
Table,
TableBody,
TableCellHead,
TableCellText,
TableHeader,
TableRowLink
} from '$lib/elements/table';
import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date';
import { canWriteKeys } from '$lib/stores/roles';
import { wizard } from '$lib/stores/wizard';
import { get } from 'svelte/store';
import type { PageData } from './$types';
import Wizard from './wizard.svelte';

export let data: PageData;
</script>

<div class="common-section u-flex u-gap-12">
<Heading tag="h3" size="7">Dev keys</Heading>
{#if $canWriteKeys}
<span class="u-margin-inline-start-auto">
<Button on:click={createDevKey}>
<span class="icon-plus" aria-hidden="true" />
<span class="text"> Create Dev key </span>
</Button>
</span>
{/if}
</div>

{#if data.devKeys.total}
<Table>
<TableHeader>
<TableCellHead>Name</TableCellHead>
<TableCellHead onlyDesktop>last accessed</TableCellHead>
<TableCellHead onlyDesktop>expiration date</TableCellHead>
</TableHeader>
<TableBody>
{#each data.devKeys.keys as key}
<TableRowLink href={`dev-keys/${key.$id}`}>
<TableCellText title="Name">
{key.name}
</TableCellText>
<TableCellText onlyDesktop title="Last Accessed">
{key.accessedAt ? toLocaleDate(key.accessedAt) : 'never'}
</TableCellText>
<TableCellText onlyDesktop title="Expiration Date">
{key.expire ? toLocaleDateTime(key.expire) : 'never'}
</TableCellText>
</TableRowLink>
{/each}
</TableBody>
</Table>
{:else}
<!-- TODO: create correct links method -->
<Empty
single
allowCreate={$canWriteKeys}
href="https://appwrite.io/docs/advanced/platform/dev-keys"
target="Dev key"
on:click={createDevKey} />
{/if}
13 changes: 13 additions & 0 deletions src/routes/(console)/project-[project]/overview/dev-keys/+page.ts
Original file line number Diff line number Diff line change
@@ -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)
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
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 devKey = await sdk.forConsole.projects.getDevKey(params.project, params.key);

if (devKey.expire) {
devKey.expire = new Date(devKey.expire).toISOString().slice(0, 23);
} else {
devKey.expire = undefined;
}

return {
devKey,
header: Header,
breadcrumbs: Breadcrumbs
};
};
Loading