mirror of
https://github.com/appwrite/console.git
synced 2026-06-06 19:27:48 +00:00
Merge branch 'main' of https://github.com/appwrite/appwrite-console-poc into feat-folder-based-routing
This commit is contained in:
Generated
+749
-2588
File diff suppressed because it is too large
Load Diff
+3
-2
@@ -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",
|
||||
|
||||
Vendored
+1
@@ -2,4 +2,5 @@
|
||||
/// <reference types="@types/gtag.js" />
|
||||
interface Window {
|
||||
GOOGLE_ANALYTICS: string | false;
|
||||
VERCEL_ANALYTICS_ID: string | false;
|
||||
}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -88,7 +88,7 @@
|
||||
</div>
|
||||
<input
|
||||
{id}
|
||||
{placeholder}
|
||||
placeholder={!tags.length ? placeholder : ''}
|
||||
type="text"
|
||||
class="tags-input-text"
|
||||
bind:value
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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'}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,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');
|
||||
|
||||
@@ -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
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user