Merge branch 'main' of https://github.com/appwrite/appwrite-console-poc into feat-folder-based-routing

This commit is contained in:
Torsten Dittmann
2022-08-25 13:26:01 +02:00
20 changed files with 1100 additions and 2854 deletions
+749 -2588
View File
File diff suppressed because it is too large Load Diff
+3 -2
View File
@@ -21,8 +21,9 @@
"@aw-labs/appwrite-console": "^1.0.0-0",
"@aw-labs/icons": "0.0.0-44",
"@aw-labs/ui": "0.0.0-44",
"echarts": "^5.3.0",
"tippy.js": "^6.3.0"
"echarts": "^5.3.3",
"tippy.js": "^6.3.7",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@playwright/test": "^1.25.0",
+1
View File
@@ -2,4 +2,5 @@
/// <reference types="@types/gtag.js" />
interface Window {
GOOGLE_ANALYTICS: string | false;
VERCEL_ANALYTICS_ID: string | false;
}
+5 -7
View File
@@ -7,15 +7,13 @@
class:u-info={type === 'info'}
class:u-error={type === 'error'}
class:u-success={type === 'success'}
class:u-warning={type === 'warning'}
class:u-neutral={type === 'neutral'}>
class:u-warning={type === 'warning'}>
{#if type}
<span
class:icon-info={type === 'info'}
class:icon-error={type === 'error'}
class:icon-success={type === 'success'}
class:icon-warning={type === 'warning'}
class:icon-neutral={type === 'neutral'}
class:icon-info={type === 'info' || type === 'neutral'}
class:icon-exclamation-circle={type === 'error'}
class:icon-check-circle={type === 'success'}
class:icon-exclamation={type === 'warning'}
aria-hidden="true" />
{/if}
<span class="text">
+1 -1
View File
@@ -88,7 +88,7 @@
</div>
<input
{id}
{placeholder}
placeholder={!tags.length ? placeholder : ''}
type="text"
class="tags-input-text"
bind:value
+4
View File
@@ -1,5 +1,6 @@
<script lang="ts">
export let disabled = false;
export let selected = false;
export let success = false;
export let warning = false;
export let danger = false;
@@ -18,6 +19,7 @@
rel={external ? 'noopener noreferrer' : ''}
class="tag"
class:is-disabled={disabled}
class:is-selected={selected}
class:is-success={success}
class:is-warning={warning}
class:is-danger={danger}
@@ -31,6 +33,7 @@
type={submit ? 'submit' : 'button'}
class="tag"
class:is-disabled={disabled}
class:is-selected={selected}
class:is-success={success}
class:is-warning={warning}
class:is-danger={danger}
@@ -41,6 +44,7 @@
<div
class="tag"
class:is-disabled={disabled}
class:is-selected={selected}
class:is-success={success}
class:is-warning={warning}
class:is-danger={danger}
+7 -3
View File
@@ -1,9 +1,10 @@
export function bytesToSize(bytes: number, decimals = 1) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
export function calculateSize(bytes: number, decimals = 1) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
@@ -11,7 +12,10 @@ export function bytesToSize(bytes: number, decimals = 1) {
}
export function sizeToBytes(value: number, unit: string) {
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const index = sizes.indexOf(unit);
return value * Math.pow(1024, index);
}
export function bytesToSize(value: number, unit: string) {
const index = sizes.indexOf(unit);
return value / Math.pow(1024, index);
}
+69
View File
@@ -0,0 +1,69 @@
import { getCLS, getFCP, getFID, getLCP, getTTFB, type Metric } from 'web-vitals';
type Options = {
params:
| {
[s: string]: string;
}
| ArrayLike<string>;
path: string;
analyticsId: string;
debug?: boolean;
};
const vitalsUrl = 'https://vitals.vercel-analytics.com/v1/vitals';
function getConnectionSpeed() {
return 'connection' in navigator &&
navigator['connection'] &&
'effectiveType' in navigator['connection']
? navigator['connection']['effectiveType']
: '';
}
function sendToAnalytics(metric: Metric, options: Options) {
const page = Object.entries(options.params).reduce(
(acc, [key, value]) => acc.replace(value, `[${key}]`),
options.path
);
const body = {
dsn: options.analyticsId,
id: metric.id,
page,
href: location.href,
event_name: metric.name,
value: metric.value.toString(),
speed: getConnectionSpeed()
};
if (options.debug) {
console.log('[Analytics]', metric.name, JSON.stringify(body, null, 2));
}
const blob = new Blob([new URLSearchParams(body).toString()], {
// This content type is necessary for `sendBeacon`
type: 'application/x-www-form-urlencoded'
});
if (navigator.sendBeacon) {
navigator.sendBeacon(vitalsUrl, blob);
} else
fetch(vitalsUrl, {
body: blob,
method: 'POST',
credentials: 'omit',
keepalive: true
});
}
export function webVitals(options: Options) {
try {
getFID((metric) => sendToAnalytics(metric, options));
getTTFB((metric) => sendToAnalytics(metric, options));
getLCP((metric) => sendToAnalytics(metric, options));
getCLS((metric) => sendToAnalytics(metric, options));
getFCP((metric) => sendToAnalytics(metric, options));
} catch (err) {
console.error('[Analytics]', err);
}
}
+1 -1
View File
@@ -20,7 +20,7 @@
</button>
<div class="alert-sticky-image">
<span
class:icon-check-cirle={type === 'success'}
class:icon-check-circle={type === 'success'}
class:icon-exclamation={type === 'warning'}
class:icon-exclamation-circle={type === 'error'}
class:icon-info={type === 'info'}
+10
View File
@@ -8,10 +8,12 @@
import { app } from '$lib/stores/app';
import Notifications from '$lib/layout/notifications.svelte';
import Loading from './_loading.svelte';
import { webVitals } from '$lib/helpers/vitals';
let loaded = false;
if (browser) {
window.VERCEL_ANALYTICS_ID = import.meta.env.VERCEL_ANALYTICS_ID?.toString() ?? false;
window.GOOGLE_ANALYTICS = import.meta.env.VITE_GOOGLE_ANALYTICS?.toString() ?? false;
}
@@ -31,6 +33,14 @@
}
});
$: if (browser && window.VERCEL_ANALYTICS_ID) {
webVitals({
path: $page.url.pathname,
params: $page.params,
analyticsId: window.VERCEL_ANALYTICS_ID
});
}
$: {
if (browser) {
if ($app.theme === 'auto') {
@@ -72,8 +72,9 @@
<CardGrid>
<h2 class="heading-level-6">Users Limit</h2>
<p>
This limits new users from signing up for your project, regardless of authentication
method. You can still create users from your Appwrite console.
Limit new users from signing up for your project, regardless of authentication method.
You can still create users and team memberships from your Appwrite console. <b>
The maximum limit is 10,000 users.</b>
</p>
<svelte:fragment slot="aside">
@@ -135,7 +136,7 @@
</CardGrid>
<CardGrid>
<h2 class="heading-level-6">Session Length - (Coming Soon)</h2>
<h2 class="heading-level-6">Session Length (Coming Soon)</h2>
<p>
If you reduce the limit, users who are currently logged in will be logged out of the
application.
@@ -51,14 +51,14 @@
<TableBody>
{#each response.memberships as membership}
<TableRowLink
href={`${base}/console/${project}/authentication/user/${membership.userId}`}>
href={`${base}/console/${project}/authentication/teams/${membership.teamId}`}>
<TableCellText title="Name">
<div class="u-flex u-gap-12">
<Avatar
size={32}
src={getAvatar(membership.userName)}
name={membership.userName} />
<span>{membership.userName ? membership.userName : 'n/a'}</span>
src={getAvatar(membership.teamName)}
name={membership.teamName} />
<span>{membership.teamName ? membership.teamName : 'n/a'}</span>
</div>
</TableCellText>
<TableCellText title="Role">{membership.roles}</TableCellText>
@@ -31,7 +31,7 @@
<h2 class="heading-level-5">Buckets</h2>
<Button on:click={() => (showCreate = true)}>
<span class="icon-plus" aria-hidden="true" /> <span class="text">Add bucket</span>
<span class="icon-plus" aria-hidden="true" /> <span class="text">Create bucket</span>
</Button>
</div>
@@ -17,7 +17,7 @@
TableCell
} from '$lib/elements/table';
import { toLocaleDate } from '$lib/helpers/date';
import { bytesToSize } from '$lib/helpers/sizeConvertion';
import { calculateSize } from '$lib/helpers/sizeConvertion';
import { Container } from '$lib/layout';
import { base } from '$app/paths';
import { files } from './store';
@@ -97,7 +97,7 @@
</TableCell>
<TableCellText title="Type">{file.mimeType}</TableCellText>
<TableCellText title="Size"
>{bytesToSize(file.sizeOriginal)}</TableCellText>
>{calculateSize(file.sizeOriginal)}</TableCellText>
<TableCellText title="Date Created"
>{toLocaleDate(file.$createdAt)}</TableCellText>
<TableCell>
@@ -132,7 +132,7 @@
</TableCell>
<TableCellText title="Type">{file.mimeType}</TableCellText>
<TableCellText title="Size"
>{bytesToSize(file.sizeOriginal)}</TableCellText>
>{calculateSize(file.sizeOriginal)}</TableCellText>
<TableCellText title="Date Created"
>{toLocaleDate(file.$createdAt)}</TableCellText>
<TableCell showOverflow>
@@ -7,7 +7,7 @@
import { page } from '$app/stores';
import { uploader } from '$lib/stores/uploader';
import { bucket } from './store';
import { bytesToSize } from '$lib/helpers/sizeConvertion';
import { calculateSize } from '$lib/helpers/sizeConvertion';
export let showCreate = false;
@@ -120,7 +120,7 @@
</div>
</div>
<p>Max file size: {bytesToSize($bucket.maximumFileSize)}</p>
<p>Max file size: {calculateSize($bucket.maximumFileSize)}</p>
</div>
{#if !showDropdown}
@@ -7,7 +7,7 @@
import { toLocaleDate, toLocaleDateTime } from '$lib/helpers/date';
import { sdkForProject } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import { bytesToSize } from '$lib/helpers/sizeConvertion';
import { calculateSize } from '$lib/helpers/sizeConvertion';
import Delete from './_deleteFile.svelte';
import { page } from '$app/stores';
import { onMount } from 'svelte';
@@ -101,7 +101,7 @@
<svelte:fragment slot="aside">
<div>
<p>MIME Type: {$file.mimeType}</p>
<p>Size: {bytesToSize($file.sizeOriginal)}</p>
<p>Size: {calculateSize($file.sizeOriginal)}</p>
<p>Created: {toLocaleDate($file.$createdAt)}</p>
<p>Last Updated: {toLocaleDate($file.$updatedAt)}</p>
</div>
@@ -2,27 +2,26 @@
import { Alert, CardGrid, Box } from '$lib/components';
import { Container } from '$lib/layout';
import {
Form,
Button,
InputText,
InputTags,
InputNumber,
InputSelect,
InputSwitch,
Helper
InputSwitch
} from '$lib/elements/forms';
import { bucket } from '../store';
import { toLocaleDateTime } from '$lib/helpers/date';
import { sizeToBytes } from '$lib/helpers/sizeConvertion';
import { sizeToBytes, bytesToSize } from '$lib/helpers/sizeConvertion';
import { sdkForProject } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import Delete from '../_deleteBucket.svelte';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import Pill from '$lib/elements/pill.svelte';
let showDelete = false;
let showError: false | 'name' | 'size' = false,
errorMessage = 'Something went wrong',
errorType: 'error' | 'warning' | 'success' = 'error';
let enabled: boolean = null,
bucketName: string = null,
bucketPermissions: string = null,
@@ -32,13 +31,14 @@
encryption: boolean = null,
antivirus: boolean = null,
maxSize: number;
let byteUnit: 'Bytes' | 'KB' | 'MB' | 'GB' = 'Bytes',
let byteUnit: 'Bytes' | 'KB' | 'MB' | 'GB' = 'MB',
options = [
{ label: 'Bytes', value: 'Bytes' },
{ label: 'Kilobytes', value: 'KB' },
{ label: 'Megabytes', value: 'MB' },
{ label: 'Gigabytes', value: 'GB' }
];
let suggestedExtensions = ['jpg', 'png', 'svg', 'gif', 'html', 'pdf', 'mp4'];
let extensions = $bucket.allowedFileExtensions;
let isExtensionsDisabled = true;
@@ -53,7 +53,7 @@
encryption ??= $bucket.encryption;
antivirus ??= $bucket.antivirus;
});
$: maxSizePlaceholder = bytesToSize($bucket.maximumFileSize, byteUnit);
$: if (bucketPermissions || bucketRead || bucketWrite) {
if (bucketPermissions !== $bucket.permission) {
arePermsDisabled = false;
@@ -73,12 +73,6 @@
} else isExtensionsDisabled = true;
}
function addError(location: typeof showError, message: string, type: typeof errorType) {
showError = location;
errorMessage = message;
errorType = type;
}
async function toggleBucket() {
try {
await sdkForProject.storage.updateBucket(
@@ -105,13 +99,15 @@
try {
await sdkForProject.storage.updateBucket($bucket.$id, $bucket.name, $bucket.permission);
$bucket.name = bucketName;
showError = false;
addNotification({
message: 'Name has been updated',
type: 'success'
});
} catch (error) {
addError('name', error.message, 'error');
addNotification({
message: error.message,
type: 'error'
});
}
}
async function updatePermissions() {
@@ -156,7 +152,7 @@
$bucket.encryption = encryption;
$bucket.antivirus = antivirus;
addNotification({
message: `${$bucket.name} has been updatede`,
message: `${$bucket.name} has been updated`,
type: 'success'
});
} catch (error) {
@@ -181,7 +177,7 @@
);
$bucket.maximumFileSize = maxSize;
addNotification({
message: `${$bucket.name} has been updatede`,
message: `${$bucket.name} has been updated`,
type: 'success'
});
} catch (error) {
@@ -206,7 +202,7 @@
);
$bucket.allowedFileExtensions = extensions;
addNotification({
message: `${$bucket.name} has been updatede`,
message: `${$bucket.name} has been updated`,
type: 'success'
});
} catch (error) {
@@ -220,230 +216,233 @@
<Container>
{#if $bucket}
<CardGrid>
<h2 class="heading-level-7">{$bucket.name}</h2>
<Form on:submit={toggleBucket}>
<CardGrid>
<h2 class="heading-level-7">{$bucket.name}</h2>
<svelte:fragment slot="aside">
<ul>
<InputSwitch
label={enabled ? 'Enabled' : 'Disabled'}
id="toggle"
bind:value={enabled} />
</ul>
<p>Created: {toLocaleDateTime($bucket.$createdAt)}</p>
<p>Last Updated: {toLocaleDateTime($bucket.$updatedAt)}</p>
</svelte:fragment>
<svelte:fragment slot="aside">
<ul>
<InputSwitch
label={enabled ? 'Enabled' : 'Disabled'}
id="toggle"
bind:value={enabled} />
</ul>
<p>Created: {toLocaleDateTime($bucket.$createdAt)}</p>
<p>Last Updated: {toLocaleDateTime($bucket.$updatedAt)}</p>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={enabled === $bucket.enabled}
on:click={() => {
toggleBucket();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<svelte:fragment slot="actions">
<Button disabled={enabled === $bucket.enabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<CardGrid>
<h6 class="heading-level-7">Update Name</h6>
<Form on:submit={updateName}>
<CardGrid>
<h6 class="heading-level-7">Update Name</h6>
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
bind:value={bucketName} />
</ul>
</svelte:fragment>
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
bind:value={bucketName} />
{#if showError === 'name'}
<Helper type={errorType}>{errorMessage}</Helper>
<svelte:fragment slot="actions">
<Button disabled={bucketName === $bucket.name || !bucketName} submit
>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<Form on:submit={updatePermissions}>
<CardGrid>
<h6 class="heading-level-7">Update Permissions</h6>
<p>
Assign read or write permissions at the <b>Bucket Level</b> or
<b>File Level</b>. If Bucket Level permissions are assigned, file permissions
will be ignored.
</p>
<svelte:fragment slot="aside">
<ul class="u-flex u-gap-12 common-section">
<li>
<label class="label">
<input
type="radio"
class="is-small"
name="level"
bind:group={bucketPermissions}
value="bucket" />
<span>Bucket Level</span>
</label>
</li>
<li>
<label class="label">
<input
type="radio"
class="is-small"
name="level"
bind:group={bucketPermissions}
value="file" />
<span>File Level</span>
</label>
</li>
</ul>
<Alert type="info">
<p>
Tip: Add <b>role:all</b> for wildcards access. Check out our
documentation for more on <a href="/#">Permissions</a>
</p>
</Alert>
{#if bucketPermissions === 'bucket'}
<ul class="common-section">
<InputTags
id="read"
label="Read Access"
placeholder="User ID, Team ID, or Role"
bind:tags={bucketRead} />
<InputTags
id="write"
label="Write Access"
placeholder="User ID, Team ID, or Role"
bind:tags={bucketWrite} />
</ul>
{/if}
</ul>
</svelte:fragment>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={arePermsDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<svelte:fragment slot="actions">
<Button
disabled={bucketName === $bucket.name || !bucketName}
on:click={() => {
updateName();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<CardGrid>
<h6 class="heading-level-7">Update Permissions</h6>
<p>
Assign read or write permissions at the <b>Bucket Level</b> or
<b>File Level</b>. If Bucket Level permissions are assigned, file permissions will
be ignored.
</p>
<svelte:fragment slot="aside">
<ul class="u-flex u-gap-12 common-section">
<li>
<label class="label">
<input
type="radio"
class="is-small"
name="level"
bind:group={bucketPermissions}
value="bucket" />
<span>Bucket Level</span>
</label>
</li>
<li>
<label class="label">
<input
type="radio"
class="is-small"
name="level"
bind:group={bucketPermissions}
value="file" />
<span>File Level</span>
</label>
</li>
</ul>
<Alert type="info">
<p>
Tip: Add <b>role:all</b> for wildcards access. Check out our documentation
for more on <a href="/#">Permissions</a>
</p>
</Alert>
{#if bucketPermissions === 'bucket'}
<Form on:submit={updateSecurity}>
<CardGrid>
<h2 class="heading-level-7">Update Security Settings</h2>
<p>
Enable or disable security services for the bucket including <b> Ecryption</b>
and <b> Antivirus scanning.</b>
</p>
<svelte:fragment slot="aside">
<ul class="form-list">
<li class="form-item">
<label class="choice-item" for="encryption">
<div class="input-text-wrapper">
<input
label="Encryption"
id="encryption"
type="checkbox"
class="switch"
role="switch"
bind:checked={encryption} />
</div>
<div class="choice-item-content">
<div class="choice-item-title">Encryption</div>
<div class="choice-item-paragraph">
This parameter allows you to configure whether or not the
files inside the bucket will be encrypted. We don't encrypt
files bigger than 20MB.
</div>
</div>
</label>
</li>
<li class="form-item">
<label class="choice-item" for="antivirus">
<div class="input-text-wrapper">
<input
label="Antivirus"
id="antivirus"
type="checkbox"
class="switch"
role="switch"
bind:checked={antivirus} />
</div>
<div class="choice-item-content">
<div class="choice-item-title">Antivirus</div>
<div class="choice-item-paragraph">
This parameter allows you to configure whether or not the
files inside the bucket should be scanned by the Appwrite
Antivirus scanner.
</div>
</div>
</label>
</li>
<li />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={encryption === $bucket.encryption &&
antivirus === $bucket.antivirus}
submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<Form on:submit={updateMaxSize}>
<CardGrid>
<h2 class="heading-level-6">Update Maximum File Size</h2>
<p>Set the maximum file size allowed in the bucket.</p>
<svelte:fragment slot="aside">
<ul class="u-flex u-gap-12">
<InputNumber
id="size"
label="Size"
placeholder={`${maxSizePlaceholder}`}
bind:value={maxSize} />
<InputSelect id="bytes" label="Bytes" {options} bind:value={byteUnit} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button disabled={!maxSize} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<Form on:submit={updateAllowedExtensions}>
<CardGrid>
<h6 class="heading-level-7">Update Allowed File Extensions</h6>
<p>
A maxiumum of 100 file extensions can be added. Leave blank to allow all file
types.
</p>
<svelte:fragment slot="aside">
<ul class="common-section">
<InputTags
id="read"
label="Read Access"
placeholder="User ID, Team ID, or Role"
bind:tags={bucketRead} />
<InputTags
id="write"
label="Write Access"
placeholder="User ID, Team ID, or Role"
bind:tags={bucketWrite} />
label="Allowed file extensions"
placeholder="Allowed file extensions (mp4, jpg, pdf, etc.)"
bind:tags={extensions} />
<li class="u-flex u-gap-12 u-margin-block-start-8 ">
{#each suggestedExtensions as ext}
<Pill
selected={extensions.includes(ext)}
button
on:click={() => {
if (!extensions.includes(ext))
extensions = [...extensions, ext];
}}>
<span class="icon-plus" aria-hidden="true" />
{ext}
</Pill>
{/each}
</li>
</ul>
{/if}
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={arePermsDisabled}
on:click={() => {
updatePermissions();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<CardGrid>
<h2 class="heading-level-7">Update Security Settings</h2>
<p>
Enable or disable security services for the bucket including <b> Ecryption</b>
and <b> Antivirus scanning.</b>
</p>
<svelte:fragment slot="aside">
<ul class="form-list">
<li class="form-item">
<label class="choice-item" for="encryption">
<div class="input-text-wrapper">
<input
label="Encryption"
id="encryption"
type="checkbox"
class="switch"
role="switch"
bind:checked={encryption} />
</div>
<div class="choice-item-content">
<div class="choice-item-title">Encryption</div>
</svelte:fragment>
<div class="choice-item-paragraph">
This parameter allows you to configure whether or not the files
inside the bucket will be encrypted. We don't encrypt files
bigger than 20MB.
</div>
</div>
</label>
</li>
<li class="form-item">
<label class="choice-item" for="antivirus">
<div class="input-text-wrapper">
<input
label="Antivirus"
id="antivirus"
type="checkbox"
class="switch"
role="switch"
bind:checked={antivirus} />
</div>
<div class="choice-item-content">
<div class="choice-item-title">Antivirus</div>
<svelte:fragment slot="actions">
<Button disabled={isExtensionsDisabled} submit>Update</Button>
</svelte:fragment>
</CardGrid>
</Form>
<div class="choice-item-paragraph">
This parameter allows you to configure whether or not the files
inside the bucket should be scanned by the Appwrite Antivirus
scanner.
</div>
</div>
</label>
</li>
<li />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={encryption === $bucket.encryption && antivirus === $bucket.antivirus}
on:click={() => {
updateSecurity();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<CardGrid>
<h2 class="heading-level-6">Update Maximum File Size</h2>
<p>Set the maximum file size allowed in the bucket.</p>
<svelte:fragment slot="aside">
<ul class="u-flex u-gap-12">
<InputNumber
id="size"
label="Size"
placeholder={$bucket.maximumFileSize + ''}
bind:value={maxSize} />
<InputSelect id="bytes" label="Bytes" {options} bind:value={byteUnit} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={!maxSize}
on:click={() => {
updateMaxSize();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<CardGrid>
<h6 class="heading-level-7">Update Allowed File Extensions</h6>
<p>
A maxiumum of 100 file extensions can be added. Leave blank to allow all file types.
</p>
<svelte:fragment slot="aside">
<Alert type="info">
<p>
Tip: Commonly added extensions include JPG, PNG, SVG, GIF, HTML, PDF, MP4.
</p>
</Alert>
<ul class="common-section">
<InputTags
id="read"
label="Allowed file extensions"
placeholder="Allowed file extensions (mp4, jpg, pdf, etc.)"
bind:tags={extensions} />
</ul>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={isExtensionsDisabled}
on:click={() => {
updateAllowedExtensions();
}}>Update</Button>
</svelte:fragment>
</CardGrid>
<CardGrid>
<h6 class="heading-level-7">Delete Bucket</h6>
<p>
-5
View File
@@ -26,10 +26,6 @@ test('login page has a working sign up link', async ({ page }) => {
await page.waitForTimeout(100);
const signup = page.locator('a[href="/register"]');
expect(await signup.isVisible());
await signup.click();
await page.waitForNavigation({ waitUntil: 'networkidle' });
expect(page.url()).toContain('/register');
expect(await page.locator('Register').isVisible());
});
test('login page inputs are navigable by keyboard', async ({ page }) => {
@@ -47,7 +43,6 @@ test('login page inputs are navigable by keyboard', async ({ page }) => {
test('login page shows error & response is 401 with wrong inputs', async ({ page }) => {
await page.goto('/login');
await page.fill('id=email', 'wrongemail@apppwrite.io');
await page.fill('id=password', 'wrongpassword');
await page.click('button:has-text("Login")');
+1 -1
View File
@@ -1,7 +1,7 @@
import { expect, test } from '@playwright/test';
test('register page has inputs', async ({ page }) => {
await page.goto('/login');
await page.goto('/register');
const name = page.locator('id=name');
const mail = page.locator('id=email');
const pass = page.locator('id=password');
+3
View File
@@ -9,6 +9,9 @@ const config = {
ssr: {
noExternal: ['echarts']
},
define: {
'import.meta.env.VERCEL_ANALYTICS_ID': JSON.stringify(process.env.VERCEL_ANALYTICS_ID)
},
legacy: {
buildSsrCjsExternalHeuristics: true
},