mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge pull request #2946 from appwrite/codex/render-deprecated-api-key-scopes
[codex] Render deprecated API key scopes
This commit is contained in:
+18
-9
@@ -155,12 +155,15 @@ export const defaultRoles: string[] = ['owner'];
|
||||
|
||||
// these are kept for backwards compatibility with keys and events that already exists.
|
||||
// for the new ones, we use the new terminology.
|
||||
export const scopes: {
|
||||
export type ScopeDefinition = {
|
||||
scope: string;
|
||||
description: string;
|
||||
category: string;
|
||||
icon: string;
|
||||
}[] = [
|
||||
deprecated?: boolean;
|
||||
};
|
||||
|
||||
export const scopes: ScopeDefinition[] = [
|
||||
{
|
||||
scope: 'sessions.write',
|
||||
description: "Access to create, update and delete your project's sessions",
|
||||
@@ -207,13 +210,15 @@ export const scopes: {
|
||||
scope: 'collections.read',
|
||||
description: "Access to read your project's database collections",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'collections.write',
|
||||
description: "Access to create, update, and delete your project's database collections",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'tables.read',
|
||||
@@ -231,14 +236,16 @@ export const scopes: {
|
||||
scope: 'attributes.read',
|
||||
description: "Access to read your project's database collection's attributes",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'attributes.write',
|
||||
description:
|
||||
"Access to create, update, and delete your project's database collection's attributes",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'columns.read',
|
||||
@@ -268,13 +275,15 @@ export const scopes: {
|
||||
scope: 'documents.read',
|
||||
description: "Access to read your project's database documents",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'documents.write',
|
||||
description: "Access to create, update, and delete your project's database documents",
|
||||
category: 'Database',
|
||||
icon: 'database'
|
||||
icon: 'database',
|
||||
deprecated: true
|
||||
},
|
||||
{
|
||||
scope: 'rows.read',
|
||||
@@ -466,7 +475,7 @@ export const scopes: {
|
||||
}
|
||||
];
|
||||
|
||||
export const cloudOnlyBackupScopes = [
|
||||
export const cloudOnlyBackupScopes: ScopeDefinition[] = [
|
||||
{
|
||||
scope: 'policies.read',
|
||||
description: 'Access to read your database backup policies',
|
||||
|
||||
+1
-5
@@ -16,7 +16,6 @@
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import Scopes from '../api-keys/scopes.svelte';
|
||||
import { InteractiveText, Layout, Typography } from '@appwrite.io/pink-svelte';
|
||||
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
|
||||
|
||||
export let key: Models.DevKey | Models.Key;
|
||||
export let keyType: 'api' | 'dev' = 'api';
|
||||
@@ -163,8 +162,6 @@
|
||||
{#if isApiKey}
|
||||
<Form onSubmit={updateScopes}>
|
||||
{@const apiKey = asApiKey(key)}
|
||||
{@const apiKeyCorrectScopes = getEffectiveScopes(apiKey.scopes)}
|
||||
{@const currentEffective = scopes ? getEffectiveScopes(scopes) : null}
|
||||
<CardGrid>
|
||||
<svelte:fragment slot="title">Scopes</svelte:fragment>
|
||||
You can choose which permission scope to grant your application. It is a best practice
|
||||
@@ -178,8 +175,7 @@
|
||||
<svelte:fragment slot="actions">
|
||||
<Button
|
||||
submit
|
||||
disabled={scopes &&
|
||||
!symmetricDifference(currentEffective, apiKeyCorrectScopes).length}
|
||||
disabled={scopes && !symmetricDifference(scopes, apiKey.scopes).length}
|
||||
>Update</Button>
|
||||
</svelte:fragment>
|
||||
</CardGrid>
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
import { Badge, Layout, Table } from '@appwrite.io/pink-svelte';
|
||||
import DeleteBatch from './deleteBatch.svelte';
|
||||
import { capitalize } from '$lib/helpers/string';
|
||||
import { getEffectiveScopes } from '../api-keys/scopes.svelte';
|
||||
|
||||
let {
|
||||
keyType = 'api',
|
||||
@@ -31,7 +30,7 @@
|
||||
|
||||
function getApiKeyScopeCount(key: Models.Key | Models.DevKey) {
|
||||
const apiKey = key as Models.Key;
|
||||
return getEffectiveScopes(apiKey.scopes).length;
|
||||
return apiKey.scopes.length;
|
||||
}
|
||||
|
||||
function getExpiryDetails(key: Models.Key | Models.DevKey): {
|
||||
|
||||
@@ -31,29 +31,21 @@
|
||||
import { Button } from '$lib/elements/forms';
|
||||
import { symmetricDifference } from '$lib/helpers/array';
|
||||
import { scopes as allScopes, cloudOnlyBackupScopes } from '$lib/constants';
|
||||
import { Accordion, Divider, Layout, Selector } from '@appwrite.io/pink-svelte';
|
||||
import { Accordion, Badge, Divider, Layout, Selector } from '@appwrite.io/pink-svelte';
|
||||
import type { Scopes } from '@appwrite.io/console';
|
||||
|
||||
export let scopes: Scopes[];
|
||||
|
||||
const baseFilteredScopes = allScopes.filter((scope) => {
|
||||
const val = scope.scope;
|
||||
if (!val) return false;
|
||||
|
||||
const legacyPrefixes = ['collections.', 'attributes.', 'documents.'];
|
||||
return !legacyPrefixes.some((prefix) => val.startsWith(prefix));
|
||||
});
|
||||
|
||||
// insert cloud-only scopes right after databases.write
|
||||
const databasesWriteIndex = baseFilteredScopes.findIndex((s) => s.scope === 'databases.write');
|
||||
const databasesWriteIndex = allScopes.findIndex((s) => s.scope === 'databases.write');
|
||||
const filteredScopes =
|
||||
isCloud && databasesWriteIndex !== -1
|
||||
? [
|
||||
...baseFilteredScopes.slice(0, databasesWriteIndex + 1),
|
||||
...allScopes.slice(0, databasesWriteIndex + 1),
|
||||
...cloudOnlyBackupScopes,
|
||||
...baseFilteredScopes.slice(databasesWriteIndex + 1)
|
||||
...allScopes.slice(databasesWriteIndex + 1)
|
||||
]
|
||||
: baseFilteredScopes;
|
||||
: allScopes;
|
||||
|
||||
// include all scopes
|
||||
const scopeCatalog = new Set([
|
||||
@@ -90,9 +82,8 @@
|
||||
|
||||
onMount(() => {
|
||||
scopes.forEach((scope) => {
|
||||
const newerScope = toNewerScope(scope);
|
||||
if (newerScope in activeScopes) {
|
||||
activeScopes[newerScope] = true;
|
||||
if (scope in activeScopes) {
|
||||
activeScopes[scope] = true;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -111,36 +102,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
function toNewerScope(scope: string): string {
|
||||
for (const pair of compatPairs) {
|
||||
if (scope.startsWith(pair.legacy)) {
|
||||
return scope.replace(pair.legacy, pair.newer);
|
||||
}
|
||||
}
|
||||
return scope;
|
||||
}
|
||||
|
||||
function getAllScopeVariants(scope: string): string[] {
|
||||
const variants = new Set([scope]);
|
||||
|
||||
for (const pair of compatPairs) {
|
||||
if (scope.startsWith(pair.newer)) {
|
||||
variants.add(scope.replace(pair.newer, pair.legacy));
|
||||
} else if (scope.startsWith(pair.legacy)) {
|
||||
variants.add(scope.replace(pair.legacy, pair.newer));
|
||||
}
|
||||
}
|
||||
|
||||
return Array.from(variants);
|
||||
}
|
||||
|
||||
function categoryState(category: string, s: string[]): boolean | 'indeterminate' {
|
||||
const scopesByCategory = filteredScopes.filter((n) => n.category === category);
|
||||
|
||||
const activeInCategory = scopesByCategory.filter((scopeItem) => {
|
||||
const newerScope = scopeItem.scope;
|
||||
return s.some((scope) => toNewerScope(scope) === newerScope);
|
||||
});
|
||||
const activeInCategory = scopesByCategory.filter((scopeItem) =>
|
||||
s.includes(scopeItem.scope as Scopes)
|
||||
);
|
||||
|
||||
if (activeInCategory.length === 0) {
|
||||
return false;
|
||||
@@ -154,27 +120,16 @@
|
||||
function onCategoryChange(event: CustomEvent<boolean | 'indeterminate'>, category: Category) {
|
||||
if (event.detail === 'indeterminate') return;
|
||||
filteredScopes.forEach((s) => {
|
||||
if (s.category === category) {
|
||||
if (s.category === category && !s.deprecated) {
|
||||
activeScopes[s.scope] = event.detail;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): Scopes[] {
|
||||
const result = new Set<string>();
|
||||
|
||||
Object.entries(activeScopesObj).forEach(([scope, isActive]) => {
|
||||
if (isActive) {
|
||||
const variants = getAllScopeVariants(scope);
|
||||
variants.forEach((variant) => {
|
||||
if (scopeCatalog.has(variant)) {
|
||||
result.add(variant);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return Array.from(result) as Scopes[];
|
||||
return Object.entries(activeScopesObj)
|
||||
.filter(([scope, isActive]) => isActive && scopeCatalog.has(scope))
|
||||
.map(([scope]) => scope as Scopes);
|
||||
}
|
||||
|
||||
$: {
|
||||
@@ -203,9 +158,7 @@
|
||||
{@const checked = categoryState(category, scopes)}
|
||||
{@const isLastItem = index === categories.length - 1}
|
||||
{@const scopesLength = filteredScopes.filter(
|
||||
(n) =>
|
||||
n.category === category &&
|
||||
scopes.some((scope) => toNewerScope(scope) === n.scope)
|
||||
(n) => n.category === category && scopes.includes(n.scope as Scopes)
|
||||
).length}
|
||||
<Accordion
|
||||
selectable
|
||||
@@ -216,12 +169,17 @@
|
||||
on:change={(event) => onCategoryChange(event, category)}>
|
||||
<Layout.Stack>
|
||||
{#each filteredScopes.filter((s) => s.category === category) as scope}
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id={scope.scope}
|
||||
label={scope.scope}
|
||||
description={scope.description}
|
||||
bind:checked={activeScopes[scope.scope]} />
|
||||
<Layout.Stack direction="row" alignItems="center" gap="s">
|
||||
<Selector.Checkbox
|
||||
size="s"
|
||||
id={scope.scope}
|
||||
label={`${scope.scope}${scope.deprecated ? ' (Deprecated)' : ''}`}
|
||||
description={scope.description}
|
||||
bind:checked={activeScopes[scope.scope]} />
|
||||
{#if scope.deprecated}
|
||||
<Badge size="xs" variant="secondary" content="Deprecated" />
|
||||
{/if}
|
||||
</Layout.Stack>
|
||||
{/each}
|
||||
</Layout.Stack>
|
||||
</Accordion>
|
||||
|
||||
Reference in New Issue
Block a user