Merge branch 'main' into 'remove-archives'.

This commit is contained in:
Darshan
2026-02-12 13:57:07 +05:30
78 changed files with 1244 additions and 328 deletions
+1 -1
View File
@@ -39,7 +39,7 @@ ENV NODE_OPTIONS=--max_old_space_size=8192
RUN bun run build
FROM nginx:1.26.3-alpine
FROM nginx:1.29.5-alpine
EXPOSE 80
+6 -6
View File
@@ -6,11 +6,11 @@
"name": "@appwrite/console",
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc",
"@faker-js/faker": "^9.9.0",
"@plausible-analytics/tracker": "^0.4.4",
"@popperjs/core": "^2.11.8",
@@ -107,15 +107,15 @@
"@analytics/type-utils": ["@analytics/type-utils@0.6.4", "", {}, "sha512-Ou1gQxFakOWLcPnbFVsrPb8g1wLLUZYYJXDPjHkG07+5mustGs5yqACx42UAu4A6NszNN6Z5gGxhyH45zPWRxw=="],
"@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }],
"@appwrite.io/console": ["@appwrite.io/console@https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa", { "dependencies": { "bignumber.js": "9.0.0", "json-bigint": "1.0.0" } }],
"@appwrite.io/pink-icons": ["@appwrite.io/pink-icons@0.25.0", "", {}, "sha512-0O3i2oEuh5mWvjO80i+X6rbzrWLJ1m5wmv2/M3a1p2PyBJsFxN8xQMTEmTn3Wl/D26SsM7SpzbdW6gmfgoVU9Q=="],
"@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89", { "peerDependencies": { "svelte": "^4.0.0" } }],
"@appwrite.io/pink-icons-svelte": ["@appwrite.io/pink-icons-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc", { "peerDependencies": { "svelte": "^4.0.0" } }],
"@appwrite.io/pink-legacy": ["@appwrite.io/pink-legacy@1.0.3", "", { "dependencies": { "@appwrite.io/pink-icons": "1.0.0", "the-new-css-reset": "^1.11.2" } }, "sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ=="],
"@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }],
"@appwrite.io/pink-svelte": ["@appwrite.io/pink-svelte@https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc", { "dependencies": { "@appwrite.io/pink-icons-svelte": "2.0.0-RC.1", "@floating-ui/dom": "^1.6.13", "@melt-ui/pp": "^0.3.2", "@melt-ui/svelte": "^0.86.6", "@tanstack/svelte-virtual": "^3.13.10", "ansicolor": "^2.0.3", "d3": "^7.9.0", "fuse.js": "^7.1.0", "pretty-bytes": "^6.1.1", "shiki": "^1.18.0", "svelte-motion": "^0.12.2", "svelte-sonner": "^0.3.28" }, "peerDependencies": { "svelte": "^4.0.0" } }],
"@asamuzakjp/css-color": ["@asamuzakjp/css-color@3.2.0", "", { "dependencies": { "@csstools/css-calc": "^2.1.3", "@csstools/css-color-parser": "^3.0.9", "@csstools/css-parser-algorithms": "^3.0.4", "@csstools/css-tokenizer": "^3.0.3", "lru-cache": "^10.4.3" } }, "sha512-K1A6z8tS3XsmCMM86xoWdn7Fkdn9m6RSVtocUrJYIwZnFVkng/PvkEoWtOWmP+Scc6saYWHWZYbndEEXxl24jw=="],
+3 -3
View File
@@ -20,11 +20,11 @@
},
"dependencies": {
"@ai-sdk/svelte": "^1.1.24",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@95675c4",
"@appwrite.io/console": "https://pkg.vc/-/@appwrite/@appwrite.io/console@c6f60aa",
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@c1feb89",
"@appwrite.io/pink-icons-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-icons-svelte@df765cc",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@dd7e01c",
"@appwrite.io/pink-svelte": "https://pkg.vc/-/@appwrite/@appwrite.io/pink-svelte@df765cc",
"@faker-js/faker": "^9.9.0",
"@plausible-analytics/tracker": "^0.4.4",
"@popperjs/core": "^2.11.8",
@@ -62,12 +62,7 @@
}
</script>
<Modal
bind:error
bind:show
dismissible={false}
onSubmit={handleSubmit}
title="Update payment method state">
<Modal bind:show bind:error onSubmit={handleSubmit} title="Update payment method state">
<Layout.Stack direction="column" gap="m">
<Typography.Text>
State information is required for US payment methods to apply correct taxes and meet
@@ -36,6 +36,7 @@
</Layout.Stack>
<Table.Root
class="responsive-table"
columns={[
{ id: 'type', width: { min: 150 } },
{ id: 'name', width: { min: 80 } },
@@ -102,6 +102,7 @@
</Layout.Stack>
<Table.Root
class="responsive-table"
columns={[
{ id: 'type', width: { min: 150 } },
{ id: 'name', width: { min: 80 } },
+235 -20
View File
@@ -1,35 +1,246 @@
<script lang="ts">
import { Icon, Layout, Tag, Tooltip } from '@appwrite.io/pink-svelte';
import { queries, tagFormat, tags } from './store';
import {
Icon,
Layout,
Tooltip,
CompoundTagRoot,
CompoundTagChild,
Typography,
ActionMenu,
Selector
} from '@appwrite.io/pink-svelte';
import { capitalize } from '$lib/helpers/string';
import { queries, tags } from './store';
import { IconX } from '@appwrite.io/pink-icons-svelte';
import { parsedTags } from './setFilters';
import { parsedTags, type ParsedTag } from './setFilters';
import { Button } from '$lib/elements/forms';
import type { Column } from '$lib/helpers/types';
import { writable, type Writable } from 'svelte/store';
import Menu from '$lib/components/menu/menu.svelte';
import { addFilterAndApply, buildFilterCol, type FilterData } from './quickFilters';
import QuickFilters from '$lib/components/filters/quickFilters.svelte';
import { isSmallViewport } from '$lib/stores/viewport';
let {
columns = writable([]),
analyticsSource = ''
}: { columns?: Writable<Column[]>; analyticsSource?: string } = $props();
function parseTagParts(tagString: string): { text: string; operator: boolean }[] {
return tagString
.split(/\*\*(.*?)\*\*/)
.map((part, index) => {
// Even indices are outside bold (operators), odd indices are inside bold (values)
if (index % 2 === 0) {
return part
.split(/\s+/)
.filter(Boolean)
.map((t) => ({ text: t, operator: true }));
} else {
return [{ text: part, operator: false }];
}
})
.flat()
.filter((p) => Boolean(p.text));
}
function getFilterFor(title: string): FilterData | null {
if (!columns) return null;
const col = ($columns as unknown as Column[]).find((c) => c.title === title);
if (!col) return null;
const filter = buildFilterCol(col);
return filter ?? null;
}
// Build available filter definitions from provided columns
let availableFilters = $derived(
($columns as unknown as Column[] | undefined)?.length
? (($columns as unknown as Column[])
.map((c) => (c.filter !== false ? buildFilterCol(c) : null))
.filter((f) => f && f.options) as FilterData[])
: []
);
// QuickFilters uses the same filters list
let filterCols = $derived(availableFilters);
// Always-show placeholders are derived from available filters (no hardcoding)
// Use reactive array so runes can track changes
let hiddenPlaceholders: string[] = $state([]);
let activeTitles = $derived(
($parsedTags || []).map((t) => (t as ParsedTag).title).filter(Boolean) as string[]
);
// Compute current placeholders (major filters not already active or dismissed)
let placeholders = $derived(
availableFilters
.filter((f) => !activeTitles.includes(f.title))
.filter((f) => !hiddenPlaceholders.includes(f.title))
);
</script>
{#if $parsedTags?.length}
<Layout.Stack direction="row" gap="s" wrap="wrap" alignItems="center" inline>
<Layout.Stack direction="row" gap="s" wrap="wrap" alignItems="center" inline>
{#if $parsedTags?.length}
{#each $parsedTags as tag (tag.tag)}
<span>
<Tooltip
disabled={Array.isArray(tag.value) ? tag.value?.length < 3 : true}
maxWidth="600px">
<Tag
size="s"
on:click={() => {
const t = $tags.filter((t) => t.tag.includes(tag.tag.split(' ')[0]));
t.forEach((t) => (t ? queries.removeFilter(t) : null));
queries.apply();
parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag));
}}>
{#key tag.tag}
<span use:tagFormat>{tag.tag}</span>
{/key}
<Icon icon={IconX} size="s" slot="end" />
</Tag>
<CompoundTagRoot size="s">
{@const parts = parseTagParts(tag.tag)}
{@const property = (tag as ParsedTag).title}
{#each parts as part}
<CompoundTagChild>
<Menu>
<span>
{#if part.operator}
<Typography.Text color="--fgcolor-neutral-secondary"
>{part.text}</Typography.Text>
{:else}
<Typography.Text
variant="m-500"
color="--fgcolor-neutral-secondary"
>{part.text
.split(' or ')
.map((t) => capitalize(t))
.join(' or ')}</Typography.Text>
{/if}
</span>
<svelte:fragment slot="menu">
{#if property}
{@const filter = getFilterFor(property)}
{#if filter}
{@const isArray = filter?.array}
{@const selectedArray = Array.isArray(tag.value)
? tag.value
: []}
{#each filter.options as option (filter.title + option.value + option.label)}
<ActionMenu.Root>
<ActionMenu.Item.Button
on:click={() => {
if (isArray) {
const exists =
selectedArray.includes(
option.value
);
const next = exists
? selectedArray.filter(
(v) =>
v !== option.value
)
: [
...selectedArray,
option.value
];
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
null,
next,
$columns,
analyticsSource
);
} else {
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
option.value,
[],
$columns,
analyticsSource
);
}
}}>
<Layout.Stack direction="row" gap="s">
{#if isArray}
<Selector.Checkbox
checked={selectedArray.includes(
option.value
)}
size="s" />
{/if}
{capitalize(option.label)}
</Layout.Stack>
</ActionMenu.Item.Button>
</ActionMenu.Root>
{/each}
{/if}
{/if}
</svelte:fragment>
</Menu>
</CompoundTagChild>
{/each}
<CompoundTagChild
dismiss
on:click={() => {
const t = $tags.filter((t) =>
t.tag.includes((tag as ParsedTag).title)
);
t.forEach((t) => (t ? queries.removeFilter(t) : null));
queries.apply();
parsedTags.update((tags) => tags.filter((t) => t.tag !== tag.tag));
}}>
<Icon icon={IconX} size="s" />
</CompoundTagChild>
</CompoundTagRoot>
<span slot="tooltip">{tag?.value?.toString()}</span>
</Tooltip>
</span>
{/each}
{/if}
<!-- Always render remaining placeholder tags alongside active tags -->
{#if placeholders?.length}
{#each placeholders as filter (filter.title + filter.id)}
<span>
<Menu>
<CompoundTagRoot size="s">
<CompoundTagChild>
<span>{capitalize(filter.title)}</span>
</CompoundTagChild>
<CompoundTagChild
dismiss
on:click={(e) => {
e.stopPropagation();
if (!hiddenPlaceholders.includes(filter.title)) {
hiddenPlaceholders = [...hiddenPlaceholders, filter.title];
}
}}>
<Icon icon={IconX} size="s" />
</CompoundTagChild>
</CompoundTagRoot>
<svelte:fragment slot="menu">
{#if filter.options}
{#each filter.options as option (filter.title + option.value + option.label)}
<ActionMenu.Root>
<ActionMenu.Item.Button
on:click={() => {
addFilterAndApply(
filter.id,
filter.title,
filter.operator,
filter?.array ? null : option.value,
filter?.array ? [option.value] : [],
$columns,
analyticsSource
);
}}>
{capitalize(option.label)}
</ActionMenu.Item.Button>
</ActionMenu.Root>
{/each}
{/if}
</svelte:fragment>
</Menu>
</span>
{/each}
{/if}
{#if $parsedTags?.length}
<Button
size="s"
text
@@ -38,5 +249,9 @@
queries.apply();
parsedTags.set([]);
}}>Clear all</Button>
</Layout.Stack>
{/if}
{/if}
{#if filterCols?.length && !$isSmallViewport}
<QuickFilters {columns} {analyticsSource} {filterCols} />
{/if}
</Layout.Stack>
+22 -5
View File
@@ -13,19 +13,36 @@
let {
columns,
filterCols,
analyticsSource
analyticsSource,
buttonVariant = 'ghost'
}: {
columns: Writable<Column[]>;
filterCols: FilterData[];
analyticsSource?: string;
buttonVariant?: 'ghost' | 'secondary';
} = $props();
</script>
<Menu>
<Button secondary badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
<Icon icon={IconFilterLine} slot="start" size="s" />
Filters
</Button>
{#if buttonVariant === 'secondary'}
<Button
ariaLabel="Filters"
secondary
size="s"
badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
<Icon icon={IconFilterLine} size="s" slot="start" />
<span class="text">Filters</span>
</Button>
{:else}
<Button
ariaLabel="Filters"
text
icon
size="s"
badge={$parsedTags?.length ? `${$parsedTags.length}` : undefined}>
<Icon icon={IconFilterLine} size="s" />
</Button>
{/if}
<svelte:fragment slot="menu">
{#each filterCols.filter((f) => f?.options) as filter (filter.title + filter.id)}
{#if filter.options}
+22 -11
View File
@@ -3,15 +3,21 @@ import { get, writable } from 'svelte/store';
import { type FilterData } from './quickFilters';
import { tags, type TagValue } from './store';
export const parsedTags = writable<TagValue[]>([]);
export type ParsedTag = TagValue & {
title: string;
};
export const parsedTags = writable<ParsedTag[]>([]);
export function setFilters(localTags: TagValue[], filterCols: FilterData[], $columns: Column[]) {
if (!localTags?.length) {
parsedTags.set([]);
filterCols.forEach((filter) => {
resetOptions(filter);
cleanOldTags(filter?.title);
});
} else {
parsedTags.set([]);
filterCols.forEach((filter) => {
const id = filter?.id?.toLowerCase();
if (id?.includes('duration')) {
@@ -47,9 +53,10 @@ export function setFilterData(filter: FilterData) {
});
}
cleanOldTags(filter?.title);
const newTag = {
const newTag: ParsedTag = {
tag: tagData.tag.replace(',', ' or '),
value: tagData.value
value: tagData.value,
title: filter.title
};
parsedTags.update((tags) => {
@@ -69,9 +76,10 @@ export function setTimeFilter(filter: FilterData, columns: Column[]) {
const ranges = col.elements as { value: string; label: string }[];
const timeRange = ranges.find((range) => range.value === timeTag.value);
if (timeRange) {
const newTag = {
const newTag: ParsedTag = {
tag: `**${filter.title}** is **${timeRange.label}**`,
value: timeRange.value
value: timeRange.value,
title: filter.title
};
cleanOldTags(filter?.title);
@@ -102,9 +110,10 @@ export function setSizeFilter(filter: FilterData, columns: Column[]) {
if (sizeRange) {
cleanOldTags(filter?.title);
const newTag = {
const newTag: ParsedTag = {
tag: `**${filter.title}** is **${sizeRange.label}**`,
value: sizeTag.value
value: sizeTag.value,
title: filter.title
};
parsedTags.update((tags) => {
tags.push(newTag);
@@ -126,9 +135,10 @@ export function setStatusCodeFilter(filter: FilterData, columns: Column[]) {
const codeRange = ranges.find((c) => c?.value && c.value === statusCodeTag.value);
if (codeRange) {
cleanOldTags(filter?.title);
const newTag = {
const newTag: ParsedTag = {
tag: `**${filter.title}** is **${codeRange.label}**`,
value: statusCodeTag.value
value: statusCodeTag.value,
title: filter.title
};
parsedTags.update((tags) => {
tags.push(newTag);
@@ -156,9 +166,10 @@ export function setDateFilter(filter: FilterData, columns: Column[]) {
});
if (dateRange) {
cleanOldTags(filter?.title);
const newTag = {
const newTag: ParsedTag = {
tag: `**${filter.title}** is **${dateRange.label}**`,
value: dateTag.value
value: dateTag.value,
title: filter.title
};
parsedTags.update((tags) => {
tags.push(newTag);
+50 -2
View File
@@ -156,6 +156,10 @@ export enum ValidOperators {
export enum ValidTypes {
String = 'string',
Varchar = 'varchar',
Text = 'text',
Mediumtext = 'mediumtext',
Longtext = 'longtext',
Integer = 'integer',
Double = 'double',
Boolean = 'boolean',
@@ -175,8 +179,32 @@ const operatorsDefault = new Map<
hideInput?: boolean;
}
>([
[ValidOperators.StartsWith, { query: Query.startsWith, types: [ValidTypes.String] }],
[ValidOperators.EndsWith, { query: Query.endsWith, types: [ValidTypes.String] }],
[
ValidOperators.StartsWith,
{
query: Query.startsWith,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext
]
}
],
[
ValidOperators.EndsWith,
{
query: Query.endsWith,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext
]
}
],
[
ValidOperators.GreaterThan,
{
@@ -211,6 +239,10 @@ const operatorsDefault = new Map<
query: Query.equal,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
@@ -227,6 +259,10 @@ const operatorsDefault = new Map<
query: Query.notEqual,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
@@ -242,6 +278,10 @@ const operatorsDefault = new Map<
query: Query.isNotNull,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
@@ -260,6 +300,10 @@ const operatorsDefault = new Map<
query: Query.isNull,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
@@ -278,6 +322,10 @@ const operatorsDefault = new Map<
query: Query.contains,
types: [
ValidTypes.String,
ValidTypes.Varchar,
ValidTypes.Text,
ValidTypes.Mediumtext,
ValidTypes.Longtext,
ValidTypes.Integer,
ValidTypes.Double,
ValidTypes.Boolean,
@@ -152,7 +152,13 @@
<span slot="description">
Select the directory where your site code is located using the menu below.
</span>
<DirectoryPicker {directories} {isLoading} on:select={fetchContents} bind:expanded />
<DirectoryPicker
{directories}
{isLoading}
bind:expanded
selected={currentPath}
openTo={currentPath}
on:select={fetchContents} />
<svelte:fragment slot="footer">
<Button secondary on:click={() => (show = false)}>Cancel</Button>
@@ -9,19 +9,31 @@
*/
export let maxSize: $$Props['maxSize'];
/**
* Hides empty segments where size is <= 0;
*/
export let hideEmptySegments: $$Props['hideEmptySegments'] = false;
/**
* The data for the progressbar
*/
export let data: $$Props['data'];
$: segments = hideEmptySegments ? data.filter((item) => item.size > 0) : data;
/**
* The remaining value of the progressbar
*/
$: remainder = data.reduce((sum: number, item: ProgressbarData) => sum - item.size, maxSize);
$: remainder = segments.reduce(
(sum: number, item: ProgressbarData) => sum - item.size,
maxSize
);
$: segmentGap = segments.length > 1 ? '2px' : '0px';
</script>
<section class="progressbar__container">
{#each $$props.data as item}
<section class="progressbar__container" style:--progressbar-segment-gap={segmentGap}>
{#each segments as item}
<Tooltip disabled={!item.tooltip} maxWidth="max-content">
<div
class="progressbar__content"
@@ -64,7 +76,7 @@
border-radius: var(--progressbar-border-radius);
display: flex;
flex-direction: row;
gap: 2px;
gap: var(--progressbar-segment-gap, 2px);
margin-top: 1rem;
overflow: hidden;
}
+1
View File
@@ -11,6 +11,7 @@ export type ProgressbarData = {
export type ProgressbarProps = {
maxSize: number;
hideEmptySegments?: boolean;
data: Array<ProgressbarData>;
};
+17 -1
View File
@@ -183,7 +183,8 @@ function generateValueForField(
function generateSingleValue(field: Field): string | number | boolean | NestedNumberArray | null {
switch (field.type) {
case 'string': {
case 'string':
case 'varchar': {
if ('format' in field && field.format) {
switch (field.format) {
case 'email': {
@@ -214,6 +215,21 @@ function generateSingleValue(field: Field): string | number | boolean | NestedNu
}
}
case 'text': {
// TEXT type max size is 16,383 characters (utf8mb4)
return generateStringValue(field.key, 1000);
}
case 'mediumtext': {
// MEDIUMTEXT type max size is 4,194,303 characters (utf8mb4)
return generateStringValue(field.key, 1000);
}
case 'longtext': {
// LONGTEXT type max size is 1,073,741,823 characters (utf8mb4)
return generateStringValue(field.key, 1000);
}
case 'integer': {
const intAttr = field as Models.ColumnInteger;
const minCompat = coerceToNumber(intAttr.min);
+2 -2
View File
@@ -1,5 +1,5 @@
import { writable, type Writable } from 'svelte/store';
import { deepClone, objectEntries } from './object';
import { objectEntries } from './object';
/**
* Given an object `obj`, returns an object of writable stores for each key in that object.
@@ -16,7 +16,7 @@ export function createConservative<Obj extends Record<string, unknown>>(obj: Obj
[K in keyof Obj]: Writable<Obj[K]>;
};
const history = deepClone(obj);
const history = structuredClone(obj);
function listen(input: Obj) {
objectEntries(input).forEach(([key, value]) => {
+4
View File
@@ -30,6 +30,10 @@ export type Prettify<T> = T & {};
const columnTypes = [
'string',
'varchar',
'text',
'mediumtext',
'longtext',
'integer',
'double',
'boolean',
+1 -1
View File
@@ -49,7 +49,7 @@
expanded={databasesScreen && !insideSideSheet}
slotSpacing={databasesScreen && !insideSideSheet}>
{#if logs.total}
<Table.Root {columns} let:root>
<Table.Root class="responsive-table" {columns} let:root>
<svelte:fragment slot="header" let:root>
<Table.Header.Cell column="user" {root}>User</Table.Header.Cell>
<Table.Header.Cell column="event" {root}>Event</Table.Header.Cell>
+51 -23
View File
@@ -1,12 +1,8 @@
<script lang="ts">
import { SearchQuery, ViewSelector } from '$lib/components';
import {
FiltersBottomSheet,
ParsedTagList,
queryParamToMap,
Filters
} from '$lib/components/filters';
import { FiltersBottomSheet, ParsedTagList, queryParamToMap } from '$lib/components/filters';
import QuickFilters from '$lib/components/filters/quickFilters.svelte';
import Button from '$lib/elements/forms/button.svelte';
import { View } from '$lib/helpers/load';
import type { Column } from '$lib/helpers/types';
@@ -18,7 +14,7 @@
import DisplaySettingsModal from './displaySettingsModal.svelte';
import { buildFilterCol } from '$lib/components/filters/quickFilters';
import { afterNavigate } from '$app/navigation';
import { setFilters } from '$lib/components/filters/setFilters';
import { parsedTags, setFilters } from '$lib/components/filters/setFilters';
let {
columns,
@@ -28,7 +24,7 @@
hasSearch = false,
searchPlaceholder = 'Search by ID',
hasFilters = false,
hasCustomFiltersOnly = false,
filtersStyle = 'chips',
analyticsSource = '',
children
}: {
@@ -39,7 +35,7 @@
hasSearch?: boolean;
searchPlaceholder?: string;
hasFilters?: boolean;
hasCustomFiltersOnly?: boolean;
filtersStyle?: 'chips' | 'dropdown';
analyticsSource?: string;
children?: Snippet;
} = $props();
@@ -52,9 +48,12 @@
let showDisplaySettingsModal = $state(false);
let showFilters = $state(false);
let filterCols = $derived(
const filterCols = $derived(
$columns.map((col) => (col.filter !== false ? buildFilterCol(col) : null)).filter(Boolean)
);
const filtersBadge = $derived(
filtersStyle === 'dropdown' && $parsedTags?.length ? `${$parsedTags.length}` : undefined
);
afterNavigate((p) => {
if (!hasFilters) return;
@@ -62,7 +61,6 @@
const localQueries = queryParamToMap(paramQueries || '[]');
const localTags = Array.from(localQueries.keys());
setFilters(localTags, filterCols, $columns);
filterCols = filterCols;
});
</script>
@@ -106,21 +104,47 @@
{#if showSearch && hasSearch}
<SearchQuery placeholder={searchPlaceholder} />
{/if}
{#if hasFilters && filtersStyle === 'chips'}
<div style="overflow-x: auto;">
<ParsedTagList {columns} {analyticsSource} />
</div>
{/if}
</Layout.Stack>
{:else}
<Layout.Stack direction="row" justifyContent="space-between">
<Layout.Stack direction="row" alignItems="center">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="flex-start">
<Layout.Stack
direction="row"
alignItems="flex-start"
gap="m"
style="min-width: 0; flex: 1 1 auto;">
{#if hasSearch}
<SearchQuery placeholder={searchPlaceholder} />
<div style="flex: 0 0 auto; max-width: 360px; min-width: 0;">
<SearchQuery placeholder={searchPlaceholder} />
</div>
{/if}
{#if hasFilters && filtersStyle === 'chips'}
<!-- Tags with Filters button (rendered inside ParsedTagList) -->
<Layout.Stack
direction="row"
alignItems="center"
gap="s"
wrap="wrap"
style="min-width: 0; flex: 1 1 auto;">
<ParsedTagList {columns} {analyticsSource} />
</Layout.Stack>
{/if}
</Layout.Stack>
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
{#if hasFilters && $columns?.length}
{#if hasCustomFiltersOnly}
<Filters query="[]" {columns} {analyticsSource} />
{:else}
<QuickFilters {columns} {analyticsSource} {filterCols} />
{/if}
<Layout.Stack
direction="row"
alignItems="center"
justifyContent="flex-end"
style="align-self: flex-start; white-space: nowrap;">
{#if hasFilters && filtersStyle === 'dropdown'}
<QuickFilters
{columns}
{analyticsSource}
buttonVariant="secondary"
filterCols={filterCols.filter((f) => f?.options)} />
{/if}
{#if hasDisplaySettings}
<ViewSelector ui="new" {view} {columns} {hideView} {hideColumns} />
@@ -131,7 +155,6 @@
</Layout.Stack>
</Layout.Stack>
{/if}
<ParsedTagList />
</Layout.Stack>
</header>
@@ -171,7 +194,12 @@
{/snippet}
{#snippet filtersButton(icon = false)}
<Button ariaLabel="Filters" on:click={() => (showFilters = !showFilters)} secondary {icon}>
<Button
ariaLabel="Filters"
on:click={() => (showFilters = !showFilters)}
text
{icon}
badge={filtersBadge}>
<Icon icon={IconFilterLine} />
</Button>
{/snippet}
+9 -5
View File
@@ -5,8 +5,9 @@
import { activeHeaderAlert } from '$routes/(console)/store';
import { onMount, setContext } from 'svelte';
import { writable } from 'svelte/store';
import { showSubNavigation } from '$lib/stores/layout';
import { showSubNavigation, showOnboardingAnimation } from '$lib/stores/layout';
import { organization, organizationList } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
import { user } from '$lib/stores/user';
import { isCloud } from '$lib/system';
@@ -176,7 +177,8 @@
$: subNavigation = $page.data.subNavigation;
$: shouldRenderSidebar = !$isNewWizardStatusOpen && showSideNavigation;
$: shouldRenderSidebar =
!$isNewWizardStatusOpen && showSideNavigation && !$showOnboardingAnimation;
$: hasSidebarSpace = shouldRenderSidebar && !$isTabletViewport && !!selectedProject;
$: {
@@ -203,9 +205,9 @@
class:is-open={$showSubNavigation}
class:u-hide={$wizard.show || $wizard.cover}
class:is-fixed-layout={$activeHeaderAlert?.show}
class:no-header={!showHeader}
class:no-header={!showHeader || $showOnboardingAnimation}
style:--p-side-size={sideSize}>
{#if showHeader}
{#if showHeader && !$showOnboardingAnimation}
<Navbar {...navbarProps} bind:sideBarIsOpen={$isSidebarOpen} bind:showAccountMenu />
{/if}
@@ -220,7 +222,9 @@
bind:state />
{/if}
<SideNavigation bind:subNavigation />
{#if !$showOnboardingAnimation}
<SideNavigation bind:subNavigation />
{/if}
<div
class="content"
+1
View File
@@ -27,3 +27,4 @@ export function updateLayout(args: updateLayoutArguments) {
}
export const showSubNavigation = writable(false);
export const showOnboardingAnimation = writable(false);
+46 -61
View File
@@ -1,5 +1,5 @@
import { writable } from 'svelte/store';
import { Resources } from '@appwrite.io/console';
import { includesAll } from '$lib/helpers/array';
const initialFormData = {
@@ -51,66 +51,66 @@ export const ResourcesFriendly = {
row: { singular: 'Row', plural: 'Rows' }
};
const resources = Object.keys(ResourcesFriendly);
type Resource = (typeof resources)[number];
// @todo: @itznotabug - check if other resources are correct and work fine!
export const providerResources: Record<Provider, Resource[]> = {
appwrite: [...resources], // new terminology, others are ok?
export const providerResources: Record<Provider, Resources[]> = {
appwrite: Object.values(Resources),
supabase: [
'user',
'database',
'collection',
'attribute',
'index',
'document',
'bucket',
'file'
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Index,
Resources.Document,
Resources.Bucket,
Resources.File
],
nhost: ['user', 'database', 'collection', 'attribute', 'index', 'document', 'bucket', 'file'],
firebase: ['user', 'database', 'collection', 'attribute', 'document', 'bucket', 'file']
nhost: [
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Index,
Resources.Document,
Resources.Bucket,
Resources.File
],
firebase: [
Resources.User,
Resources.Database,
Resources.Collection,
Resources.Attribute,
Resources.Document,
Resources.Bucket,
Resources.File
]
};
export const migrationFormToResources = (
formData: MigrationFormData,
provider: Provider
): Resource[] => {
const resources: Resource[] = [];
const addResource = (resource: Resource) => {
): Resources[] => {
const resources: Resources[] = [];
const addResource = (resource: Resources) => {
if (providerResources[provider].includes(resource)) {
resources.push(resource);
}
};
if (formData.users.root) {
addResource('user');
}
if (formData.users.teams) {
addResource('team');
addResource('membership');
addResource(Resources.User);
}
if (formData.databases.root) {
addResource('database');
addResource('table');
addResource('column');
addResource('columnIndex');
addResource(Resources.Database);
addResource(Resources.Table);
addResource(Resources.Column);
addResource(Resources.Index);
}
if (formData.databases.rows) {
addResource('row');
}
if (formData.functions.root) {
addResource('function');
}
if (formData.functions.env) {
addResource('environment-variable');
}
if (formData.functions.inactive) {
addResource('deployment');
addResource(Resources.Row);
}
if (formData.storage.root) {
addResource('bucket');
addResource('file');
addResource(Resources.Bucket);
addResource(Resources.File);
}
return resources;
@@ -137,33 +137,18 @@ export const isVersionAtLeast = (version: string, atLeast: string) => {
return compareVersions(version, atLeast) >= 0;
};
export const resourcesToMigrationForm = (
resources: Resource[],
version = '0.0.0'
): MigrationFormData => {
export const resourcesToMigrationForm = (resources: Resources[]): MigrationFormData => {
const formData = { ...initialFormData };
if (resources.includes('user')) {
if (resources.includes(Resources.User)) {
formData.users.root = true;
}
if (includesAll(resources, ['team', 'membership'])) {
formData.users.teams = true;
}
if (resources.includes('database')) {
if (resources.includes(Resources.Database)) {
formData.databases.root = true;
}
if (includesAll(resources, ['table', 'column', 'row'])) {
if (includesAll(resources, [Resources.Table, Resources.Column, Resources.Row] as Resources[])) {
formData.databases.rows = true;
}
if (resources.includes('function') && isVersionAtLeast(version, '1.4.0')) {
formData.functions.root = true;
}
if (resources.includes('environment-variable') && isVersionAtLeast(version, '1.4.0')) {
formData.functions.env = true;
}
if (resources.includes('deployment') && isVersionAtLeast(version, '1.4.0')) {
formData.functions.inactive = true;
}
if (includesAll(resources, ['bucket', 'file'])) {
if (includesAll(resources, [Resources.Bucket, Resources.File] as Resources[])) {
formData.storage.root = true;
}
@@ -5,14 +5,13 @@
import {
createMigrationFormStore,
createMigrationProviderStore,
isVersionAtLeast,
type MigrationFormData,
providerResources,
resourcesToMigrationForm
} from '$lib/stores/migration';
import { Button } from '$lib/elements/forms';
import { wizard } from '$lib/stores/wizard';
import type { Models } from '@appwrite.io/console';
import { Resources, type Models } from '@appwrite.io/console';
import type { sdk } from '$lib/stores/sdk';
import ImportReport from '$routes/(console)/project-[region]-[project]/settings/migrations/(import)/importReport.svelte';
@@ -33,11 +32,9 @@
}
function selectAll() {
$formData = resourcesToMigrationForm(resources, version);
$formData = resourcesToMigrationForm(resources);
}
$: version = report?.version || '0.0.0';
let error = false;
let isOpen = false;
let report: Models.MigrationReport;
@@ -98,14 +95,21 @@
const shouldRenderGroup = (groupKey: string): boolean => {
if (groupKey === 'functions') {
return resources.includes('function') && isVersionAtLeast(version, '1.4.0');
// Functions not in SDK Resources enum, skip
return false;
}
if (groupKey === 'storage') {
return resources.includes('bucket') && resources.includes('file');
return resources.includes(Resources.Bucket) && resources.includes(Resources.File);
}
return resources.includes(groupKey.slice(0, -1));
// Map groupKey to Resources enum
const groupToResource: Record<string, Resources> = {
users: Resources.User,
databases: Resources.Database
};
const resource = groupToResource[groupKey];
return resource ? resources.includes(resource) : false;
};
// no typecasting in svelte context!
@@ -125,14 +129,6 @@
</script>
<Layout.Stack gap="l">
{#if report && !isVersionAtLeast(version, '1.4.0') && $provider.provider === 'appwrite'}
<Alert.Inline status="warning">
<svelte:fragment slot="title">Functions not available for import</svelte:fragment>
To migrate your functions, update the version of the Appwrite instance you're importing from
to a version newer than 1.4
</Alert.Inline>
{/if}
{#if error}
<Alert.Inline status="error" title="Couldnt load resources">
{#if migrationType === 'provider'}
+4 -1
View File
@@ -3,6 +3,7 @@ import Breadcrumbs from './breadcrumbs.svelte';
import type { LayoutLoad } from './$types';
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import { Platform, Query } from '@appwrite.io/console';
export const load: LayoutLoad = async ({ depends }) => {
depends(Dependencies.FACTORS);
@@ -10,7 +11,9 @@ export const load: LayoutLoad = async ({ depends }) => {
const [factors, identities] = await Promise.all([
sdk.forConsole.account.listMFAFactors(),
sdk.forConsole.account.listIdentities()
sdk.forConsole.account.listIdentities({
queries: [Query.notContains('provider', Platform.Imagine)]
})
]);
return {
@@ -97,6 +97,7 @@
</Layout.Stack>
<Table.Root
class="responsive-table"
let:root
columns={[
{ id: 'client', width: { min: 450 } },
@@ -8,12 +8,13 @@
import { Dependencies } from '$lib/constants';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import { base, resolve } from '$app/paths';
import { addNotification } from '$lib/stores/notifications';
import CreateProject from '$lib/layout/createProject.svelte';
import { loadAvailableRegions } from '$routes/(console)/regions';
import { regions as regionsStore } from '$lib/stores/organization';
import { user } from '$lib/stores/user';
import { showOnboardingAnimation } from '$lib/stores/layout';
let isLoading = false;
let startAnimation = false;
@@ -57,10 +58,17 @@
});
startAnimation = true;
showOnboardingAnimation.set(true);
setTimeout(async () => {
await invalidate(Dependencies.ACCOUNT);
goto(`${base}/project-${project.region ?? 'default'}-${project.$id}`);
await goto(
resolve('/(console)/project-[region]-[project]', {
region: project.region ?? 'default',
project: project.$id
})
);
showOnboardingAnimation.set(false);
}, 3000);
} catch (e) {
trackError(e, Submit.ProjectCreate);
@@ -169,11 +169,11 @@
{/if}
</Layout.Stack>
{#if isCloud && data.currentPlan?.projects !== 0 && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed}
{#if isCloud && !data.program && data.currentPlan?.projects && activeProjectsTotal <= data.currentPlan.projects && !freePlanAlertDismissed}
<Alert.Inline dismissible on:dismiss={dismissFreePlanAlert}>
<Typography.Text
>Your Free plan includes up to 2 projects and limited resources. Upgrade to unlock
more capacity and features.</Typography.Text>
>Your Free plan includes up to {data.currentPlan?.projects} projects and limited resources.
Upgrade to unlock more capacity and features.</Typography.Text>
<svelte:fragment slot="actions">
<Button
compact
@@ -151,11 +151,17 @@
}
async function trackDowngradeFeedback() {
const selectedReason = feedbackDowngradeOptions.find(
(option) => option.value === feedbackDowngradeReason
)?.label;
const defaultReason = feedbackDowngradeOptions.find(
(option) => option.value === 'other'
)?.label;
await sdk.forConsole.organizations.createDowngradeFeedback({
organizationId: data.organization.$id,
reason: feedbackDowngradeOptions.find(
(option) => option.value === feedbackDowngradeReason
)?.label,
reason: selectedReason ?? defaultReason,
message: feedbackMessage ?? '',
fromPlanId: data.organization.billingPlanId,
toPlanId: selectedPlan.$id
@@ -58,7 +58,7 @@
<ResponsiveContainerHeader
hasSearch
{columns}
bind:view={data.view}
view={data.view}
searchPlaceholder="Search by name or ID">
{#if $canWriteDatabases}
<Tooltip disabled={!isLimited}>
@@ -4,7 +4,7 @@
import { Button, InputText } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { ID, type Models } from '@appwrite.io/console';
import { BackupServices, ID, type Models } from '@appwrite.io/console';
import { createEventDispatcher } from 'svelte';
import { isCloud } from '$lib/system';
import { currentPlan } from '$lib/stores/organization';
@@ -65,7 +65,7 @@
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
policyId: ID.unique(),
services: ['databases'],
services: [BackupServices.Databases],
retention: policy.retained,
schedule: policy.schedule,
name: policy.label,
@@ -1,10 +1,11 @@
<script module lang="ts">
import { IndexType, OrderBy } from '@appwrite.io/console';
export type CreateIndexesCallbackType = {
key: string;
type: IndexType;
fields: string[];
lengths: (number | null)[];
orders: string[];
orders: OrderBy[];
};
</script>
@@ -22,9 +23,7 @@
import { isSmallViewport } from '$lib/stores/viewport';
import { type Entity, getTerminologies } from '$database/(entity)';
import { resolveRoute, withPath } from '$lib/stores/navigation';
import { IndexType } from '@appwrite.io/console';
import { columnOptions as baseColumnOptions } from '$database/table-[table]/columns/store';
import { IndexOrder } from '$database/(suggestions)';
let {
entity,
@@ -63,7 +62,7 @@
let fieldList: Array<{
value: string;
order: IndexOrder | null;
order: OrderBy | null;
length: number | null;
}> = $state([{ value: '', order: null, length: null }]);
@@ -78,13 +77,13 @@
let orderOptions = $derived.by(() =>
selectedType === IndexType.Spatial
? [
{ value: 'ASC', label: 'ASC' },
{ value: 'DESC', label: 'DESC' },
{ value: OrderBy.Asc, label: 'ASC' },
{ value: OrderBy.Desc, label: 'DESC' },
{ value: null, label: 'NONE' }
]
: [
{ value: 'ASC', label: 'ASC' },
{ value: 'DESC', label: 'DESC' }
{ value: OrderBy.Asc, label: 'ASC' },
{ value: OrderBy.Desc, label: 'DESC' }
]
);
@@ -111,7 +110,7 @@
function initialize() {
const field = entity.fields.filter((field) => externalFieldKey === field.key);
const isSpatial = field.length && isSpatialType(field[0]);
const order = isSpatial ? null : IndexOrder.ASC;
const order = isSpatial ? null : OrderBy.Asc;
selectedType = isSpatial ? IndexType.Spatial : IndexType.Key;
@@ -165,8 +164,7 @@
try {
const orders = fieldList
.map((field) => field.order)
.filter((order: IndexOrder) => order !== null)
.map((order) => String(order));
.filter((order): order is OrderBy => order !== null);
await onCreateIndex({
key,
@@ -1504,7 +1504,7 @@
id="type"
required
label="Type"
value={selectedOption?.name || 'String'}
value={selectedOption?.name || 'Text'}
on:change={(e) => {
const newOption = columnOptions.find(
(opt) => opt.name === e.detail
@@ -2,16 +2,11 @@
import { Alert, Accordion, Icon, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
import { Button, InputSelect /*InputNumber*/ } from '$lib/elements/forms';
import {
showIndexesSuggestions,
IndexOrder,
mockSuggestions,
type SuggestedIndexSchema
} from './store';
import { showIndexesSuggestions, mockSuggestions, type SuggestedIndexSchema } from './store';
import { Modal } from '$lib/components';
import { type Entity, SideSheet } from '$database/(entity)';
import { isSmallViewport } from '$lib/stores/viewport';
import { IndexType } from '@appwrite.io/console';
import { IndexType, OrderBy } from '@appwrite.io/console';
import { capitalize } from '$lib/helpers/string';
import { type Columns } from '../table-[table]/store';
import { isRelationship } from '../table-[table]/rows/store';
@@ -74,7 +69,7 @@
key: column.name,
type: IndexType.Key,
fields: [column.name],
orders: index === 2 ? IndexOrder.DESC : IndexOrder.ASC,
orders: index === 2 ? OrderBy.Desc : OrderBy.Asc,
lengths: []
}));
} else {
@@ -90,7 +85,7 @@
return {
key: index.columns[0],
type: index.type as IndexType,
orders: (index.orders?.[0] as IndexOrder) || IndexOrder.ASC,
orders: (index.orders?.[0] as OrderBy) || OrderBy.Asc,
fields: index.columns,
lengths: index.lengths ?? []
};
@@ -118,7 +113,7 @@
indexes.push({
key: '',
type: IndexType.Key,
orders: IndexOrder.ASC,
orders: OrderBy.Asc,
fields: [],
lengths: null
});
@@ -139,11 +134,11 @@
}
function getOrderOptions(selectedType: IndexType) {
const base = [IndexOrder.ASC, IndexOrder.DESC];
const values = selectedType === IndexType.Spatial ? [...base, IndexOrder.NONE] : base;
const base = [OrderBy.Asc, OrderBy.Desc];
const values = selectedType === IndexType.Spatial ? [...base, null] : base;
return values.map((order) => ({
label: capitalize(String(order)),
label: order ? capitalize(String(order)) : 'None',
value: order
}));
}
@@ -165,7 +160,8 @@
function prepareIndexForCreation(index: SuggestedIndexSchema, columnMap: Map<string, number>) {
// prepare orders array
const orders = index.orders !== null ? index.fields.map(() => String(index.orders)) : [];
const orders: OrderBy[] =
index.orders !== null ? index.fields.map(() => index.orders as OrderBy) : [];
// prepare lengths array
let lengths: (number | null)[];
@@ -222,7 +218,10 @@
const usedKeys = new Set<string>();
const columnMap: Map<string, number> = new Map(
table.fields
.filter((field) => field.type === 'string' && 'size' in field)
.filter(
(field) =>
(field.type === 'string' || field.type === 'varchar') && 'size' in field
)
.map((field) => [field.key, field['size']])
);
@@ -1,5 +1,5 @@
import { writable } from 'svelte/store';
import { IndexType } from '@appwrite.io/console';
import { IndexType, OrderBy } from '@appwrite.io/console';
import { columnOptions } from '../table-[table]/columns/store';
export type TableColumnSuggestions = {
@@ -30,11 +30,7 @@ export type SuggestedColumnSchema = {
isPlaceholder?: boolean;
};
export enum IndexOrder {
ASC = 'ASC',
DESC = 'DESC',
NONE = null
}
export type IndexOrder = OrderBy | null;
export type SuggestedIndexSchema = {
key: string;
@@ -146,7 +142,7 @@ export function mapSuggestedColumns<T extends ColumnInput>(columns: T[]): Sugges
required: col.required ?? false,
array: false,
default: col.default ?? null,
size: col.type === 'string' ? (col.size ?? undefined) : undefined,
size: col.type === 'string' || col.type === 'varchar' ? (col.size ?? undefined) : undefined,
min:
col.type === 'integer' || col.type === 'double'
? (col.min ?? col.formatOptions?.min ?? undefined)
@@ -17,7 +17,7 @@
import { onMount } from 'svelte';
import { feedback } from '$lib/stores/feedback';
import { cronExpression, type UserBackupPolicy } from '$lib/helpers/backups';
import { ID } from '@appwrite.io/console';
import { BackupServices, ID } from '@appwrite.io/console';
import { showCreateBackup, showCreatePolicy } from './store';
import { getProjectId } from '$lib/helpers/project';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
@@ -68,7 +68,7 @@
const createManualBackup = async () => {
try {
await sdk.forProject(page.params.region, page.params.project).backups.createArchive({
services: ['databases'],
services: [BackupServices.Databases],
resourceId: data.database.$id
});
await invalidate(Dependencies.BACKUPS);
@@ -123,7 +123,7 @@
return sdk.forProject(page.params.region, page.params.project).backups.createPolicy({
policyId: ID.unique(),
services: ['databases'],
services: [BackupServices.Databases],
retention: policy.retained,
schedule: policy.schedule,
name: policy.label,
@@ -15,7 +15,7 @@
import { addNotification } from '$lib/stores/notifications';
import { invalidate } from '$app/navigation';
import { calculateSize } from '$lib/helpers/sizeConvertion';
import { ID, type Models } from '@appwrite.io/console';
import { BackupServices, ID, type Models } from '@appwrite.io/console';
import { columns } from './store';
import { Click, Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { copy } from '$lib/helpers/copy';
@@ -157,7 +157,7 @@
.forProject(page.params.region, page.params.project)
.backups.createRestoration({
archiveId: selectedBackup.$id,
services: ['databases'],
services: [BackupServices.Databases],
newResourceId: newDatabaseInfo.id ?? ID.unique(),
newResourceName: newDatabaseInfo.name
});
@@ -90,7 +90,7 @@
let createIndex: CreateIndex;
let createColumn: CreateColumn;
let selectedOption: Option['name'] = 'String';
let selectedOption: Option['name'] = 'Text';
let createMoreColumns = false;
/* terminology */
@@ -191,7 +191,11 @@
function getMinMaxSizeForColumn(
column: Columns
): { display: string; tooltip?: string } | undefined {
if (column.type === 'string' && !column['format'] && column.key !== '$id') {
if (
(column.type === 'string' || column.type === 'varchar') &&
!column['format'] &&
column.key !== '$id'
) {
const stringColumn = column as Models.ColumnString;
return { display: `Size: ${stringColumn.size}` };
} else if (column.type === 'integer' || column.type === 'double') {
@@ -32,9 +32,9 @@
tableId,
key: originalKey,
required: data.required,
xdefault: data.default,
min: data.min,
max: data.max,
xdefault: data.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
@@ -0,0 +1,99 @@
<script context="module" lang="ts">
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
export async function submitLongtext(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnLongtext>
) {
await sdk
.forProject(page.params.region, page.params.project)
.tablesDB.createLongtextColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data.default,
array: data.array
});
}
export async function updateLongtext(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnLongtext>,
originalKey?: string
) {
await sdk
.forProject(page.params.region, page.params.project)
.tablesDB.updateLongtextColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.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
};
export let editing = false;
export let disabled = false;
let savedDefault = data.default;
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault;
}
}
const {
stores: { required, array },
listen
} = createConservative<Partial<Models.ColumnLongtext>>({
required: false,
array: false,
...data
});
$: listen(data);
$: handleDefaultState($required || $array);
</script>
<Layout.Stack gap="xs" direction="column">
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
Maximum size: 1,073,741,823 characters
</Typography.Text>
</Layout.Stack>
<InputTextarea
id="default"
label="Default"
placeholder="Enter text"
bind:value={data.default}
disabled={data.required || data.array || disabled}
nullable={!data.required && !data.array} />
<RequiredArrayCheckboxes
{editing}
{disabled}
bind:array={data.array}
bind:required={data.required} />
@@ -0,0 +1,99 @@
<script context="module" lang="ts">
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
export async function submitMediumtext(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnMediumtext>
) {
await sdk
.forProject(page.params.region, page.params.project)
.tablesDB.createMediumtextColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data.default,
array: data.array
});
}
export async function updateMediumtext(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnMediumtext>,
originalKey?: string
) {
await sdk
.forProject(page.params.region, page.params.project)
.tablesDB.updateMediumtextColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.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
};
export let editing = false;
export let disabled = false;
let savedDefault = data.default;
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault;
}
}
const {
stores: { required, array },
listen
} = createConservative<Partial<Models.ColumnMediumtext>>({
required: false,
array: false,
...data
});
$: listen(data);
$: handleDefaultState($required || $array);
</script>
<Layout.Stack gap="xs" direction="column">
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
Maximum size: 4,194,303 characters
</Typography.Text>
</Layout.Stack>
<InputTextarea
id="default"
label="Default"
placeholder="Enter text"
bind:value={data.default}
disabled={data.required || data.array || disabled}
nullable={!data.required && !data.array} />
<RequiredArrayCheckboxes
{editing}
{disabled}
bind:array={data.array}
bind:required={data.required} />
@@ -7,6 +7,10 @@ import Float, { submitFloat, updateFloat } from './float.svelte';
import Integer, { submitInteger, updateInteger } from './integer.svelte';
import Ip, { submitIp, updateIp } from './ip.svelte';
import String, { submitString, updateString } from './string.svelte';
import Varchar, { submitVarchar, updateVarchar } from './varchar.svelte';
import Text, { submitText, updateText } from './text.svelte';
import Mediumtext, { submitMediumtext, updateMediumtext } from './mediumtext.svelte';
import Longtext, { submitLongtext, updateLongtext } from './longtext.svelte';
import Url, { submitUrl, updateUrl } from './url.svelte';
import Datetime, { submitDatetime, updateDatetime } from './datetime.svelte';
import Point, { submitPoint, updatePoint } from './point.svelte';
@@ -32,7 +36,10 @@ import type { ComponentType } from 'svelte';
export type Option = {
name:
| 'String'
| 'Text'
| 'Mediumtext'
| 'Longtext'
| 'Varchar'
| 'Integer'
| 'Float'
| 'Boolean'
@@ -44,11 +51,16 @@ export type Option = {
| 'Relationship'
| 'Point'
| 'Line'
| 'Polygon';
| 'Polygon'
| 'String (deprecated)';
sentenceName: string;
component: Component;
type:
| 'string'
| 'text'
| 'mediumtext'
| 'longtext'
| 'varchar'
| 'integer'
| 'double'
| 'boolean'
@@ -75,12 +87,39 @@ export type Option = {
export const columnOptions: Option[] = [
{
name: 'String',
sentenceName: 'string',
component: String,
type: 'string',
create: submitString,
update: updateString,
name: 'Text',
sentenceName: 'text',
component: Text,
type: 'text',
create: submitText,
update: updateText,
icon: IconText
},
{
name: 'Mediumtext',
sentenceName: 'mediumtext',
component: Mediumtext,
type: 'mediumtext',
create: submitMediumtext,
update: updateMediumtext,
icon: IconText
},
{
name: 'Longtext',
sentenceName: 'longtext',
component: Longtext,
type: 'longtext',
create: submitLongtext,
update: updateLongtext,
icon: IconText
},
{
name: 'Varchar',
sentenceName: 'varchar',
component: Varchar,
type: 'varchar',
create: submitVarchar,
update: updateVarchar,
icon: IconText
},
{
@@ -194,6 +233,15 @@ export const columnOptions: Option[] = [
create: submitRelationship,
update: updateRelationship,
icon: IconRelationship
},
{
name: 'String (deprecated)',
sentenceName: 'string',
component: String,
type: 'string',
create: submitString,
update: updateString,
icon: IconText
}
];
@@ -0,0 +1,95 @@
<script context="module" lang="ts">
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
export async function submitText(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnText>
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.createTextColumn({
databaseId,
tableId,
key,
required: data.required,
xdefault: data.default,
array: data.array
});
}
export async function updateText(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnText>,
originalKey?: string
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateTextColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data.default,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.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
};
export let editing = false;
export let disabled = false;
let savedDefault = data.default;
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault;
}
}
const {
stores: { required, array },
listen
} = createConservative<Partial<Models.ColumnText>>({
required: false,
array: false,
...data
});
$: listen(data);
$: handleDefaultState($required || $array);
</script>
<Layout.Stack gap="xs" direction="column">
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
Maximum size: 16,383 characters
</Typography.Text>
</Layout.Stack>
<InputTextarea
id="default"
label="Default"
placeholder="Enter text"
bind:value={data.default}
disabled={data.required || data.array || disabled}
nullable={!data.required && !data.array} />
<RequiredArrayCheckboxes
{editing}
{disabled}
bind:array={data.array}
bind:required={data.required} />
@@ -0,0 +1,175 @@
<script context="module" lang="ts">
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
export async function submitVarchar(
databaseId: string,
tableId: string,
key: string,
data: Partial<Models.ColumnVarchar>
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.createVarcharColumn({
databaseId,
tableId,
key,
size: data.size,
required: data.required,
xdefault: data.default,
array: data.array
});
}
export async function updateVarchar(
databaseId: string,
tableId: string,
data: Partial<Models.ColumnVarchar>,
originalKey?: string
) {
await sdk.forProject(page.params.region, page.params.project).tablesDB.updateVarcharColumn({
databaseId,
tableId,
key: originalKey,
required: data.required,
xdefault: data.default,
size: data.size,
newKey: data.key !== originalKey ? data.key : undefined
});
}
</script>
<script lang="ts">
import { createConservative } from '$lib/helpers/stores';
import RequiredArrayCheckboxes from './requiredArrayCheckboxes.svelte';
import { InputNumber, InputText, InputTextarea } from '$lib/elements/forms';
import { table } from '../store';
import { ProgressBar } from '$lib/components';
import { Layout, Typography, Tooltip, Icon } from '@appwrite.io/pink-svelte';
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
export let data: Partial<Models.ColumnVarchar> = {
required: false,
size: 255,
array: false
};
export let editing = false;
export let disabled = false;
// Local size for reactivity
let size = data.size ?? 255;
$: data.size = size;
function formatBytes(bytes: number): string {
return bytes < 1024 ? `${bytes.toLocaleString()} bytes` : `${(bytes / 1024).toFixed(1)} KB`;
}
function getNewColumnBytes(s: number): number {
return s * 4 + (s <= 255 ? 1 : 2);
}
function getProgressData(bytesUsed: number, bytesMax: number, s: number) {
const newBytes = getNewColumnBytes(s);
const exceeds = bytesUsed + newBytes > bytesMax;
return [
{
size: bytesUsed,
color: 'hsl(var(--color-information-100))',
tooltip: {
title: 'Current usage:',
label: formatBytes(bytesUsed)
}
},
{
size: newBytes,
color: exceeds ? 'hsl(var(--color-danger-100))' : 'hsl(var(--color-success-100))',
tooltip: {
title: 'New column:',
label: formatBytes(newBytes)
}
}
];
}
$: bytesMax = $table?.bytesMax ?? 65535;
$: bytesUsed = $table?.bytesUsed ?? 0;
$: newColumnBytes = getNewColumnBytes(size);
$: exceedsLimit = !editing && bytesUsed + newColumnBytes > bytesMax;
let savedDefault = data.default;
function handleDefaultState(hideDefault: boolean) {
if (hideDefault) {
savedDefault = data.default;
data.default = null;
} else {
data.default = savedDefault;
}
}
const {
stores: { required, array },
listen
} = createConservative<Partial<Models.ColumnVarchar>>({
required: false,
array: false,
...data
});
$: listen(data);
$: handleDefaultState($required || $array);
</script>
<InputNumber
id="size"
label="Size"
required
{disabled}
placeholder="Enter size"
bind:value={size}
min={1}
max={16383} />
{#if !editing}
<Layout.Stack gap="xs">
<Layout.Stack direction="row" gap="xs" alignItems="center">
<Typography.Text variant="m-500" color="--fgcolor-neutral-secondary">
Row size usage
</Typography.Text>
<Tooltip maxWidth="300px">
<Icon icon={IconInfo} size="s" />
<span slot="tooltip">
Database rows have a maximum size of 64 KB. varchar columns use 4 bytes per
character plus a small overhead. text, mediumtext, and longtext columns only use
~20 bytes regardless of content length.
</span>
</Tooltip>
</Layout.Stack>
<ProgressBar
hideEmptySegments
maxSize={bytesMax}
data={getProgressData(bytesUsed, bytesMax, size)} />
{#if exceedsLimit}
<Typography.Text variant="m-400" color="--fgcolor-danger">
This column exceeds the remaining row space. Consider using text, mediumtext, or
longtext instead.
</Typography.Text>
{/if}
</Layout.Stack>
{/if}
<svelte:component
this={size >= 50 ? InputTextarea : InputText}
id="default"
label="Default"
placeholder="Enter string"
maxlength={size}
bind:value={data.default}
disabled={data.required || data.array || disabled}
nullable={!data.required && !data.array} />
<RequiredArrayCheckboxes
{editing}
{disabled}
bind:array={data.array}
bind:required={data.required} />
@@ -23,7 +23,7 @@
columns = $bindable(null),
columnId = $bindable(null),
columnsOrder = $bindable(null),
selectedOption = $bindable('String'),
selectedOption = $bindable('Text'),
createMore = $bindable(false),
onColumnsReorder = null
}: {
@@ -63,8 +63,8 @@
default: null
};
/* default to string */
selectedOption = 'String';
/* default to text */
selectedOption = 'Text';
$option = columnOptions[0];
}
@@ -27,6 +27,10 @@
| Models.ColumnInteger
| Models.ColumnIp
| Models.ColumnString
| Models.ColumnText
| Models.ColumnMediumtext
| Models.ColumnLongtext
| Models.ColumnVarchar
| Models.ColumnDatetime
| Models.ColumnUrl
| Models.ColumnPoint
@@ -35,6 +39,10 @@
const columnsTypeMap = {
string: String,
varchar: String,
text: String,
mediumtext: String,
longtext: String,
integer: Integer,
double: Integer,
boolean: Boolean,
@@ -20,6 +20,10 @@
limited?: boolean;
column:
| Models.ColumnString
| Models.ColumnVarchar
| Models.ColumnText
| Models.ColumnMediumtext
| Models.ColumnLongtext
| Models.ColumnInteger
| Models.ColumnFloat
| Models.ColumnBoolean
@@ -32,8 +36,8 @@
const maxlength = $derived(
limited
? undefined
: column.type === 'string'
? (column as Models.ColumnString).size
: column.type === 'string' || column.type === 'varchar'
? (column as Models.ColumnString | Models.ColumnVarchar).size
: undefined
);
@@ -30,6 +30,12 @@ export function isString(field: Field): field is Models.ColumnString | Models.At
return field?.type === 'string';
}
export function isTextType(field: Field): boolean {
if (!field) return false;
const textTypes = ['string', 'varchar', 'text', 'mediumtext', 'longtext'];
return textTypes.includes(field?.type);
}
export function isSpatialType(
field: Columns | Attributes | Column
): field is
@@ -21,7 +21,10 @@
return {
name: table.name,
tableId: page.params.table,
databaseId: page.params.database
databaseId: page.params.database,
rowSecurity: table.recordSecurity,
enabled: table.enabled,
permissions: table.$permissions
};
});
@@ -8,6 +8,7 @@
import { addNotification } from '$lib/stores/notifications';
import type { Models } from '@appwrite.io/console';
import { columns } from '../store';
import { isTextType } from '../rows/store';
import { preferences } from '$lib/stores/preferences';
import { page } from '$app/state';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
@@ -47,7 +48,7 @@
function getValidColumns() {
return ($columns as Models.ColumnString[]).filter(
(attr) => attr.type === 'string' && !attr?.array
(attr) => isTextType(attr) && !attr?.array
);
}
@@ -14,6 +14,10 @@ export type Columns =
| Models.ColumnInteger
| Models.ColumnIp
| Models.ColumnString
| Models.ColumnText
| Models.ColumnMediumtext
| Models.ColumnLongtext
| Models.ColumnVarchar
| Models.ColumnUrl
| Models.ColumnPoint
| Models.ColumnLine
@@ -11,7 +11,7 @@
import { Fieldset, Layout, Icon, Input, Tag } from '@appwrite.io/pink-svelte';
import { IconGithub, IconPencil } from '@appwrite.io/pink-icons-svelte';
import { onMount } from 'svelte';
import { ID, Runtime, TemplateReferenceType } from '@appwrite.io/console';
import { ID, Runtime, TemplateReferenceType, type Scopes } from '@appwrite.io/console';
import { CustomId } from '$lib/components';
import { getIconFromRuntime } from '$lib/stores/runtimes';
import { regionalConsoleVariables } from '$routes/(console)/project-[region]-[project]/store';
@@ -39,7 +39,7 @@
let specification = $state('');
let runtime = $state<Runtime>();
let installCommand = $state('');
let selectedScopes = $state<string[]>([]);
let selectedScopes = $state<Scopes[]>([]);
let rootDir = $state(data.repository?.rootDirectory);
let variables = $state<Array<{ key: string; value: string; secret: boolean }>>([]);
@@ -15,7 +15,13 @@
import { writable } from 'svelte/store';
import ProductionBranch from '$lib/components/git/productionBranchFieldset.svelte';
import Configuration from './configuration.svelte';
import { ID, Runtime, TemplateReferenceType, type Models } from '@appwrite.io/console';
import {
ID,
Runtime,
TemplateReferenceType,
type Models,
type Scopes
} from '@appwrite.io/console';
import {
ConnectBehaviour,
NewRepository,
@@ -62,7 +68,7 @@
let showConfig = false;
let silentMode = false;
let entrypoint = '';
let selectedScopes: string[] = [];
let selectedScopes: Scopes[] = [];
let execute = true;
let variables: Partial<Models.TemplateVariable>[] = [];
let specification = specificationOptions[0]?.value || '';
@@ -1,15 +1,16 @@
<script lang="ts">
import { scopes } from '$lib/constants';
import { Fieldset, Layout, Selector } from '@appwrite.io/pink-svelte';
import type { Scopes } from '@appwrite.io/console';
export let templateScopes: string[];
export let selectedScopes: string[];
export let selectedScopes: Scopes[];
export let execute = true;
let scopeList = scopes
.filter((s) => templateScopes.includes(s.scope))
.map((s) => {
selectedScopes.push(s.scope);
selectedScopes.push(s.scope as Scopes);
return {
value: s,
checked: true
@@ -37,7 +38,7 @@
on:change={() => {
selectedScopes = scopeList
.filter((s) => s.checked)
.map((s) => s.value.scope);
.map((s) => s.value.scope as Scopes);
}}>
</Selector.Switch>
{/each}
@@ -9,7 +9,7 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { installation, repository, sortBranches } from '$lib/stores/vcs';
import { Runtime, VCSReferenceType, type Models } from '@appwrite.io/console';
import { Runtime, VCSReferenceType, type Models, type Scopes } from '@appwrite.io/console';
import { IconGithub } from '@appwrite.io/pink-icons-svelte';
import { Icon, Input, Layout, Skeleton, Typography } from '@appwrite.io/pink-svelte';
import { func } from '../store';
@@ -87,7 +87,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $installation.$id || undefined,
providerRepositoryId: selectedRepository || undefined,
providerBranch: branch || undefined
@@ -10,7 +10,13 @@
import { sortBranches } from '$lib/stores/vcs';
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
import { LabelCard } from '$lib/components';
import { type Models, ProxyResourceType, Runtime, StatusCode } from '@appwrite.io/console';
import {
type Models,
ProxyResourceType,
Runtime,
StatusCode,
type Scopes
} from '@appwrite.io/console';
import { statusCodeOptions } from '$lib/stores/domains';
import { writable } from 'svelte/store';
import { onMount } from 'svelte';
@@ -140,7 +146,7 @@
logging: data.func.logging || undefined,
entrypoint: data.func.entrypoint,
commands: data.func.commands || undefined,
scopes: data.func.scopes || undefined,
scopes: (data.func.scopes as Scopes[]) || undefined,
installationId: selectedInstallationId,
providerRepositoryId: selectedRepository,
providerBranch: 'main'
@@ -10,7 +10,7 @@
import { createEventDispatcher } from 'svelte';
import { func } from '../store';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
export let show = false;
const functionId = page.params.function;
@@ -35,7 +35,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: '',
providerRepositoryId: '',
providerBranch: '',
@@ -7,7 +7,7 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime, type Models } from '@appwrite.io/console';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
import { page } from '$app/state';
export let func: Models.Function;
@@ -30,7 +30,7 @@
logging: func.logging || undefined,
entrypoint: func.entrypoint || undefined,
commands: buildCommand || undefined,
scopes: func.scopes || undefined,
scopes: (func.scopes as Scopes[]) || undefined,
installationId: func.installationId || undefined,
providerRepositoryId: func.providerRepositoryId || undefined,
providerBranch: func.providerBranch || undefined,
@@ -14,7 +14,7 @@
import { EventModal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
import { IconPlus, IconX } from '@appwrite.io/pink-icons-svelte';
import { Icon, Layout, Link, Table, Typography } from '@appwrite.io/pink-svelte';
@@ -41,7 +41,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -7,7 +7,7 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime, type Models } from '@appwrite.io/console';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
import { Typography } from '@appwrite.io/pink-svelte';
import { page } from '$app/state';
@@ -31,7 +31,7 @@
logging,
entrypoint: func.entrypoint || undefined,
commands: func.commands || undefined,
scopes: func.scopes || undefined,
scopes: (func.scopes as Scopes[]) || undefined,
installationId: func.installationId || undefined,
providerRepositoryId: func.providerRepositoryId || undefined,
providerBranch: func.providerBranch || undefined,
@@ -10,7 +10,7 @@
import { onMount } from 'svelte';
import { func } from '../store';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
const functionId = page.params.function;
let functionName: string = null;
@@ -36,7 +36,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -12,7 +12,7 @@
import { Roles } from '$lib/components/permissions';
import { symmetricDifference } from '$lib/helpers/array';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
import { Link } from '$lib/elements';
const functionId = page.params.function;
@@ -41,7 +41,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -6,7 +6,7 @@
import { Button, Form, InputText } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Runtime, type Models } from '@appwrite.io/console';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
import { onMount } from 'svelte';
import DisconnectRepo from './disconnectRepo.svelte';
import { installation, repository as repositoryStore, sortBranches } from '$lib/stores/vcs';
@@ -86,7 +86,7 @@
logging: func.logging || undefined,
entrypoint: func.entrypoint || undefined,
commands: func.commands || undefined,
scopes: func.scopes || undefined,
scopes: (func.scopes as Scopes[]) || undefined,
installationId: func.installationId || undefined,
providerRepositoryId: func.providerRepositoryId || undefined,
providerBranch: selectedBranch,
@@ -145,7 +145,7 @@
logging: func.logging || undefined,
entrypoint: func.entrypoint,
commands: func.commands || undefined,
scopes: func.scopes || undefined,
scopes: (func.scopes as Scopes[]) || undefined,
installationId: selectedInstallationId,
providerRepositoryId: selectedRepository,
providerBranch: 'main'
@@ -7,7 +7,7 @@
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime, type Models } from '@appwrite.io/console';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
import Link from '$lib/elements/link.svelte';
import { Alert } from '@appwrite.io/pink-svelte';
import { isStarterPlan, getChangePlanUrl } from '$lib/stores/billing';
@@ -39,7 +39,7 @@
logging: func.logging || undefined,
entrypoint: func.entrypoint || undefined,
commands: func.commands || undefined,
scopes: func.scopes || undefined,
scopes: (func.scopes as Scopes[]) || undefined,
installationId: func.installationId || undefined,
providerRepositoryId: func.providerRepositoryId || undefined,
providerBranch: func.providerBranch || undefined,
@@ -10,7 +10,7 @@
import { func } from '../store';
import InputSelect from '$lib/elements/forms/inputSelect.svelte';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime, type Models } from '@appwrite.io/console';
import { Runtime, type Models, type Scopes } from '@appwrite.io/console';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import Link from '$lib/elements/link.svelte';
@@ -40,7 +40,7 @@
logging: $func.logging || undefined,
entrypoint: entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -10,7 +10,7 @@
import { onMount } from 'svelte';
import { func } from '../store';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
import { Link } from '$lib/elements';
import { parseExpression } from 'cron-parser';
@@ -41,7 +41,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -10,16 +10,16 @@
import { onMount } from 'svelte';
import { func } from '../store';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes as ScopesType } from '@appwrite.io/console';
import Scopes from '$routes/(console)/project-[region]-[project]/overview/api-keys/scopes.svelte';
import { symmetricDifference } from '$lib/helpers/array';
import { Link } from '$lib/elements';
const functionId = page.params.function;
let functionScopes: string[] = null;
let functionScopes: ScopesType[] = null;
onMount(async () => {
functionScopes ??= $func.scopes;
functionScopes ??= $func.scopes as ScopesType[];
});
async function updateScopes() {
@@ -39,7 +39,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: functionScopes,
scopes: functionScopes || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -10,7 +10,7 @@
import { onMount } from 'svelte';
import { func } from '../store';
import { isValueOfStringEnum } from '$lib/helpers/types';
import { Runtime } from '@appwrite.io/console';
import { Runtime, type Scopes } from '@appwrite.io/console';
const functionId = page.params.function;
let timeout: number = null;
@@ -36,7 +36,7 @@
logging: $func.logging || undefined,
entrypoint: $func.entrypoint || undefined,
commands: $func.commands || undefined,
scopes: $func.scopes || undefined,
scopes: ($func.scopes as Scopes[]) || undefined,
installationId: $func.installationId || undefined,
providerRepositoryId: $func.providerRepositoryId || undefined,
providerBranch: $func.providerBranch || undefined,
@@ -137,7 +137,8 @@
hideView
hasSearch
hasFilters
bind:view={data.view}
filtersStyle="dropdown"
view={data.view}
analyticsSource="messaging_messages"
searchPlaceholder="Search by description, type, status, or ID">
{#if $canWriteMessages}
@@ -23,8 +23,8 @@
view={View.Table}
hideView
hasFilters
hasCustomFiltersOnly
hasSearch
filtersStyle="dropdown"
analyticsSource="messaging_providers"
searchPlaceholder="Search by name or ID">
{#if $canWriteProviders}
@@ -70,8 +70,8 @@
view={View.Table}
hideView
hasFilters
hasCustomFiltersOnly
hasSearch
filtersStyle="dropdown"
analyticsSource="messaging_topics_filter"
searchPlaceholder="Search by name or ID">
{#if $canWriteTopics}
@@ -14,6 +14,7 @@
import { addNotification } from '$lib/stores/notifications';
import { writable } from 'svelte/store';
import Scopes from '../api-keys/scopes.svelte';
import { type Scopes as ScopesType } from '@appwrite.io/console';
import { page } from '$app/state';
import { copy } from '$lib/helpers/copy';
@@ -23,7 +24,7 @@
let formComponent: Form;
let isSubmitting = writable(false);
let scopes: string[] = [];
let scopes: ScopesType[] = [];
let name = '';
let expire: string | null = null;
@@ -12,7 +12,7 @@
import { project } from '../../store';
import Delete from './delete.svelte';
import UpdateExpirationDate from './updateExpirationDate.svelte';
import type { Models } from '@appwrite.io/console';
import type { Models, Scopes as ScopesType } from '@appwrite.io/console';
import { symmetricDifference } from '$lib/helpers/array';
import Scopes from '../api-keys/scopes.svelte';
import { InteractiveText, Layout, Typography } from '@appwrite.io/pink-svelte';
@@ -22,7 +22,7 @@
export let keyType: 'api' | 'dev' = 'api';
let name: string = null;
let scopes: string[] = null;
let scopes: ScopesType[] = null;
let showDelete = false;
const isApiKey = keyType === 'api';
@@ -33,7 +33,7 @@
onMount(() => {
name ??= key.name;
if (isApiKey) {
scopes ??= (key as Models.Key).scopes;
scopes ??= (key as Models.Key).scopes as ScopesType[];
}
});
@@ -9,7 +9,7 @@
import { sdk } from '$lib/stores/sdk';
import { Alert } from '@appwrite.io/pink-svelte';
import { ExpirationInput } from '$lib/components';
import type { Models } from '@appwrite.io/console';
import type { Models, Scopes } from '@appwrite.io/console';
import { page } from '$app/state';
export let keyType: 'api' | 'dev' = 'api';
@@ -32,7 +32,7 @@
projectId,
keyId: key.$id,
name: key.name,
scopes: (key as Models.Key).scopes,
scopes: (key as Models.Key).scopes as Scopes[],
expire: expiration
});
} else {
@@ -32,8 +32,9 @@
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 type { Scopes } from '@appwrite.io/console';
export let scopes: string[];
export let scopes: Scopes[];
const baseFilteredScopes = allScopes.filter((scope) => {
const val = scope.scope;
@@ -159,7 +160,7 @@
});
}
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): string[] {
function generateSyncedScopes(activeScopesObj: Record<string, boolean>): Scopes[] {
const result = new Set<string>();
Object.entries(activeScopesObj).forEach(([scope, isActive]) => {
@@ -173,7 +174,7 @@
}
});
return Array.from(result);
return Array.from(result) as Scopes[];
}
$: {
@@ -14,7 +14,7 @@
import Details from './details.svelte';
import ExportModal from './exportModal.svelte';
import { readOnly } from '$lib/stores/billing';
import type { Models } from '@appwrite.io/console';
import { Scopes, type Models } from '@appwrite.io/console';
import { canWriteProjects } from '$lib/stores/roles';
import {
IconCloud,
@@ -90,23 +90,23 @@
projectId: $project.$id,
name: `[AUTO-GENERATED] Migration ${new Date().toISOString()}`,
scopes: [
'users.read',
'teams.read',
'databases.read',
'collections.read' /* legacy */,
'attributes.read' /* legacy */,
'indexes.read',
'documents.read' /* legacy */,
'tables.read',
'columns.read',
'rows.read',
'files.read',
'buckets.read',
'functions.read',
'execution.read',
'locale.read',
'avatars.read',
'health.read'
Scopes.UsersRead,
Scopes.TeamsRead,
Scopes.DatabasesRead,
Scopes.CollectionsRead /* legacy */,
Scopes.AttributesRead /* legacy */,
Scopes.IndexesRead,
Scopes.DocumentsRead /* legacy */,
Scopes.TablesRead,
Scopes.ColumnsRead,
Scopes.RowsRead,
Scopes.FilesRead,
Scopes.BucketsRead,
Scopes.FunctionsRead,
Scopes.ExecutionRead,
Scopes.LocaleRead,
Scopes.AvatarsRead,
Scopes.HealthRead
]
});
@@ -10,6 +10,7 @@
import { sdk } from '$lib/stores/sdk';
import { user } from '$lib/stores/user';
import { organization } from '$lib/stores/organization';
import { Scopes } from '@appwrite.io/console';
export let show = false;
@@ -91,23 +92,23 @@
projectId: $project.$id,
name: `[AUTO-GENERATED] Migration ${new Date().toISOString()}`,
scopes: [
'users.read',
'teams.read',
'databases.read',
'collections.read' /* legacy */,
'attributes.read' /* legacy */,
'indexes.read',
'documents.read' /* legacy */,
'tables.read',
'columns.read',
'rows.read',
'files.read',
'buckets.read',
'functions.read',
'execution.read',
'locale.read',
'avatars.read',
'health.read'
Scopes.UsersRead,
Scopes.TeamsRead,
Scopes.DatabasesRead,
Scopes.CollectionsRead /* legacy */,
Scopes.AttributesRead /* legacy */,
Scopes.IndexesRead,
Scopes.DocumentsRead /* legacy */,
Scopes.TablesRead,
Scopes.ColumnsRead,
Scopes.RowsRead,
Scopes.FilesRead,
Scopes.BucketsRead,
Scopes.FunctionsRead,
Scopes.ExecutionRead,
Scopes.LocaleRead,
Scopes.AvatarsRead,
Scopes.HealthRead
]
});
@@ -41,7 +41,7 @@
<ResponsiveContainerHeader
{columns}
hasSearch
bind:view={data.view}
view={data.view}
searchPlaceholder="Search by name or ID">
{#if $canWriteBuckets}
<Tooltip disabled={!isLimited} maxWidth="200px">
@@ -19,15 +19,15 @@
const options = units.map((v) => ({ label: v.name, value: v.name }));
$: selectedUnit = $unit;
const maxValue = function formMaxFileSize() {
return (service * 1000 * 1000) / units.find((unit) => unit.name === selectedUnit).value;
};
$: maxValue = (service * 1000 * 1000) / units.find((unit) => unit.name === selectedUnit).value;
$: step = $unit === 'Gigabytes' ? 0.01 : 1;
function updateMaxSize() {
updateBucket(
bucket,
{
maximumFileSize: $baseValue
maximumFileSize: Math.round($baseValue)
},
{
trackEventName: Submit.BucketUpdateSize,
@@ -80,7 +80,8 @@
disabled={$readOnly && !GRACE_PERIOD_OVERRIDE}
placeholder={bucket.maximumFileSize.toString()}
min={0}
max={isCloud ? maxValue() : Infinity}
{step}
max={isCloud ? maxValue : Infinity}
bind:value={$value} />
<InputSelect required id="bytes" label="Bytes" {options} bind:value={$unit} />
</svelte:fragment>