Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
Tooltip,
Typography
} from '@appwrite.io/pink-svelte';
import { isRelationship, isSpatialType, isString } from '../rows/store';
import { isRelationship, isSpatialType, isTextType } from '../rows/store';
import {
columns,
type Columns,
Expand Down Expand Up @@ -443,7 +443,7 @@
{/if}
</Typography.Text>

{#if isString(column) && column.encrypt}
{#if isTextType(column) && 'encrypt' in column && column.encrypt}
<Tooltip portal>
<Icon
size="s"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<script lang="ts">
import { page } from '$app/state';
import { isCloud } from '$lib/system';
import { getChangePlanUrl } from '$lib/stores/billing';
import { currentPlan } from '$lib/stores/organization';
import {
ActionMenu,
Popover,
Layout,
Selector,
Tag,
Tooltip,
Typography,
Link
} from '@appwrite.io/pink-svelte';

let {
encrypt = $bindable(false),
editing = false,
disabled = false,
id = 'encrypt'
}: {
encrypt?: boolean;
editing?: boolean;
disabled?: boolean;
id?: string;
} = $props();

const organizationId = page.data?.organization?.$id ?? page.data?.project?.$id;
const supportsEncryption = isCloud ? $currentPlan?.databasesAllowEncrypt : true;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, let's examine the file mentioned in the review
fd encryptCheckbox.svelte --type f

Repository: appwrite/console

Length of output: 182


🏁 Script executed:

# Search for getChangePlanUrl usage to understand what ID it expects
rg "getChangePlanUrl" --type=svelte -B 3 -A 3

Repository: appwrite/console

Length of output: 90


🏁 Script executed:

# Search for project.teamId usage pattern in the codebase
rg "project\?\.teamId" --type=svelte -B 2 -A 2

Repository: appwrite/console

Length of output: 90


🏁 Script executed:

# Search for how organizationId is typically derived from page.data
rg "page\.data\?\.project\?" --type=svelte -B 1 -A 1 | head -50

Repository: appwrite/console

Length of output: 90


🏁 Script executed:

# Search for getChangePlanUrl without file type restriction
rg "getChangePlanUrl" -B 3 -A 3

Repository: appwrite/console

Length of output: 50372


🏁 Script executed:

# Search for project.teamId pattern
rg "project\.teamId" -B 2 -A 2

Repository: appwrite/console

Length of output: 20752


🏁 Script executed:

# Read the encryptCheckbox.svelte file
cat -n "src/routes/(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/encryptCheckbox.svelte"

Repository: appwrite/console

Length of output: 4540


🏁 Script executed:

# Search for organizationId patterns in similar context
rg "organizationId.*project.*\$id" -B 1 -A 1

Repository: appwrite/console

Length of output: 42


Fix organizationId fallback and make encryption support reactive.

Line 29 uses page.data?.project?.$id as the fallback for organizationId, but $id is the project ID. It should be page.data?.project?.teamId to get the organization ID, matching the pattern used elsewhere in the codebase.

Line 30's supportsEncryption is a const that reads $currentPlan, which may load asynchronously. It won't update after initialization. Use $derived to make it reactive.

Proposed fix
-    const organizationId = page.data?.organization?.$id ?? page.data?.project?.$id;
-    const supportsEncryption = isCloud ? $currentPlan?.databasesAllowEncrypt : true;
+    const organizationId = page.data?.organization?.$id ?? page.data?.project?.teamId;
+    const supportsEncryption = $derived(isCloud ? $currentPlan?.databasesAllowEncrypt : true);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const organizationId = page.data?.organization?.$id ?? page.data?.project?.$id;
const supportsEncryption = isCloud ? $currentPlan?.databasesAllowEncrypt : true;
const organizationId = page.data?.organization?.$id ?? page.data?.project?.teamId;
const supportsEncryption = $derived(isCloud ? $currentPlan?.databasesAllowEncrypt : true);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@src/routes/`(console)/project-[region]-[project]/databases/database-[database]/table-[table]/columns/encryptCheckbox.svelte
around lines 29 - 30, Replace the incorrect fallback for organizationId so it
uses the project's teamId (page.data?.project?.teamId) instead of
page.data?.project?.$id, and make supportsEncryption reactive by deriving it
from the isCloud flag and the currentPlan store (use Svelte's derived store)
rather than a const so it updates when $currentPlan loads; update references to
the symbols organizationId and supportsEncryption and import/use derived from
'svelte/store' to compute supportsEncryption from isCloud and currentPlan.

</script>

<Tooltip disabled={!(editing || disabled)} maxWidth="275px" placement="bottom-start">
<div
class="popover-holder"
class:cursor-not-allowed={editing || disabled}
class:disabled-checkbox={!supportsEncryption || editing || disabled}>
<Layout.Stack inline gap="s" alignItems="flex-start" direction="row">
<Popover let:toggle placement="bottom-start">
<Selector.Checkbox
size="s"
{id}
bind:checked={encrypt}
disabled={!supportsEncryption || editing || disabled} />

<Layout.Stack gap="xxs" direction="column">
<button
type="button"
disabled={editing || disabled}
class:cursor-pointer={!editing}
class:cursor-not-allowed={editing || disabled}
Comment thread
HarshMN2345 marked this conversation as resolved.
Outdated
on:click={(e) => {
if (!supportsEncryption) {
toggle(e);
} else {
encrypt = !encrypt;
}
}}>
<Layout.Stack inline gap="xxs" direction="row" alignItems="center">
<Typography.Text variant="m-500">Encrypted</Typography.Text>
{#if !supportsEncryption}
<Tag variant="default" size="xs" on:click={toggle}>Pro</Tag>
{/if}
</Layout.Stack>
</button>
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<Typography.Text color="--fgcolor-neutral-tertiary">
Protect column against data leaks for best privacy compliance. Encrypted
columns cannot be queried.
</Typography.Text>
</Layout.Stack>

<ActionMenu.Root width="180px" slot="tooltip">
<Typography.Text variant="m-500">
Available on Pro plan. <Link.Anchor href={getChangePlanUrl(organizationId)}
>Upgrade</Link.Anchor>
to enable encrypted columns.
</Typography.Text>
</ActionMenu.Root>
</Popover>
</Layout.Stack>
</div>

<svelte:fragment slot="tooltip">
Encryption can only be set when creating the column.
</svelte:fragment>
</Tooltip>

<style lang="scss">
.popover-holder {
& :global([role='tooltip']) {
margin-top: 4px;
left: 8rem !important;
}

&.disabled-checkbox :global(button) {
cursor: unset;
}
}

.cursor-pointer {
cursor: pointer !important;
}

.cursor-not-allowed {
cursor: not-allowed;
}
</style>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
key,
required: data.required,
xdefault: data.default,
array: data.array
array: data.array,
encrypt: data.encrypt
});
}
export async function updateLongtext(
Expand All @@ -42,17 +43,21 @@
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
import EncryptCheckbox from './encryptCheckbox.svelte';
import { InputTextarea } from '$lib/elements/forms';
import { Layout, Typography } from '@appwrite.io/pink-svelte';

export let data: Partial<Models.ColumnLongtext> = {
required: false,
array: false
array: false,
encrypt: false
};

export let editing = false;
export let disabled = false;

if (data && (data.encrypt === undefined || data.encrypt === null)) data.encrypt = false;

let savedDefault = data.default;

function handleDefaultState(hideDefault: boolean) {
Expand Down Expand Up @@ -97,3 +102,7 @@
{disabled}
bind:array={data.array}
bind:required={data.required} />

<Layout.Stack gap="xs" direction="column">
<EncryptCheckbox id="encrypt-longtext" bind:encrypt={data.encrypt} {editing} {disabled} />
</Layout.Stack>
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@
key,
required: data.required,
xdefault: data.default,
array: data.array
array: data.array,
encrypt: data.encrypt
});
}
export async function updateMediumtext(
Expand All @@ -42,17 +43,21 @@
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
import EncryptCheckbox from './encryptCheckbox.svelte';
import { InputTextarea } from '$lib/elements/forms';
import { Layout, Typography } from '@appwrite.io/pink-svelte';

export let data: Partial<Models.ColumnMediumtext> = {
required: false,
array: false
array: false,
encrypt: false
};

export let editing = false;
export let disabled = false;

if (data && (data.encrypt === undefined || data.encrypt === null)) data.encrypt = false;

let savedDefault = data.default;

function handleDefaultState(hideDefault: boolean) {
Expand Down Expand Up @@ -97,3 +102,7 @@
{disabled}
bind:array={data.array}
bind:required={data.required} />

<Layout.Stack gap="xs" direction="column">
<EncryptCheckbox id="encrypt-mediumtext" bind:encrypt={data.encrypt} {editing} {disabled} />
</Layout.Stack>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@
key,
required: data.required,
xdefault: data.default,
array: data.array
array: data.array,
encrypt: data.encrypt
});
}
export async function updateText(
Expand All @@ -38,17 +39,21 @@
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
import EncryptCheckbox from './encryptCheckbox.svelte';
import { InputTextarea } from '$lib/elements/forms';
import { Layout, Typography } from '@appwrite.io/pink-svelte';

export let data: Partial<Models.ColumnText> = {
required: false,
array: false
array: false,
encrypt: false
};

export let editing = false;
export let disabled = false;

if (data && (data.encrypt === undefined || data.encrypt === null)) data.encrypt = false;

let savedDefault = data.default;

function handleDefaultState(hideDefault: boolean) {
Expand Down Expand Up @@ -93,3 +98,7 @@
{disabled}
bind:array={data.array}
bind:required={data.required} />

<Layout.Stack gap="xs" direction="column">
<EncryptCheckbox id="encrypt-text" bind:encrypt={data.encrypt} {editing} {disabled} />
</Layout.Stack>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@
size: data.size,
required: data.required,
xdefault: data.default,
array: data.array
array: data.array,
encrypt: data.encrypt
});
}
export async function updateVarchar(
Expand Down Expand Up @@ -45,16 +46,20 @@
import { ProgressBar } from '$lib/components';
import { Layout, Typography, Tooltip, Icon } from '@appwrite.io/pink-svelte';
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
import EncryptCheckbox from './encryptCheckbox.svelte';

export let data: Partial<Models.ColumnVarchar> = {
required: false,
size: 255,
array: false
array: false,
encrypt: false
};

export let editing = false;
export let disabled = false;

if (data && (data.encrypt === undefined || data.encrypt === null)) data.encrypt = false;

// Local size for reactivity
let size = data.size ?? 255;
$: data.size = size;
Expand Down Expand Up @@ -118,6 +123,10 @@
$: listen(data);

$: handleDefaultState($required || $array);

$: if (data.encrypt && size < 150) {
size = 150;
}
</script>

<InputNumber
Expand All @@ -127,8 +136,11 @@
{disabled}
placeholder="Enter size"
bind:value={size}
min={1}
max={16383} />
min={data.encrypt ? 150 : 1}
max={16383}
helper={data.encrypt
? 'Encrypted varchar columns require a minimum size of 150.'
: undefined} />

{#if !editing}
<Layout.Stack gap="xs">
Expand Down Expand Up @@ -173,3 +185,7 @@
{disabled}
bind:array={data.array}
bind:required={data.required} />

<Layout.Stack gap="xs" direction="column">
<EncryptCheckbox id="encrypt-varchar" bind:encrypt={data.encrypt} {editing} {disabled} />
</Layout.Stack>
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
required: column?.required ?? false,
array: column?.array ?? false,
default: column?.default ?? null,
encrypt: (column as { encrypt?: boolean })?.encrypt ?? false,
...column
} as Partial<Columns>);

Expand All @@ -60,7 +61,8 @@
data = {
required: false,
array: false,
default: null
default: null,
encrypt: false
};

/* default to text */
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@
buildWildcardColumnsQuery,
isRelationship,
isRelationshipToMany,
isSpatialType,
isString
isSpatialType, isTextType
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
} from './rows/store';
import {
columns,
Expand Down Expand Up @@ -1072,7 +1071,7 @@
{@const isEmptyArray = formatted === 'Empty'}
{@const isDatetimeAttribute = rowColumn.type === 'datetime'}
{@const isEncryptedAttribute =
isString(rowColumn) && rowColumn.encrypt}
isTextType(rowColumn) && 'encrypt' in rowColumn && rowColumn.encrypt}
{#if isDatetimeAttribute}
<DualTimeView time={value}>
<span slot="title">Timestamp</span>
Expand Down
Loading