Merge branch 'feat-pink-v2' into 'remove-jwt'.

This commit is contained in:
Darshan
2025-05-15 19:42:24 +05:30
105 changed files with 717 additions and 744 deletions
+1 -1
View File
@@ -27,7 +27,7 @@
"@appwrite.io/pink-icons": "0.25.0",
"@appwrite.io/pink-icons-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4",
"@appwrite.io/pink-legacy": "^1.0.3",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4",
"@appwrite.io/pink-svelte": "https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4",
"@popperjs/core": "^2.11.8",
"@sentry/sveltekit": "^8.38.0",
"@stripe/stripe-js": "^3.5.0",
+12 -12
View File
@@ -27,8 +27,8 @@ importers:
specifier: ^1.0.3
version: 1.0.3
'@appwrite.io/pink-svelte':
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)
specifier: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4
version: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)
'@popperjs/core':
specifier: ^2.11.8
version: 2.11.8
@@ -258,14 +258,14 @@ packages:
resolution: {tarball: https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@c2f54c6}
version: 1.2.1
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@b39a7c45d4b8ebf03059018d195141c8d2b7c221':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@b39a7c45d4b8ebf03059018d195141c8d2b7c221}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4de190f223a64383f8b35e04624c577513':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4de190f223a64383f8b35e04624c577513}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4}
version: 1.0.0-next.7
peerDependencies:
svelte: ^4.0.0
@@ -279,8 +279,8 @@ packages:
'@appwrite.io/pink-legacy@1.0.3':
resolution: {integrity: sha512-GGde5fmPhs+s6/3aFeMPc/kKADG/gTFkYQSy6oBN8pK0y0XNCLrZZgBv+EBbdhwdtqVEWXa0X85Mv9w7jcIlwQ==}
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4}
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4':
resolution: {tarball: https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4}
version: 1.0.0-next.85
peerDependencies:
react-dom: ^18.0.0
@@ -3627,11 +3627,11 @@ snapshots:
'@appwrite.io/console@https://pkg.pr.new/appwrite/appwrite/@appwrite.io/console@c2f54c6': {}
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@b39a7c45d4b8ebf03059018d195141c8d2b7c221(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4de190f223a64383f8b35e04624c577513(svelte@5.25.3)':
'@appwrite.io/pink-icons-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4(svelte@5.25.3)':
dependencies:
svelte: 5.25.3
@@ -3644,9 +3644,9 @@ snapshots:
'@appwrite.io/pink-icons': 1.0.0
the-new-css-reset: 1.11.3
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@d68e5e4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)':
'@appwrite.io/pink-svelte@https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-svelte@b39a7c4(react-dom@18.3.1(react@18.3.1))(svelte@5.25.3)':
dependencies:
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@d68e5e4de190f223a64383f8b35e04624c577513(svelte@5.25.3)
'@appwrite.io/pink-icons-svelte': https://pkg.pr.new/appwrite/pink/@appwrite.io/pink-icons-svelte@b39a7c45d4b8ebf03059018d195141c8d2b7c221(svelte@5.25.3)
'@floating-ui/dom': 1.6.13
'@melt-ui/pp': 0.3.2(@melt-ui/svelte@0.86.6(svelte@5.25.3))(svelte@5.25.3)
'@melt-ui/svelte': 0.86.6(svelte@5.25.3)
@@ -1,6 +1,4 @@
import { goto } from '$app/navigation';
import { project } from '$routes/(console)/project-[region]-[project]/store';
import { get } from 'svelte/store';
import type { Searcher } from '../commands';
import { sdk } from '$lib/stores/sdk';
import { base } from '$app/paths';
@@ -11,14 +11,7 @@
} from '$lib/elements/forms';
import type { Writable } from 'svelte/store';
import Modal from '../modal.svelte';
import {
addFilter,
generateTag,
operators,
queries,
ValidOperators,
type TagValue
} from './store';
import { generateTag, operators, ValidOperators, type TagValue } from './store';
import type { Column } from '$lib/helpers/types';
import { TagList } from '.';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
@@ -1,7 +1,7 @@
<script lang="ts">
import type { Column } from '$lib/helpers/types';
import { type Writable } from 'svelte/store';
import { CustomFilters, FiltersBottomSheet } from '$lib/components/filters';
import { CustomFilters } from '$lib/components/filters';
import { addFilterAndApply, type FilterData } from './quickFilters';
import { parsedTags } from './setFilters';
import Menu from '../menu/menu.svelte';
@@ -1,5 +1,5 @@
<script lang="ts">
import { Button, InputSelect, InputText } from '$lib/elements/forms';
import { Button, InputText } from '$lib/elements/forms';
import { Fieldset, Input, Layout, Selector, Skeleton } from '@appwrite.io/pink-svelte';
import SelectRootModal from './selectRootModal.svelte';
import { sdk } from '$lib/stores/sdk';
+3 -2
View File
@@ -11,7 +11,6 @@
Typography,
Icon,
Avatar,
Skeleton,
Button as PinkButton
} from '@appwrite.io/pink-svelte';
import { IconLockClosed, IconPlus } from '@appwrite.io/pink-icons-svelte';
@@ -43,7 +42,7 @@
} = $props();
let search = $state('');
let selectedInstallation = null;
let selectedInstallation: string = null;
let isLoadingRepositories = $state(null);
async function loadInstallations() {
@@ -111,6 +110,8 @@
$repositories.search = search;
$repositories.installationId = installationId;
}
$inspect(selectedRepository);
</script>
{#if hasInstallations}
+1 -1
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { Card, Tooltip, Icon } from '@appwrite.io/pink-svelte';
import { Card, Tooltip } from '@appwrite.io/pink-svelte';
import type { HTMLAttributes } from 'svelte/elements';
import type { BaseCardProps } from './card.svelte';
import type { ComponentType } from 'svelte';
+17 -17
View File
@@ -12,7 +12,7 @@
$: totalPages = Math.ceil(total / limit);
$: currentPage = Math.floor(offset / limit + 1);
$: pages = pagination(currentPage, totalPages);
// $: pages = pagination(currentPage, totalPages);
function handleOptionClick(e: CustomEvent) {
if (currentPage !== e.detail) {
@@ -35,22 +35,22 @@
}
}
function pagination(page: number, total: number) {
const pagesShown = 5;
const start = Math.max(
1,
Math.min(page - Math.floor((pagesShown - 3) / 2), total - pagesShown + 2)
);
const end = Math.min(
total,
Math.max(page + Math.floor((pagesShown - 2) / 2), pagesShown - 1)
);
return [
...(start > 2 ? [1, '...'] : start > 1 ? [1] : []),
...Array.from({ length: end + 1 - start }, (_, i) => i + start),
...(end < total - 1 ? ['...', total] : end < total ? [total] : [])
];
}
// function pagination(page: number, total: number) {
// const pagesShown = 5;
// const start = Math.max(
// 1,
// Math.min(page - Math.floor((pagesShown - 3) / 2), total - pagesShown + 2)
// );
// const end = Math.min(
// total,
// Math.max(page + Math.floor((pagesShown - 2) / 2), pagesShown - 1)
// );
// return [
// ...(start > 2 ? [1, '...'] : start > 1 ? [1] : []),
// ...Array.from({ length: end + 1 - start }, (_, i) => i + start),
// ...(end < total - 1 ? ['...', total] : end < total ? [total] : [])
// ];
// }
</script>
{#if !hidePages}
+2 -2
View File
@@ -12,6 +12,7 @@
hasLimit = false,
name = 'items',
gap = 's',
offset = $bindable(0),
children
}: {
items: T[];
@@ -23,13 +24,12 @@
gap?:
| ('none' | 'xxxs' | 'xxs' | 'xs' | 's' | 'm' | 'l' | 'xl' | 'xxl' | 'xxxl')
| undefined;
offset?: number;
children: Snippet<[T[], number]>;
} = $props();
let total = $derived(items.length);
let offset = $state(0);
let paginatedItems = $derived(items.slice(offset, offset + limit));
</script>
+1 -1
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/state';
import { goto } from '$app/navigation';
import { onMount, onDestroy } from 'svelte';
import { onDestroy } from 'svelte';
import { trackEvent } from '$lib/actions/analytics';
import { Icon, Input } from '@appwrite.io/pink-svelte';
import { IconSearch, IconX } from '@appwrite.io/pink-icons-svelte';
+44 -40
View File
@@ -40,6 +40,7 @@
import type { HTMLAttributes } from 'svelte/elements';
import type { NavbarProject } from '$lib/components/navbar.svelte';
import { SHOW_INIT_FEATURES } from '$lib/system';
type $$Props = HTMLAttributes<HTMLElement> & {
state?: 'closed' | 'open' | 'icons';
@@ -190,46 +191,49 @@
<span slot="tooltip">{projectOption.name}</span>
</Tooltip>
{/each}
<div class="only-mobile divider">
<Divider />
</div>
<div class="products-label-container">
<span class="products-label" class:hidden={state === 'icons'}>Deploy</span>
<span class="products-label-indicator" class:hidden={state !== 'icons'}
></span>
</div>
{@const deployProjectOptions = projectOptions.filter(
(projectOption) => projectOption.category === 'deploy'
)}
{#each deployProjectOptions as projectOption}
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/${projectOption.slug}`}
class="link"
class:active={page.url.pathname.includes(projectOption.slug)}
on:click={() => {
trackEvent(`click_menu_${projectOption.slug}`);
sideBarIsOpen = false;
}}>
<span class="link-icon">
<Icon icon={projectOption.icon} size="s" />
</span>
<span
class:no-text={state === 'icons'}
class:has-text={state === 'open'}
class="link-text">
{projectOption.name}
{#if projectOption?.badge}
<Badge
variant="secondary"
content={projectOption.badge}
size="xs" />
{/if}
</span>
</a>
<span slot="tooltip">{projectOption.name}</span>
</Tooltip>
{/each}
{#if SHOW_INIT_FEATURES}
<div class="only-mobile divider">
<Divider />
</div>
<div class="products-label-container">
<span class="products-label" class:hidden={state === 'icons'}
>Deploy</span>
<span class="products-label-indicator" class:hidden={state !== 'icons'}
></span>
</div>
{@const deployProjectOptions = projectOptions.filter(
(projectOption) => projectOption.category === 'deploy'
)}
{#each deployProjectOptions as projectOption}
<Tooltip placement="right" disabled={state !== 'icons'}>
<a
href={`/console/project-${project.region}-${project.$id}/${projectOption.slug}`}
class="link"
class:active={page.url.pathname.includes(projectOption.slug)}
on:click={() => {
trackEvent(`click_menu_${projectOption.slug}`);
sideBarIsOpen = false;
}}>
<span class="link-icon">
<Icon icon={projectOption.icon} size="s" />
</span>
<span
class:no-text={state === 'icons'}
class:has-text={state === 'open'}
class="link-text">
{projectOption.name}
{#if projectOption?.badge}
<Badge
variant="secondary"
content={projectOption.badge}
size="xs" />
{/if}
</span>
</a>
<span slot="tooltip">{projectOption.name}</span>
</Tooltip>
{/each}
{/if}
<div class="only-mobile divider">
<Divider />
</div>
@@ -1,7 +1,7 @@
<script lang="ts">
import { DropList } from '$lib/components';
import { SelectSearchCheckbox } from '..';
import { Icon, Layout, Tag } from '@appwrite.io/pink-svelte';
import { Icon, Tag } from '@appwrite.io/pink-svelte';
import { IconChevronDown, IconChevronUp } from '@appwrite.io/pink-icons-svelte';
type Option = {
-1
View File
@@ -135,7 +135,6 @@
<svelte:fragment slot="list">
<slot name="tooltip" {limit} {tier} {title} {upgradeMethod} {hasUsageFees}>
{#if hasProjectLimitation}
{@const count = limit > 1 ? serviceId : serviceId.replace('s', '')}
<p class="text">
You are limited to {limit}
{title.toLocaleLowerCase()} per project on the {tier} plan.
-1
View File
@@ -8,7 +8,6 @@
import type { Coupon } from '$lib/sdk/billing';
import { app } from '$lib/stores/app';
import type { Campaign } from '$lib/stores/campaigns';
import { getApiEndpoint } from '$lib/stores/sdk';
import { Typography, Layout, Avatar } from '@appwrite.io/pink-svelte';
import { getCampaignImageUrl } from '$routes/(public)/card/helpers';
+1 -1
View File
@@ -1,6 +1,6 @@
import { afterNavigate, goto } from '$app/navigation';
import { page } from '$app/state';
import { get, writable } from 'svelte/store';
import { writable } from 'svelte/store';
export function createPersistentPagination(limit: number) {
const url = page.url;
-1
View File
@@ -1,6 +1,5 @@
import { Client, type Models, Sites, Storage } from '@appwrite.io/console';
import { writable } from 'svelte/store';
import { createAdminClient } from '$lib/stores/sdk';
import { getApiEndpoint } from '$lib/stores/sdk';
import { page } from '$app/state';
+2 -1
View File
@@ -11,7 +11,7 @@ export const VARS = {
APPWRITE_ENDPOINT: env.PUBLIC_APPWRITE_ENDPOINT ?? undefined,
GROWTH_ENDPOINT: env.PUBLIC_GROWTH_ENDPOINT ?? undefined,
PUBLIC_STRIPE_KEY: env.PUBLIC_STRIPE_KEY ?? undefined,
APPWRITE_MULTI_REGION: env.PUBLIC_APPWRITE_MULTI_REGION ?? false
APPWRITE_MULTI_REGION: env.PUBLIC_APPWRITE_MULTI_REGION === 'true'
};
export const ENV = {
@@ -28,3 +28,4 @@ export const isDev = ENV.DEV;
export const isProd = ENV.PROD;
export const hasStripePublicKey = !!VARS.PUBLIC_STRIPE_KEY;
export const GRACE_PERIOD_OVERRIDE = false;
export const SHOW_INIT_FEATURES = !ENV.PROD || env.PUBLIC_APPWRITE_INIT_FEATURES === 'true';
@@ -3,7 +3,7 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { CreditsApplied, EstimatedTotal, SelectPaymentMethod } from '$lib/components/billing';
import { CreditsApplied, SelectPaymentMethod } from '$lib/components/billing';
import { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, InputSelect, InputTags, InputText } from '$lib/elements/forms';
import { toLocaleDate } from '$lib/helpers/date';
@@ -3,18 +3,12 @@
import { base } from '$app/paths';
import { page } from '$app/state';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import {
PlanComparisonBox,
PlanSelection,
SelectPaymentMethod,
SelectPlan
} from '$lib/components/billing';
import { PlanComparisonBox, PlanSelection, SelectPaymentMethod } from '$lib/components/billing';
import ValidateCreditModal from '$lib/components/billing/validateCreditModal.svelte';
import { BillingPlan, Dependencies } from '$lib/constants';
import { Button, Form, InputTags, InputText } from '$lib/elements/forms';
import { Wizard } from '$lib/layout';
import type { Coupon, PaymentList } from '$lib/sdk/billing';
import type { Coupon } from '$lib/sdk/billing';
import { isOrganization, tierToPlan } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
import { type OrganizationError, type Organization } from '$lib/stores/organization';
@@ -37,7 +31,6 @@
let formComponent: Form;
let isSubmitting = writable(false);
let methods: PaymentList;
let name: string;
let billingPlan: BillingPlan = BillingPlan.FREE;
let paymentMethodId: string;
@@ -95,11 +88,6 @@
}
});
async function loadPaymentMethods() {
methods = await sdk.forConsole.billing.listPaymentMethods();
paymentMethodId = methods.paymentMethods.find((method) => !!method?.last4)?.$id ?? null;
}
async function validate(organizationId: string, invites: string[]) {
try {
const org = await sdk.forConsole.billing.validateOrganization(organizationId, invites);
@@ -6,7 +6,7 @@ import { isOrganization, tierToPlan } from '$lib/stores/billing';
import { ID } from '@appwrite.io/console';
import { BillingPlan } from '$lib/constants';
import { redirect } from '@sveltejs/kit';
import { base } from '$service-worker';
import { base } from '$app/paths';
export const load: PageLoad = async ({ parent }) => {
const { organizations } = await parent();
@@ -10,7 +10,6 @@
import { onMount } from 'svelte';
import { trackEvent } from '$lib/actions/analytics';
import { selectedInvoice, showRetryModal } from './store';
import { organization } from '$lib/stores/organization';
import {
ActionMenu,
Badge,
@@ -35,7 +35,6 @@
new Date($organization?.billingStartDate).getTime() - today.getTime() > 0 &&
$plansInfo.get($organization.billingPlan)?.trialDays;
const extraUsage = currentInvoice ? currentInvoice.amount - currentPlan?.price : 0;
const extraAddons = currentInvoice ? currentInvoice.usage?.length : 0;
function usageNameToLabel(name: string): string {
switch (name) {
@@ -66,20 +66,23 @@
if (setAsDefault) {
await sdk.forConsole.billing.setDefaultPaymentMethod(paymentMethodId);
}
const { clientSecret } = await sdk.forConsole.billing.retryPayment(
const { clientSecret, status } = await sdk.forConsole.billing.retryPayment(
$organization.$id,
invoice.$id,
paymentMethodId
);
await confirmPayment(
$organization.$id,
clientSecret,
paymentMethodId ? paymentMethodId : $organization.paymentMethodId,
`${base}/organization-${$organization.$id}/billing?type=validate-invoice&invoice=${invoice.$id}`
);
if (status !== 'succeeded' && status !== 'cancelled') {
// probably still pending, confirm via stripe!
await confirmPayment(
$organization.$id,
clientSecret,
paymentMethodId ? paymentMethodId : $organization.paymentMethodId,
`${base}/organization-${$organization.$id}/billing?type=validate-invoice&invoice=${invoice.$id}`
);
await sdk.forConsole.billing.updateInvoiceStatus($organization.$id, invoice.$id);
await sdk.forConsole.billing.updateInvoiceStatus($organization.$id, invoice.$id);
}
invalidate(Dependencies.ORGANIZATION);
invalidate(Dependencies.INVOICES);
@@ -10,8 +10,8 @@
import { Button, Form, InputSelect, InputTags, InputTextarea } from '$lib/elements/forms';
import { formatCurrency } from '$lib/helpers/numbers.js';
import { Wizard } from '$lib/layout';
import { type Coupon, type PaymentList } from '$lib/sdk/billing';
import { isOrganization, plansInfo, type Tier, tierToPlan } from '$lib/stores/billing';
import { type Coupon } from '$lib/sdk/billing';
import { isOrganization, plansInfo, tierToPlan } from '$lib/stores/billing';
import { addNotification } from '$lib/stores/notifications';
import { currentPlan, organization } from '$lib/stores/organization';
import { sdk } from '$lib/stores/sdk';
@@ -4,7 +4,7 @@ import { getLimit, getPage, getQuery, getSearch, pageToOffset } from '$lib/helpe
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
import { queries, queryParamToMap } from '$lib/components/filters';
export const load = async ({ depends, url, route, params }) => {
export const load = async ({ depends, url, route }) => {
depends(Dependencies.DOMAINS);
const page = getPage(url);
const limit = getLimit(url, route, PAGE_LIMIT);
@@ -11,7 +11,6 @@
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
import { addNotification } from '$lib/stores/notifications';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import { recordTypes } from './store';
import { Dependencies } from '$lib/constants';
@@ -11,12 +11,13 @@
import { members, newMemberModal, organization } from '$lib/stores/organization';
import {
canSeeBilling,
canSeeDomains,
canSeeProjects,
canSeeTeams,
isBilling,
isOwner
} from '$lib/stores/roles';
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
import { GRACE_PERIOD_OVERRIDE, isCloud, SHOW_INIT_FEATURES } from '$lib/system';
import { IconGithub, IconPlus } from '@appwrite.io/pink-icons-svelte';
import { Icon, Tooltip, Typography, Layout, Badge } from '@appwrite.io/pink-svelte';
@@ -44,8 +45,7 @@
href: `${path}/domains`,
event: 'domains',
title: 'Domains',
disabled: !isCloud
// disabled: !(isCloud && $canSeeDomains) //TODO: enable this before release
disabled: !isCloud || !$canSeeDomains || !SHOW_INIT_FEATURES
},
{
href: `${path}/members`,
@@ -16,7 +16,7 @@
import { Table, Tabs, Alert } from '@appwrite.io/pink-svelte';
import DeleteOrganizationEstimation from './deleteOrganizationEstimation.svelte';
import { onMount } from 'svelte';
import type { EstimationDeleteOrganization, Invoice, InvoiceList } from '$lib/sdk/billing';
import type { EstimationDeleteOrganization, InvoiceList } from '$lib/sdk/billing';
export let showDelete = false;
export let invoices: InvoiceList;
@@ -42,7 +42,7 @@
<Table.Header.Cell {root} />
{/if}
</svelte:fragment>
{#each invoices as invoice, i}
{#each invoices as invoice (invoice.$id)}
{@const status = invoice.status}
<Table.Row.Base {root}>
<Table.Cell column="date" {root}>
@@ -41,9 +41,9 @@
return `${base}/project-${region}-${projectId}/settings/usage`;
}
function getProjectName(projectId: string): string {
return data.projects[projectId]?.name ?? 'Unknown';
}
// function getProjectName(projectId: string): string {
// return data.projects[projectId]?.name ?? 'Unknown';
// }
function groupByProject(
metric: Metric | undefined,
@@ -8,7 +8,7 @@
import deepEqual from 'deep-equal';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import type { EmailTemplateLocale, EmailTemplateType } from '@appwrite.io/console';
import { Card, Icon, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { Icon, Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { IconInfo } from '@appwrite.io/pink-icons-svelte';
export let loading = false;
@@ -1,6 +1,5 @@
<script lang="ts">
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Modal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
@@ -24,6 +24,7 @@
import { base } from '$app/paths';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import type { Models } from '@appwrite.io/console';
import { SHOW_INIT_FEATURES } from '$lib/system';
export let data: PageData;
@@ -99,13 +100,15 @@
analyticsSource="database_documents" />
<Layout.Stack direction="row" alignItems="center" justifyContent="flex-end">
<ViewSelector view={data.view} {columns} hideView />
<Button
secondary
event={Click.DatabaseImportCsv}
disabled={!(hasAttributes && hasValidAttributes)}
on:click={() => (showImportCSV = true)}>
Import CSV
</Button>
{#if SHOW_INIT_FEATURES}
<Button
secondary
event={Click.DatabaseImportCsv}
disabled={!(hasAttributes && hasValidAttributes)}
on:click={() => (showImportCSV = true)}>
Import CSV
</Button>
{/if}
{#if !$isSmallViewport}
<Button
disabled={!(hasAttributes && hasValidAttributes)}
@@ -2,7 +2,6 @@
import type { Models } from '@appwrite.io/console';
import { sdk } from '$lib/stores/sdk';
import { page } from '$app/state';
import { get } from 'svelte/store';
export async function submitDatetime(
databaseId: string,
@@ -1,6 +1,5 @@
<script context="module" lang="ts">
import type { Models } from '@appwrite.io/console';
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -10,7 +10,6 @@
export let show = false;
export let data: Partial<Models.Document>[];
export let selectedRelationship: Models.AttributeRelationship = null;
const projectId = page.params.project;
const databaseId = page.params.database;
const limit = 10;
@@ -28,7 +28,6 @@
export let data: PageData;
const projectId = page.params.project;
const databaseId = page.params.database;
const collectionId = page.params.collection;
let showRelationships = false;
@@ -1,5 +1,4 @@
<script context="module" lang="ts">
import { get } from 'svelte/store';
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
@@ -16,7 +16,6 @@
import Confirm from '$lib/components/confirm.svelte';
export let data: PageData;
const projectId = page.params.project;
const databaseId = page.params.database;
let selectedRows: string[] = [];
@@ -3,13 +3,12 @@
import { Modal } from '$lib/components';
import { onMount } from 'svelte';
import { page } from '$app/state';
import { func } from '../store';
import { Alert, Code, Layout, Tabs } from '@appwrite.io/pink-svelte';
import { Link } from '$lib/elements';
export let show = false;
let lang = 'js';
// let lang = 'js';
let codeSnippets = {};
let os = 'unknown';
let category = 'Unix';
@@ -17,8 +16,8 @@
const functionId = page.params.function;
onMount(() => {
lang = setLanguage($func.runtime);
codeSnippets = setCodeSnippets(lang);
// lang = setLanguage($func.runtime);
codeSnippets = setCodeSnippets();
os = navigator['userAgentData']?.platform || navigator?.platform || 'unknown';
if (os.includes('Win')) {
@@ -30,29 +29,29 @@
}
});
function setLanguage(runtime: string) {
if (runtime.includes('node') || runtime.includes('deno')) {
return 'js';
} else if (runtime.includes('php')) {
return 'php';
} else if (runtime.includes('python')) {
return 'py';
} else if (runtime.includes('dart')) {
return 'dart';
} else if (runtime.includes('dotnet')) {
return 'cs';
} else if (runtime.includes('ruby')) {
return 'rb';
} else if (runtime.includes('swift')) {
return 'swift';
} else if (runtime.includes('kotlin')) {
return 'kt';
} else if (runtime.includes('java')) {
return 'java';
}
}
// function setLanguage(runtime: string) {
// if (runtime.includes('node') || runtime.includes('deno')) {
// return 'js';
// } else if (runtime.includes('php')) {
// return 'php';
// } else if (runtime.includes('python')) {
// return 'py';
// } else if (runtime.includes('dart')) {
// return 'dart';
// } else if (runtime.includes('dotnet')) {
// return 'cs';
// } else if (runtime.includes('ruby')) {
// return 'rb';
// } else if (runtime.includes('swift')) {
// return 'swift';
// } else if (runtime.includes('kotlin')) {
// return 'kt';
// } else if (runtime.includes('java')) {
// return 'java';
// }
// }
function setCodeSnippets(lang: string) {
function setCodeSnippets() {
return {
Unix: {
code: `appwrite client --projectId="${page.params.project}" && \\
@@ -1,7 +1,7 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/state';
import { PaginationWithLimit, ViewSelector, EmptyFilter } from '$lib/components';
import { PaginationWithLimit, EmptyFilter } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { columns, deploymentList, func } from './store';
import { Container, ResponsiveContainerHeader } from '$lib/layout';
@@ -9,7 +9,6 @@
import { GRACE_PERIOD_OVERRIDE, isCloud } from '$lib/system';
import { readOnly } from '$lib/stores/billing';
import Table from './table.svelte';
import { View } from '$lib/helpers/load';
import DeploymentCard from './(components)/deploymentCard.svelte';
import RedeployModal from './(modals)/redeployModal.svelte';
import { canWriteFunctions } from '$lib/stores/roles';
@@ -23,7 +22,6 @@
} from '@appwrite.io/pink-icons-svelte';
import { app } from '$lib/stores/app';
import CreateActionMenu from './(components)/createActionMenu.svelte';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
import { Menu } from '$lib/components/menu';
import DownloadActionMenuItem from './(components)/downloadActionMenuItem.svelte';
@@ -18,7 +18,6 @@
Icon,
Layout,
Logs,
Popover,
Spinner,
Typography
} from '@appwrite.io/pink-svelte';
@@ -26,8 +25,7 @@
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
import { timer } from '$lib/actions/timer';
import { app } from '$lib/stores/app';
import { getOutputDownload, getSourceDownload, showCreateDeployment } from '../store';
import { IconDotsHorizontal, IconRefresh, IconTerminal } from '@appwrite.io/pink-icons-svelte';
import { IconDotsHorizontal, IconRefresh } from '@appwrite.io/pink-icons-svelte';
import { Menu } from '$lib/components/menu';
import { canWriteFunctions } from '$lib/stores/roles';
import { Click, trackEvent } from '$lib/actions/analytics';
@@ -46,124 +46,133 @@
$: selectedLog = logs?.find((log) => log.$id === selectedLogId);
$: isFirstLog = logs.findIndex((log) => log.$id === selectedLogId) === 0;
$: isLastLog = logs.findIndex((log) => log.$id === selectedLogId) === logs.length - 1;
let sheet: HTMLDivElement;
function handleClickOutside(event: MouseEvent) {
if (sheet && !sheet.contains(event.target as Node)) {
open = false;
}
}
</script>
<svelte:window onclick={() => (open = false)} />
<svelte:window onclick={handleClickOutside} />
<Sheet bind:open closeOnBlur={false}>
<div slot="header" style:width="100%">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" gap="m" alignItems="center">
<Typography.Text variant="m-400">Execution ID</Typography.Text>
<Copy value={selectedLog?.$id}>
<Tag size="s" variant="code">{selectedLog?.$id}</Tag>
</Copy>
</Layout.Stack>
<Layout.Stack direction="row" gap="xs" alignItems="center" inline>
<Button icon text size="xs" on:click={previousLog} disabled={isFirstLog}>
<Icon icon={IconChevronUp} />
</Button>
<Button icon text size="xs" on:click={nextLog} disabled={isLastLog}>
<Icon icon={IconChevronDown} />
</Button>
</Layout.Stack>
</Layout.Stack>
</div>
{#if selectedLog}
<Layout.Stack gap="xl">
<Accordion title="Details" open>
<Layout.Stack gap="xxxl">
<Layout.Stack gap="xxxl" direction="row" wrap="wrap">
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Method
</Typography.Text>
<Typography.Text variant="m-400">
{selectedLog.requestMethod}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Status code
</Typography.Text>
<span>
<Badge
content={selectedLog.responseStatusCode.toString()}
variant="secondary"
type={selectedLog?.responseStatusCode >= 400
? 'error'
: selectedLog.responseStatusCode === 0
? undefined
: 'success'} />
</span>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Status
</Typography.Text>
<Tooltip
disabled={!selectedLog?.scheduledAt ||
selectedLog.status !== 'scheduled'}
maxWidth="400px">
<div>
<Status
status={logStatusConverter(selectedLog.status)}
label={capitalize(selectedLog.status)}>
</Status>
</div>
<span slot="tooltip">
{`Scheduled to execute on ${toLocaleDateTime(selectedLog.scheduledAt)}`}
</span>
</Tooltip>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Triggered by
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(selectedLog.trigger)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Duration
</Typography.Text>
<Typography.Text variant="m-400">
{calculateTime(selectedLog.duration)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Created
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(timeFromNow(selectedLog.$createdAt))}
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
<Layout.Stack gap="xs" inline alignItems="flex-start">
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Path
</Typography.Text>
<div>
<InteractiveText
text={selectedLog.requestPath}
variant="copy-code"
isVisible />
</div>
</Layout.Stack>
<div style="position: absolute; top: 0; " bind:this={sheet}>
<Sheet bind:open closeOnBlur={false}>
<div slot="header" style:width="100%">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" gap="m" alignItems="center">
<Typography.Text variant="m-400">Execution ID</Typography.Text>
<Copy value={selectedLog?.$id}>
<Tag size="s" variant="code">{selectedLog?.$id}</Tag>
</Copy>
</Layout.Stack>
</Accordion>
<Accordion title="Request" open>
<LogsRequest {selectedLog} />
</Accordion>
<Accordion title="Response" open hideDivider>
<LogsResponse {selectedLog} product="function" {logging} />
</Accordion>
</Layout.Stack>
{/if}
</Sheet>
<Layout.Stack direction="row" gap="xs" alignItems="center" inline>
<Button icon text size="xs" on:click={previousLog} disabled={isFirstLog}>
<Icon icon={IconChevronUp} />
</Button>
<Button icon text size="xs" on:click={nextLog} disabled={isLastLog}>
<Icon icon={IconChevronDown} />
</Button>
</Layout.Stack>
</Layout.Stack>
</div>
{#if selectedLog}
<Layout.Stack gap="xl">
<Accordion title="Details" open>
<Layout.Stack gap="xxxl">
<Layout.Stack gap="xxxl" direction="row" wrap="wrap">
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Method
</Typography.Text>
<Typography.Text variant="m-400">
{selectedLog.requestMethod}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Status code
</Typography.Text>
<span>
<Badge
content={selectedLog.responseStatusCode.toString()}
variant="secondary"
type={selectedLog?.responseStatusCode >= 400
? 'error'
: selectedLog.responseStatusCode === 0
? undefined
: 'success'} />
</span>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Status
</Typography.Text>
<Tooltip
disabled={!selectedLog?.scheduledAt ||
selectedLog.status !== 'scheduled'}
maxWidth="400px">
<div>
<Status
status={logStatusConverter(selectedLog.status)}
label={capitalize(selectedLog.status)}>
</Status>
</div>
<span slot="tooltip">
{`Scheduled to execute on ${toLocaleDateTime(selectedLog.scheduledAt)}`}
</span>
</Tooltip>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Triggered by
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(selectedLog.trigger)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Duration
</Typography.Text>
<Typography.Text variant="m-400">
{calculateTime(selectedLog.duration)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Created
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(timeFromNow(selectedLog.$createdAt))}
</Typography.Text>
</Layout.Stack>
</Layout.Stack>
<Layout.Stack gap="xs" inline alignItems="flex-start">
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Path
</Typography.Text>
<div>
<InteractiveText
text={selectedLog.requestPath}
variant="copy-code"
isVisible />
</div>
</Layout.Stack>
</Layout.Stack>
</Accordion>
<Accordion title="Request" open>
<LogsRequest {selectedLog} />
</Accordion>
<Accordion title="Response" open hideDivider>
<LogsResponse {selectedLog} product="function" {logging} />
</Accordion>
</Layout.Stack>
{/if}
</Sheet>
</div>
@@ -3,7 +3,7 @@
import { toLocaleDateTime } from '$lib/helpers/date';
import type { Column } from '$lib/helpers/types';
import type { Models } from '@appwrite.io/console';
import { Badge, Layout, Status, Table, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { Badge, Status, Table, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import Sheet from './sheet.svelte';
import { capitalize } from '$lib/helpers/string';
import { formatTimeDetailed } from '$lib/helpers/timeConversion';
@@ -2,9 +2,9 @@
import { base } from '$app/paths';
import { CardGrid, SvgIcon } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { timeFromNow, toLocaleDateTime } from '$lib/helpers/date';
import { toLocaleDateTime } from '$lib/helpers/date';
import { project } from '$routes/(console)/project-[region]-[project]/store';
import { Layout, Tooltip, Typography } from '@appwrite.io/pink-svelte';
import { Layout, Typography } from '@appwrite.io/pink-svelte';
import { func } from '../store';
import { capitalize } from '$lib/helpers/string';
</script>
@@ -27,6 +27,8 @@
export let data;
let offset = 0;
function applyFilter(filter: string, value: string, event: CustomEvent) {
const target = new URL(page.url);
if (event?.detail) {
@@ -45,6 +47,7 @@
previous.forEach((n) => target.searchParams.append(filter, n));
}
target.searchParams.delete('page');
offset = 0;
goto(target.toString());
}
@@ -140,7 +143,7 @@
</Layout.Stack>
<Layout.Stack gap="l">
{#if data.templates?.length > 0}
<Paginator items={data.templates} limit={12} hidePages={false} hasLimit>
<Paginator items={data.templates} limit={12} hidePages={false} hasLimit bind:offset>
{#snippet children(paginatedItems: typeof data.templates)}
<Layout.Grid columns={2} columnsXL={3} columnsXS={1}>
{#each paginatedItems as template}
@@ -1,9 +1,9 @@
import { Dependencies, PAGE_LIMIT } from '$lib/constants';
import { Dependencies } from '$lib/constants';
import { getSearch } from '$lib/helpers/load';
import { sdk } from '$lib/stores/sdk';
import type { PageLoad } from './$types';
export const load: PageLoad = async ({ url, route, depends, parent, params }) => {
export const load: PageLoad = async ({ url, depends, parent, params }) => {
depends(Dependencies.FUNCTIONS);
const search = getSearch(url);
@@ -6,7 +6,6 @@
import { Cover } from '$lib/layout';
import { Typography } from '@appwrite.io/pink-svelte';
const projectId = page.params.project;
const path = `${base}/project-${page.params.region}-${page.params.project}/messaging`;
const tabs = [
{
@@ -1,6 +1,5 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { Modal } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
@@ -2,7 +2,6 @@
import {
InputDomain,
InputEmail,
InputFile,
InputSelect,
InputPhone,
InputSwitch,
@@ -5,15 +5,6 @@
import { providerType, provider, providerParams } from './store';
import SettingsFormList from '../settingsFormList.svelte';
import { MessagingProviderType } from '@appwrite.io/console';
import {
Collapsible,
CollapsibleItem,
ClickableList,
ClickableListItem
} from '$lib/components';
import { getProviderText } from '../../helper';
let files: Record<string, FileList> = {};
$: inputs = providers[$providerType].providers[$provider].configure;
@@ -25,7 +25,7 @@
import { writable } from 'svelte/store';
import type { Column } from '$lib/helpers/types';
import { base } from '$app/paths';
import { Icon, Layout, Typography } from '@appwrite.io/pink-svelte';
import { Icon, Layout } from '@appwrite.io/pink-svelte';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
export let data;
@@ -22,7 +22,7 @@
let formComponent: Form;
let isSubmitting = writable(false);
let scopes = [];
let scopes: string[] = [];
let name = '',
expire = '';
@@ -20,8 +20,8 @@
export let key: Models.DevKey | Models.Key;
export let keyType: 'api' | 'dev' = 'api';
let name = null;
let scopes = null;
let name: string = null;
let scopes: string[] = null;
let showDelete = false;
const isApiKey = keyType === 'api';
@@ -184,7 +184,7 @@
<CardGrid>
<svelte:fragment slot="title">Delete {label} key</svelte:fragment>
The Dev key will be permanently deleted. This action is irreversible.
The {label} key will be permanently deleted. This action is irreversible.
<svelte:fragment slot="aside">
<Box>
<div class="u-flex u-gap-16">
@@ -34,8 +34,8 @@
const isExpiring = key.expire && diffDays(new Date(), new Date(key.expire)) < 14;
return {
status: isExpired ? 'error' : isExpiring ? 'warning' : null,
message: isExpired ? 'Expired' : isExpiring ? 'Expires soon' : null
message: isExpired ? 'Expired' : isExpiring ? 'Expires soon' : null,
status: isExpired ? (isApiKey ? 'error' : 'warning') : isExpiring ? 'warning' : null
};
}
@@ -48,7 +48,7 @@
if (isApiKey)
return 'Use API keys to authenticate your app’s requests in production, granting secure access to live data and services.';
else
return 'Dev keys allow bypassing rate limits and accessing more detailed error messages while in development.';
return 'Dev keys allow bypassing rate limits and CORS errors in your development environment.';
}
const columns = isApiKey ? $keyColumns : $devKeyColumns;
@@ -75,20 +75,12 @@
<svelte:fragment slot="title">Expiration date</svelte:fragment>
Set a date after which your {label} key will expire.
<svelte:fragment slot="aside">
{#if isExpired}
{#if isExpired || isExpiring}
<Alert.Inline
status="error"
status={isExpired && isApiKey ? 'error' : 'warning'}
on:dismiss={() => (alertsDismissed = true)}
title="Your {label} key has expired">
For security reasons, we recommend you delete your expired key and create a new
one.
</Alert.Inline>
{:else if isExpiring}
<Alert.Inline
status="warning"
on:dismiss={() => (alertsDismissed = true)}
title="Your {label} key is about to expire">
Update the expiration date to keep the key active
title={`Your ${label} key ${isExpired ? 'has expired' : 'is about to expire'}`}>
Update the expiration date to keep the key active.
</Alert.Inline>
{/if}
<ExpirationInput bind:value={expiration} {expiryOptions} />
@@ -28,6 +28,7 @@
import { writable, type Writable } from 'svelte/store';
import { IconPlus } from '@appwrite.io/pink-icons-svelte';
import { isSmallViewport } from '$lib/stores/viewport';
import { SHOW_INIT_FEATURES } from '$lib/system';
let period: UsagePeriods = '30d';
$: path = `${base}/project-${page.params.region}-${page.params.project}/overview`;
@@ -232,11 +233,13 @@
event="keys"
href={`${path}/keys`}
selected={isTabSelected('keys')}>API keys</Tab>
<Tab
noscroll
event="keys"
href={`${path}/dev-keys`}
selected={isTabSelected('dev-keys')}>Dev keys</Tab>
{#if SHOW_INIT_FEATURES}
<Tab
noscroll
event="keys"
href={`${path}/dev-keys`}
selected={isTabSelected('dev-keys')}>Dev keys</Tab>
{/if}
</Tabs>
{#if $action}
<svelte:component this={$action} />
@@ -58,7 +58,7 @@
<Form onSubmit={create} isModal>
<Modal title="Create dev key" bind:open={$showDevKeysCreateModal}>
<span slot="description">
Test your app without rate limits and more detailed error messages.
Bypass Appwrite rate limits and CORS errors in your development environment.
</span>
<Layout.Stack>
@@ -19,7 +19,7 @@
<Typography.Title color="--fgcolor-neutral-primary" size="xl">
{$project?.name}
</Typography.Title>
<Layout.Stack alignItems="center">
<Layout.Stack alignItems="center" inline>
<Id value={$project.$id}>{$project.$id}</Id>
<RegionEndpoint />
</Layout.Stack>
@@ -1,7 +1,6 @@
<script lang="ts">
import { base } from '$app/paths';
import { page } from '$app/state';
import { Id } from '$lib/components';
import { Cover, CoverTitle } from '$lib/layout';
import { key } from './store';
@@ -1,5 +1,5 @@
<script lang="ts">
import type { Component, ComponentType } from 'svelte';
import type { ComponentType } from 'svelte';
// export let icon: Component;
export let icon: ComponentType; //TODO: switch to Component after pink 2 uses svelte 5
@@ -41,13 +41,11 @@
total={data.rules.total} />
{:else if data?.search}
<EmptySearch hidePages bind:search={data.search} target="domains" hidePagination>
<svelte:fragment slot="actions">
<Button
secondary
on:click={() => {
data.search = '';
}}>Clear search</Button>
</svelte:fragment>
<Button
secondary
on:click={() => {
data.search = '';
}}>Clear search</Button>
</EmptySearch>
{:else}
<Card.Base padding="none">
@@ -5,7 +5,6 @@
import { Wizard } from '$lib/layout';
import { addNotification } from '$lib/stores/notifications';
import { sdk } from '$lib/stores/sdk';
import { Fieldset } from '@appwrite.io/pink-svelte';
import { goto, invalidate } from '$app/navigation';
import { Dependencies } from '$lib/constants';
import { writable } from 'svelte/store';
@@ -17,7 +16,6 @@
let formComponent: Form;
let isSubmitting = $state(writable(false));
let showConnectRepo = $state(false);
let domainName = $state('');
onMount(() => {
@@ -3,7 +3,7 @@ import { Dependencies } from '$lib/constants.js';
import { isCloud } from '$lib/system';
import type { Domain, DomainsList } from '$lib/sdk/domains.js';
export const load = async ({ parent, depends, params }) => {
export const load = async ({ depends, params }) => {
depends(Dependencies.DOMAINS);
let domain: Domain;
@@ -22,8 +22,6 @@
let showFailed = false;
let selectedWebhook: Models.Webhook;
const projectId = page.params.project;
$: $updateCommandGroupRanks({ webhooks: 20, domains: 10 });
</script>
@@ -16,7 +16,7 @@
let showExitModal = false;
let name = '',
events = [],
events: string[] = [],
url = '',
security = true,
httpUser = '',
@@ -8,7 +8,7 @@
import { protocol } from '$routes/(console)/store';
import type { Models } from '@appwrite.io/console';
import { IconDuplicate } from '@appwrite.io/pink-icons-svelte';
import { Icon, Image, Input, Layout, Tooltip } from '@appwrite.io/pink-svelte';
import { Icon, Image, Layout, Tooltip } from '@appwrite.io/pink-svelte';
let {
show = $bindable(),
@@ -29,7 +29,6 @@
export let variant: 'primary' | 'secondary' = 'primary';
let show = false;
const siteUrl = proxyRuleList.total > 0 ? proxyRuleList.rules[0].domain : undefined;
$: totalSize = humanFileSize(deployment?.totalSize ?? 0);
@@ -135,9 +135,6 @@
</Accordion>
<Accordion title="Environment variables" badge="Optional" hideDivider>
<svelte:fragment slot="title">
Environment variables <Badge content="Optional" variant="secondary" />
</svelte:fragment>
<Layout.Stack gap="xl">
Set up environment variables to securely manage keys and settings for your
project.
@@ -13,6 +13,8 @@
export let data;
let offset = 0;
function applyFilter(filter: string, value: string, event: CustomEvent) {
const target = new URL(page.url);
if (event?.detail) {
@@ -31,6 +33,7 @@
previous.forEach((n) => target.searchParams.append(filter, n));
}
target.searchParams.delete('page');
offset = 0;
goto(target.toString());
}
@@ -47,6 +50,8 @@
}
url.searchParams.delete('page');
offset = 0;
goto(url.toString(), { keepFocus: true });
}, 250);
@@ -125,7 +130,13 @@
</svelte:fragment>
<Layout.Stack gap="l">
{#if data.templates?.length > 0}
<Paginator items={data.templates} limit={12} hidePages={false} hasLimit gap="m">
<Paginator
items={data.templates}
limit={12}
hidePages={false}
hasLimit
gap="m"
bind:offset>
{#snippet children(paginatedItems: typeof data.templates)}
<Layout.Grid columns={3} columnsXS={2} columnsXXS={1}>
{#each paginatedItems as template (template.name)}
@@ -24,7 +24,7 @@
import Details from '../../details.svelte';
import Configuration from './configuration.svelte';
import Aside from '../../aside.svelte';
import { Adapter, BuildRuntime, Framework, ID } from '@appwrite.io/console';
import { Adapter, BuildRuntime, Framework, ID, type Models } from '@appwrite.io/console';
import {
ConnectBehaviour,
NewRepository,
@@ -56,12 +56,12 @@
let rootDir = './';
let connectBehaviour: 'now' | 'later' = 'now';
let repositoryBehaviour: 'new' | 'existing' = 'new';
let repositoryName = undefined;
let repositoryName: string = undefined;
let repositoryPrivate = true;
let selectedInstallationId = '';
let selectedRepository = '';
let showSiteConfig = false;
let variables = [];
let variables: Partial<Models.TemplateVariable>[] = [];
let silentMode = false;
onMount(async () => {
@@ -6,7 +6,6 @@
import { Cover } from '$lib/layout';
import { Typography } from '@appwrite.io/pink-svelte';
$: projectId = page.params.project;
$: path = `${base}/project-${page.params.region}-${page.params.project}/sites`;
$: tabs = [
{
@@ -12,7 +12,7 @@
import { IconList, IconPlus, IconSearch } from '@appwrite.io/pink-icons-svelte';
onMount(() => {
let previousStatus = null;
let previousStatus: string = null;
return sdk.forConsole.client.subscribe<Models.Deployment>('console', (message) => {
if (previousStatus === message.payload.status) {
return;
@@ -1,5 +1,5 @@
<script lang="ts">
import { PaginationWithLimit, ViewSelector, EmptyFilter, Empty } from '$lib/components';
import { PaginationWithLimit, EmptyFilter, Empty } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Container, ResponsiveContainerHeader } from '$lib/layout';
import { Adapter, BuildRuntime, Framework, type Models } from '@appwrite.io/console';
@@ -18,7 +18,7 @@
];
let range = SiteUsageRange.ThirtyDays;
let metrics = [
let metrics: { id: string; value: string | null; description: string }[] = [
{
id: 'buildsTotal',
value: null,
@@ -39,13 +39,11 @@
total={data.domains.total} />
{:else if data?.search}
<EmptySearch hidePages bind:search={data.search} target="domains" hidePagination>
<svelte:fragment slot="actions">
<Button
secondary
on:click={() => {
data.search = '';
}}>Clear search</Button>
</svelte:fragment>
<Button
secondary
on:click={() => {
data.search = '';
}}>Clear search</Button>
</EmptySearch>
{:else}
<Card.Base padding="none">
@@ -18,7 +18,7 @@
let domain = baseDomain;
let domainStatus: 'complete' | 'failed' | 'pending' = 'complete';
let error = null;
let error: string = null;
async function onSubmit() {
try {
await sdk
@@ -6,7 +6,6 @@
import { Cover, CoverTitle } from '$lib/layout';
import { site } from './store';
$: projectId = page.params.project;
$: path = `${base}/project-${page.params.region}-${page.params.project}/sites/site-${page.params.site}`;
$: tabs = [
{
@@ -1,15 +1,13 @@
<script lang="ts">
import { invalidate } from '$app/navigation';
import { EmptyFilter, PaginationWithLimit, ViewSelector } from '$lib/components';
import { EmptyFilter, PaginationWithLimit } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button } from '$lib/elements/forms';
import { Container, ResponsiveContainerHeader } from '$lib/layout';
import { sdk } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import Table from './table.svelte';
import { ParsedTagList, QuickFilters } from '$lib/components/filters';
import { Card, Empty, Layout } from '@appwrite.io/pink-svelte';
import { View } from '$lib/helpers/load';
import { Card, Empty } from '@appwrite.io/pink-svelte';
import { columns } from './store';
export let data;
@@ -42,94 +42,111 @@
$: selectedLog = logs?.find((log) => log.$id === selectedLogId);
$: isFirstLog = logs.findIndex((log) => log.$id === selectedLogId) === 0;
$: isLastLog = logs.findIndex((log) => log.$id === selectedLogId) === logs.length - 1;
let sheet: HTMLDivElement;
function handleClickOutside(event: MouseEvent) {
if (sheet && !sheet.contains(event.target as Node)) {
open = false;
}
}
</script>
<svelte:window onclick={() => (open = false)} />
<svelte:window onclick={handleClickOutside} />
<Sheet bind:open closeOnBlur={false}>
<div slot="header" style:width="100%">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" gap="m" alignItems="center">
<Typography.Text variant="m-400">Log ID</Typography.Text>
<Copy value={selectedLog?.$id}>
<Tag size="s" variant="code">{selectedLog?.$id}</Tag>
</Copy>
</Layout.Stack>
<Layout.Stack direction="row" gap="xs" alignItems="center" inline>
<Button icon text size="xs" on:click={previousLog} disabled={isFirstLog}>
<Icon icon={IconChevronUp} />
</Button>
<Button icon text size="xs" on:click={nextLog} disabled={isLastLog}>
<Icon icon={IconChevronDown} />
</Button>
</Layout.Stack>
</Layout.Stack>
</div>
{#if selectedLog}
<Layout.Stack gap="xl">
<Accordion title="Details" open>
<Layout.Stack gap="xl">
<Layout.Stack direction="row">
<Layout.Grid columnsXS={2} columns={4} gap="xxxl" rowGap="xl">
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Method
</Typography.Text>
<Typography.Text variant="m-400">
{selectedLog.requestMethod}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Status code
</Typography.Text>
<span>
<Badge
content={selectedLog.responseStatusCode.toString()}
variant="secondary"
type={selectedLog?.responseStatusCode >= 400
? 'error'
: 'success'} />
</span>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Duration
</Typography.Text>
<Typography.Text variant="m-400">
{calculateTime(selectedLog.duration)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Created
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(timeFromNow(selectedLog.$createdAt))}
</Typography.Text>
</Layout.Stack>
</Layout.Grid>
</Layout.Stack>
<Layout.Stack gap="xs" inline alignItems="flex-start">
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Path
</Typography.Text>
<div>
<InteractiveText
text={selectedLog.requestPath}
variant="copy-code"
isVisible />
</div>
</Layout.Stack>
<div style="position: absolute; top: 0; " bind:this={sheet}>
<Sheet bind:open closeOnBlur={false}>
<div slot="header" style:width="100%">
<Layout.Stack direction="row" justifyContent="space-between" alignItems="center">
<Layout.Stack direction="row" gap="m" alignItems="center">
<Typography.Text variant="m-400">Log ID</Typography.Text>
<Copy value={selectedLog?.$id}>
<Tag size="s" variant="code">{selectedLog?.$id}</Tag>
</Copy>
</Layout.Stack>
</Accordion>
<Accordion title="Request" open>
<LogsRequest {selectedLog} />
</Accordion>
<Accordion title="Response" open hideDivider>
<LogsResponse {selectedLog} logging={$site?.logging} product="site" />
</Accordion>
</Layout.Stack>
{/if}
</Sheet>
<Layout.Stack direction="row" gap="xs" alignItems="center" inline>
<Button icon text size="xs" on:click={previousLog} disabled={isFirstLog}>
<Icon icon={IconChevronUp} />
</Button>
<Button icon text size="xs" on:click={nextLog} disabled={isLastLog}>
<Icon icon={IconChevronDown} />
</Button>
</Layout.Stack>
</Layout.Stack>
</div>
{#if selectedLog}
<Layout.Stack gap="xl">
<Accordion title="Details" open>
<Layout.Stack gap="xl">
<Layout.Stack direction="row">
<Layout.Grid columnsXS={2} columns={4} gap="xxxl" rowGap="xl">
<Layout.Stack gap="xs" inline>
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-tertiary">
Method
</Typography.Text>
<Typography.Text variant="m-400">
{selectedLog.requestMethod}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-tertiary">
Status code
</Typography.Text>
<span>
<Badge
content={selectedLog.responseStatusCode.toString()}
variant="secondary"
type={selectedLog?.responseStatusCode >= 400
? 'error'
: 'success'} />
</span>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-tertiary">
Duration
</Typography.Text>
<Typography.Text variant="m-400">
{calculateTime(selectedLog.duration)}
</Typography.Text>
</Layout.Stack>
<Layout.Stack gap="xs" inline>
<Typography.Text
variant="m-400"
color="--fgcolor-neutral-tertiary">
Created
</Typography.Text>
<Typography.Text variant="m-400">
{capitalize(timeFromNow(selectedLog.$createdAt))}
</Typography.Text>
</Layout.Stack>
</Layout.Grid>
</Layout.Stack>
<Layout.Stack gap="xs" inline alignItems="flex-start">
<Typography.Text variant="m-400" color="--fgcolor-neutral-tertiary">
Path
</Typography.Text>
<div>
<InteractiveText
text={selectedLog.requestPath}
variant="copy-code"
isVisible />
</div>
</Layout.Stack>
</Layout.Stack>
</Accordion>
<Accordion title="Request" open>
<LogsRequest {selectedLog} />
</Accordion>
<Accordion title="Response" open hideDivider>
<LogsResponse {selectedLog} logging={$site?.logging} product="site" />
</Accordion>
</Layout.Stack>
{/if}
</Sheet>
</div>
@@ -2,7 +2,7 @@
import { Id } from '$lib/components';
import type { Column } from '$lib/helpers/types';
import type { Models } from '@appwrite.io/console';
import { Badge, Layout, Table, Typography } from '@appwrite.io/pink-svelte';
import { Badge, Table, Typography } from '@appwrite.io/pink-svelte';
import Sheet from './sheet.svelte';
import DualTimeView from '$lib/components/dualTimeView.svelte';
@@ -1,6 +1,5 @@
import { sdk } from '$lib/stores/sdk';
import { Dependencies } from '$lib/constants';
import { page } from '$app/state';
export const load = async ({ params, depends, parent }) => {
depends(Dependencies.VARIABLES);
@@ -15,19 +15,76 @@
import { getFrameworkIcon } from '$lib/stores/sites';
import { page } from '$app/state';
export let site: Models.Site;
export let frameworks: Models.Framework[];
let selectedFramework: Models.Framework = frameworks.find(
(framework) => framework.key === site.framework
let {
site,
frameworks
}: {
site: Models.Site;
frameworks: Models.Framework[];
} = $props();
let frameworkKey = $state(site.framework);
let installCommand = $state(site?.installCommand);
let buildCommand = $state(site?.buildCommand);
let outputDirectory = $state(site?.outputDirectory);
let fallback = $state(site?.fallbackFile);
let adapter: Adapter = $state(site.adapter as Adapter);
let selectedFramework: Models.Framework = $derived(
frameworks.find((framework) => framework.key === site.framework)
);
let frameworkKey = site.framework;
let adapter: Adapter = site.adapter as Adapter;
let installCommand = site?.installCommand;
let buildCommand = site?.buildCommand;
let outputDirectory = site?.outputDirectory;
let fallback = site?.fallbackFile;
let isButtonDisabled = true;
let showFallback = site.adapter === Adapter.Static;
let showFallback = $derived(adapter === Adapter.Static);
let hasChanges = $derived(
installCommand === site?.installCommand &&
buildCommand === site?.buildCommand &&
outputDirectory === site?.outputDirectory &&
selectedFramework?.key === site?.framework &&
fallback === (site?.fallbackFile || undefined) &&
adapter === site?.adapter
);
let frameworkAdapterData = $derived(
selectedFramework.adapters.find((a) => a.key === adapter) ?? selectedFramework.adapters[0]
);
$effect(() => {
if (selectedFramework?.key !== site.framework) {
//Update adapter
const singleAdapter = selectedFramework?.adapters?.length <= 1;
if (singleAdapter) {
const hasSSR = selectedFramework?.adapters?.some((a) => a?.key === Adapter.Ssr);
const hasStatic = selectedFramework?.adapters?.some(
(a) => a?.key === Adapter.Static
);
if (!hasSSR) {
adapter = Adapter.Static;
} else if (!hasStatic) {
adapter = Adapter.Ssr;
}
}
//Update values
const data = selectedFramework.adapters.find((a) => a.key === adapter);
installCommand = data.installCommand;
buildCommand = data.buildCommand;
outputDirectory = data.outputDirectory;
adapter = selectedFramework.adapters[0].key as Adapter;
} else {
adapter = site.adapter as Adapter;
installCommand = site?.installCommand ?? frameworkAdapterData.installCommand;
buildCommand = site?.buildCommand ?? frameworkAdapterData.buildCommand;
outputDirectory = site?.outputDirectory ?? frameworkAdapterData.outputDirectory;
}
});
$effect(() => {
if (adapter === Adapter.Static) {
if (!fallback) {
fallback ||= selectedFramework.adapters.find(
(a) => a.key === Adapter.Static
).fallbackFile;
}
}
});
async function update() {
let adptr = selectedFramework.adapters.find((a) => a.key === adapter);
@@ -50,12 +107,13 @@
outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
(adptr?.key as Adapter) || undefined,
fallback || undefined,
adptr?.key === 'static' ? fallback || undefined : undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined
site.providerRootDirectory || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -72,41 +130,16 @@
}
}
$: if (
installCommand === site?.installCommand &&
buildCommand === site?.buildCommand &&
outputDirectory === site?.outputDirectory &&
selectedFramework?.key === site?.framework &&
fallback === (site?.fallbackFile || undefined) &&
adapter === site?.adapter
) {
isButtonDisabled = true;
} else {
// console.log(adapter, site?.adapter);
isButtonDisabled = false;
}
function reset(type: 'installCommand' | 'buildCommand' | 'outputDirectory') {
const data = selectedFramework.adapters.find((a) => a.key === adapter);
$: frameworkAdapterData = selectedFramework.adapters.find((a) => a.key === adapter);
$: if (adapter === Adapter.Static) {
showFallback = true;
fallback ||= selectedFramework.adapters.find((a) => a.key === Adapter.Static).fallbackFile;
} else {
showFallback = false;
fallback = undefined;
}
$: if (fallback === '') {
fallback = null;
}
$: hasSSR = selectedFramework?.adapters?.some((a) => a?.key === Adapter.Ssr);
$: hasStatic = selectedFramework?.adapters?.some((a) => a?.key === Adapter.Static);
$: if (selectedFramework?.adapters?.length <= 1 || !hasSSR) {
adapter = Adapter.Static;
}
$: if (selectedFramework?.adapters?.length <= 1 || !hasStatic) {
adapter = Adapter.Ssr;
if (type === 'installCommand') {
installCommand = site?.installCommand ?? data.installCommand;
} else if (type === 'buildCommand') {
buildCommand = site?.buildCommand ?? data.buildCommand;
} else if (type === 'outputDirectory') {
outputDirectory = site?.outputDirectory ?? data.outputDirectory;
}
}
</script>
@@ -199,16 +232,10 @@
id="installCommand"
label="Install command"
bind:value={installCommand}
placeholder={frameworkAdapterData?.installCommand} />
placeholder={frameworkAdapterData?.installCommand ||
'Enter install command'} />
<Button
secondary
size="s"
on:click={() =>
(installCommand =
site?.installCommand ??
selectedFramework.adapters[site.adapter]
?.defaultInstallCommand)}>
<Button secondary size="s" on:click={() => reset('installCommand')}>
Reset
</Button>
</Layout.Stack>
@@ -217,15 +244,9 @@
id="buildCommand"
label="Build command"
bind:value={buildCommand}
placeholder={frameworkAdapterData?.buildCommand} />
<Button
secondary
size="s"
on:click={() =>
(buildCommand =
site?.buildCommand ??
selectedFramework.adapters[site.adapter]
?.defaultBuildCommand)}>
placeholder={frameworkAdapterData?.buildCommand ||
'Enter build command'} />
<Button secondary size="s" on:click={() => reset('buildCommand')}>
Reset
</Button>
</Layout.Stack>
@@ -234,15 +255,9 @@
id="outputDirectory"
label="Output directory"
bind:value={outputDirectory}
placeholder={frameworkAdapterData?.outputDirectory} />
<Button
secondary
size="s"
on:click={() =>
(outputDirectory =
site?.outputDirectory ??
selectedFramework.adapters[site.adapter]
?.defaultOutputDirectory)}>
placeholder={frameworkAdapterData?.outputDirectory ||
'Enter output directory'} />
<Button secondary size="s" on:click={() => reset('outputDirectory')}>
Reset
</Button>
</Layout.Stack>
@@ -267,7 +282,7 @@
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={isButtonDisabled} submit>Update</Button>
<Button disabled={hasChanges} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
@@ -21,20 +21,21 @@
site.$id,
site.name,
site.framework as Framework,
site.enabled || undefined,
site?.enabled || undefined,
logging,
site.timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.timeout || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
site?.providerBranch || undefined,
site?.providerSilentMode || undefined,
site?.providerRootDirectory || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -25,20 +25,21 @@
site.$id,
siteName,
site.framework as Framework,
site.enabled || undefined,
site.logging || undefined,
site.timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.enabled || undefined,
site?.logging || undefined,
site?.timeout || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
site?.providerBranch || undefined,
site?.providerSilentMode || undefined,
site?.providerRootDirectory || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -68,20 +68,21 @@
site.$id,
site.name,
site?.framework as Framework,
site.enabled || undefined,
site.logging || undefined,
site.timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.enabled || undefined,
site?.logging || undefined,
site?.timeout || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
selectedBranch || undefined,
silentMode || undefined,
selectedDir || undefined
selectedDir || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -26,20 +26,20 @@
site.$id,
site.name,
site.framework as Framework,
site.enabled || undefined,
site.logging || undefined,
site.timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.enabled || undefined,
site?.logging || undefined,
site?.timeout || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined,
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
site?.providerBranch || undefined,
site?.providerSilentMode || undefined,
site?.providerRootDirectory || undefined,
specification || undefined
);
await invalidate(Dependencies.FUNCTION);
@@ -31,20 +31,21 @@
site.$id,
site.name,
site?.framework as Framework,
site.enabled || undefined,
site.logging || undefined,
site.timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.enabled || undefined,
site?.logging || undefined,
site?.timeout || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
site?.providerBranch || undefined,
site?.providerSilentMode || undefined,
site?.providerRootDirectory || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -24,20 +24,21 @@
site.$id,
site.name,
site?.framework as Framework,
site.enabled || undefined,
site.logging || undefined,
site?.enabled || undefined,
site?.logging || undefined,
timeout || undefined,
site.installCommand || undefined,
site.buildCommand || undefined,
site.outputDirectory || undefined,
site?.installCommand || undefined,
site?.buildCommand || undefined,
site?.outputDirectory || undefined,
(site?.buildRuntime as BuildRuntime) || undefined,
site.adapter as Adapter,
site.fallbackFile || undefined,
site.installationId || undefined,
site.providerRepositoryId || undefined,
site.providerBranch || undefined,
site.providerSilentMode || undefined,
site.providerRootDirectory || undefined
site?.adapter as Adapter,
site?.fallbackFile || undefined,
site?.installationId || undefined,
site?.providerRepositoryId || undefined,
site?.providerBranch || undefined,
site?.providerSilentMode || undefined,
site?.providerRootDirectory || undefined,
site?.specification || undefined
);
await invalidate(Dependencies.SITE);
addNotification({
@@ -29,7 +29,6 @@
let showDelete = false;
let selectedFile: Models.File = null;
const projectId = page.params.project;
const bucketId = page.params.bucket;
function getPreview(fileId: string) {
@@ -21,15 +21,12 @@
Alert,
Badge,
Button as PinkButton,
InteractiveText,
Layout,
Icon,
Popover,
Table,
Typography
} from '@appwrite.io/pink-svelte';
import {
IconChevronRight,
IconDotsHorizontal,
IconDuplicate,
IconKey,
@@ -42,6 +39,7 @@
import { type Models, Permission, Role } from '@appwrite.io/console';
import { isSmallViewport } from '$lib/stores/viewport';
import { Menu } from '$lib/components/menu';
import { SHOW_INIT_FEATURES } from '$lib/system';
let showFileAlert = true;
@@ -212,7 +210,10 @@
</div>
<svelte:fragment slot="aside">
<div>
<p><span class="u-bold">Filename:</span> {$file.name}</p>
<p>
<span class="u-bold">Filename:</span>
<span class="u-trim">{$file.name}</span>
</p>
<p><span class="u-bold">MIME type:</span> {$file.mimeType}</p>
<p><span class="u-bold">Size:</span> {calculateSize($file.sizeOriginal)}</p>
<p><span class="u-bold">Created:</span> {toLocaleDate($file.$createdAt)}</p>
@@ -231,51 +232,55 @@
</svelte:fragment>
</CardGrid>
<CardGrid>
<svelte:fragment slot="title">File tokens</svelte:fragment>
Use tokens to provide public access to the file.
<svelte:fragment slot="aside">
{#if $tokens.total}
<Layout.Stack
justifyContent="flex-end"
direction={$isSmallViewport ? 'column' : 'row'}>
<Button secondary on:click={() => (showManageToken = true)}>
<Icon size="s" icon={IconPlus} />
<span class="text">Create token</span>
</Button>
</Layout.Stack>
{#if SHOW_INIT_FEATURES}
<CardGrid>
<svelte:fragment slot="title">File tokens</svelte:fragment>
Use tokens to provide public access to the file.
<svelte:fragment slot="aside">
{#if $tokens.total}
<Layout.Stack
justifyContent="flex-end"
direction={$isSmallViewport ? 'column' : 'row'}>
<Button secondary on:click={() => (showManageToken = true)}>
<Icon size="s" icon={IconPlus} />
<span class="text">Create token</span>
</Button>
</Layout.Stack>
<Table.Root columns={$columns} let:root>
<svelte:fragment slot="header" let:root>
{#each $columns as { id, title } (id)}
<Table.Header.Cell column={id} {root}>{title}</Table.Header.Cell>
{/each}
</svelte:fragment>
<Table.Root columns={$columns} let:root>
<svelte:fragment slot="header" let:root>
{#each $columns as { id, title } (id)}
<Table.Header.Cell column={id} {root}
>{title}</Table.Header.Cell>
{/each}
</svelte:fragment>
{#each $tokens.tokens as token}
<Table.Row.Base {root}>
<Table.Cell column="created" {root}
>{toLocaleDate(token.$createdAt)}</Table.Cell>
{#each $tokens.tokens as token}
<Table.Row.Base {root}>
<Table.Cell column="created" {root}
>{toLocaleDate(token.$createdAt)}</Table.Cell>
<Table.Cell column="expire" {root}>
<Layout.Stack gap="s" direction="row">
{@const expiration = getExpiryDetails(token)}
{token.expire ? cleanFormattedDate(token.expire) : 'Never'}
<Table.Cell column="expire" {root}>
<Layout.Stack gap="s" direction="row">
{@const expiration = getExpiryDetails(token)}
{token.expire
? cleanFormattedDate(token.expire)
: 'Never'}
{#if expiration.status}
<Badge
size="s"
variant="secondary"
type={expiration.status}
content={expiration.message} />
{/if}
</Layout.Stack>
</Table.Cell>
{#if expiration.status}
<Badge
size="s"
variant="secondary"
type={expiration.status}
content={expiration.message} />
{/if}
</Layout.Stack>
</Table.Cell>
<Table.Cell column="last_accessed" {root}
>{token.accessedAt
? cleanFormattedDate(token.accessedAt, true)
: 'Never'}</Table.Cell>
<Table.Cell column="last_accessed" {root}
>{token.accessedAt
? cleanFormattedDate(token.accessedAt, true)
: 'Never'}</Table.Cell>
<Table.Cell column="actions" {root}>
<Layout.Stack alignItems="flex-end">
@@ -284,53 +289,54 @@
<Icon size="s" icon={IconDotsHorizontal} />
</PinkButton.Button>
<svelte:fragment slot="menu" let:toggle>
<ActionMenu.Root>
<ActionMenu.Root noPadding>
<svelte:fragment slot="menu" let:toggle>
<ActionMenu.Root>
<ActionMenu.Root noPadding>
<ActionMenu.Item.Button
on:click={(e) => {
toggle(e);
showCopyUrlModal = true;
selectedFileToken = token;
}}
leadingIcon={IconDuplicate}>
Copy URL
</ActionMenu.Item.Button>
</ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconPencil}
on:click={(e) => {
toggle(e);
showCopyUrlModal = true;
showManageToken = true;
selectedFileToken = token;
}}
leadingIcon={IconDuplicate}>
Copy URL
}}>
Edit expiry
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
status="danger"
leadingIcon={IconTrash}
on:click={async () => {
toggle(e);
tokenDeleteMode = true;
showManageToken = true;
selectedFileToken = token;
}}>
Delete
</ActionMenu.Item.Button>
</ActionMenu.Root>
<ActionMenu.Item.Button
leadingIcon={IconPencil}
on:click={(e) => {
toggle(e);
showManageToken = true;
selectedFileToken = token;
}}>
Edit expiry
</ActionMenu.Item.Button>
<ActionMenu.Item.Button
status="danger"
leadingIcon={IconTrash}
on:click={async (e) => {
toggle(e);
tokenDeleteMode = true;
showManageToken = true;
selectedFileToken = token;
}}>
Delete
</ActionMenu.Item.Button>
</ActionMenu.Root>
</svelte:fragment>
</Menu>
</Layout.Stack>
</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
{:else}
<Empty on:click={() => (showManageToken = true)}>Create new file token</Empty>
{/if}
</svelte:fragment>
</CardGrid>
</svelte:fragment>
</Menu>
</Layout.Stack>
</Table.Cell>
</Table.Row.Base>
{/each}
</Table.Root>
{:else}
<Empty on:click={() => (showManageToken = true)}
>Create new file token</Empty>
{/if}
</svelte:fragment>
</CardGrid>
{/if}
<CardGrid>
<svelte:fragment slot="title">Permissions</svelte:fragment>
Assign read or write permissions at the bucket level or file level. If bucket level permissions
@@ -2,7 +2,7 @@
import { page } from '$app/state';
import { sdk } from '$lib/stores/sdk';
import type { Models } from '@appwrite.io/console';
import { Modal, Card, CopyInput } from '$lib/components';
import { Modal, CopyInput } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Alert, Layout, Tabs, Typography } from '@appwrite.io/pink-svelte';
@@ -8,7 +8,6 @@
import { Table } from '@appwrite.io/pink-svelte';
export let data: PageData;
const projectId = page.params.project;
</script>
<Table.Root columns={$columns} let:root>
@@ -103,9 +103,9 @@
}
}
async function requestAccess() {
//TODO: Implement request access
}
// async function requestAccess() {
// //TODO: Implement request access
// }
$: console.log(data);
</script>

Some files were not shown because too many files have changed in this diff Show More