Merge branch 'cloud-current' of https://github.com/appwrite/console into christy-usage-test

This commit is contained in:
Christy Jacob committed 2023-04-19 14:59:43 +00:00
commit 7b64ac16dc
118 files changed
+1519 -947

No files matched your search

+3 -7
View File
@@ -17,9 +17,9 @@ jobs:
- name: Use Node.js
uses: actions/setup-node@v2
with:
node-version: 16
- name: Audit dependencies
run: npm audit --audit-level low
node-version: 18
# - name: Audit dependencies
# run: npm audit --audit-level low
- name: Install dependencies
run: npm ci
- name: Build Console
@@ -30,7 +30,3 @@ jobs:
run: npm run lint
- name: Unit Tests
run: npm test
# - name: E2E Tests
# run: |
# npx playwright install
# npm run e2e
+15 -1
View File
@@ -33,7 +33,21 @@ If you are worried about or don’t know where to start, check out the next sect
## Development
Once you've created a project and installed dependencies with `npm install`, create a `.env` file using `.env.example` as a template.
### 1. Clone the repository with git
```bash
git clone https://github.com/appwrite/console.git appwrite-console
```
### 2. Install dependencies with npm
```bash
npm install
```
### 3. Setup environment variables
Add a `.env` file by copying the `.env.example` file as a template in the project's root directory.
Finally, start a development server:
+35 -29
View File
@@ -16,6 +16,7 @@
"@sentry/tracing": "^7.36.0",
"analytics": "^0.8.1",
"echarts": "^5.4.1",
"logrocket": "^3.0.1",
"pretty-bytes": "^6.1.0",
"prismjs": "^1.29.0",
"tippy.js": "^6.3.7",
@@ -24,7 +25,7 @@
"devDependencies": {
"@playwright/test": "^1.30.0",
"@sveltejs/adapter-static": "1.0.6",
"@sveltejs/kit": "1.3.10",
"@sveltejs/kit": "^1.3.10",
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@testing-library/dom": "^8.20.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -33,7 +34,7 @@
"@types/prismjs": "^1.26.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vitest/ui": "^0.28.4",
"@vitest/ui": "^0.28.5",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-svelte3": "^4.0.0",
@@ -49,7 +50,7 @@
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "^0.28.4"
"vitest": "^0.28.5"
},
"engines": {
"node": ">=16"
@@ -1982,17 +1983,17 @@
}
},
"node_modules/@sveltejs/vite-plugin-svelte": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.2.tgz",
"integrity": "sha512-xCEan0/NNpQuL0l5aS42FjwQ6wwskdxC3pW1OeFtEKNZwRg7Evro9lac9HesGP6TdFsTv2xMes5ASQVKbCacxg==",
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/@sveltejs/vite-plugin-svelte/-/vite-plugin-svelte-2.0.3.tgz",
"integrity": "sha512-o+cguBFdwIGtRbNkYOyqTM7KvRUffxh5bfK4oJsWKG2obu+v/cbpT03tJrGl58C7tRXo/aEC0/axN5FVHBj0nA==",
"dev": true,
"dependencies": {
"debug": "^4.3.4",
"deepmerge": "^4.2.2",
"deepmerge": "^4.3.0",
"kleur": "^4.1.5",
"magic-string": "^0.27.0",
"magic-string": "^0.29.0",
"svelte-hmr": "^0.15.1",
"vitefu": "^0.2.3"
"vitefu": "^0.2.4"
},
"engines": {
"node": "^14.18.0 || >= 16"
@@ -2003,9 +2004,9 @@
}
},
"node_modules/@sveltejs/vite-plugin-svelte/node_modules/magic-string": {
"version": "0.27.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.27.0.tgz",
"integrity": "sha512-8UnnX2PeRAPZuN12svgR9j7M1uWMovg/CEnIwIG0LFkXSJJe4PdfUGiTGl8V9bsBHFUtfVINcSyYxd7q+kx9fA==",
"version": "0.29.0",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.29.0.tgz",
"integrity": "sha512-WcfidHrDjMY+eLjlU+8OvwREqHwpgCeKVBUpQ3OhYYuvfaYCUgcbuBzappNzZvg/v8onU3oQj+BYpkOJe9Iw4Q==",
"dev": true,
"dependencies": {
"@jridgewell/sourcemap-codec": "^1.4.13"
@@ -6261,6 +6262,11 @@
"integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
"dev": true
},
"node_modules/logrocket": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/logrocket/-/logrocket-3.0.1.tgz",
"integrity": "sha512-jOWG+jEzobKVxGytzZ+4KGm2kiMQMRTHab2uDWQyVZcHfEF38BlCH1yjQVY4LCmuQUwZitP9biMzJZnyUQ0dtQ=="
},
"node_modules/loupe": {
"version": "2.3.6",
"resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.6.tgz",
@@ -6449,15 +6455,15 @@
}
},
"node_modules/mlly": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.0.tgz",
"integrity": "sha512-cwzBrBfwGC1gYJyfcy8TcZU1f+dbH/T+TuOhtYP2wLv/Fb51/uV7HJQfBPtEupZ2ORLRU1EKFS/QfS3eo9+kBQ==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/mlly/-/mlly-1.1.1.tgz",
"integrity": "sha512-Jnlh4W/aI4GySPo6+DyTN17Q75KKbLTyFK8BrGhjNP4rxuUjbRWhE6gHg3bs33URWAF44FRm7gdQA348i3XxRw==",
"dev": true,
"dependencies": {
"acorn": "^8.8.1",
"pathe": "^1.0.0",
"acorn": "^8.8.2",
"pathe": "^1.1.0",
"pkg-types": "^1.0.1",
"ufo": "^1.0.1"
"ufo": "^1.1.0"
}
},
"node_modules/mri": {
@@ -6934,14 +6940,14 @@
}
},
"node_modules/pkg-types": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.1.tgz",
"integrity": "sha512-jHv9HB+Ho7dj6ItwppRDDl0iZRYBD0jsakHXtFgoLr+cHSF6xC+QL54sJmWxyGxOLYSHm0afhXhXcQDQqH9z8g==",
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/pkg-types/-/pkg-types-1.0.2.tgz",
"integrity": "sha512-hM58GKXOcj8WTqUXnsQyJYXdeAPbythQgEF3nTcEo+nkD49chjQ9IKm/QJy9xf6JakXptz86h7ecP2024rrLaQ==",
"dev": true,
"dependencies": {
"jsonc-parser": "^3.2.0",
"mlly": "^1.0.0",
"pathe": "^1.0.0"
"mlly": "^1.1.1",
"pathe": "^1.1.0"
}
},
"node_modules/playwright-core": {
@@ -8286,9 +8292,9 @@
}
},
"node_modules/ufo": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.0.1.tgz",
"integrity": "sha512-boAm74ubXHY7KJQZLlXrtMz52qFvpsbOxDcZOnw/Wf+LS4Mmyu7JxmzD4tDLtUQtmZECypJ0FrCz4QIe6dvKRA==",
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/ufo/-/ufo-1.1.0.tgz",
"integrity": "sha512-LQc2s/ZDMaCN3QLpa+uzHUOQ7SdV0qgv3VBXOolQGXTaaZpIur6PwUclF5nN2hNkiTRcUugXd1zFOW3FLJ135Q==",
"dev": true
},
"node_modules/undici": {
@@ -8387,9 +8393,9 @@
"peer": true
},
"node_modules/vite": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.1.tgz",
"integrity": "sha512-LM9WWea8vsxhr782r9ntg+bhSFS06FJgCvvB0+8hf8UWtvaiDagKYWXndjfX6kGl74keHJUcpzrQliDXZlF5yg==",
"version": "4.1.4",
"resolved": "https://registry.npmjs.org/vite/-/vite-4.1.4.tgz",
"integrity": "sha512-3knk/HsbSTKEin43zHu7jTwYWv81f8kgAL99G5NWBcA1LKvtvcVAC4JjBH1arBunO9kQka+1oGbrMKOjk4ZrBg==",
"dev": true,
"dependencies": {
"esbuild": "^0.16.14",
+4 -3
View File
@@ -27,6 +27,7 @@
"@sentry/tracing": "^7.36.0",
"analytics": "^0.8.1",
"echarts": "^5.4.1",
"logrocket": "^3.0.1",
"pretty-bytes": "^6.1.0",
"prismjs": "^1.29.0",
"tippy.js": "^6.3.7",
@@ -35,7 +36,7 @@
"devDependencies": {
"@playwright/test": "^1.30.0",
"@sveltejs/adapter-static": "1.0.6",
"@sveltejs/kit": "1.3.10",
"@sveltejs/kit": "^1.3.10",
"@sveltejs/vite-plugin-svelte": "^2.0.2",
"@testing-library/dom": "^8.20.0",
"@testing-library/jest-dom": "^5.16.5",
@@ -44,7 +45,7 @@
"@types/prismjs": "^1.26.0",
"@typescript-eslint/eslint-plugin": "^5.50.0",
"@typescript-eslint/parser": "^5.50.0",
"@vitest/ui": "^0.28.4",
"@vitest/ui": "^0.28.5",
"eslint": "^8.33.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-svelte3": "^4.0.0",
@@ -60,7 +61,7 @@
"tslib": "^2.5.0",
"typescript": "^4.9.5",
"vite": "^4.1.1",
"vitest": "^0.28.4"
"vitest": "^0.28.5"
},
"type": "module",
"pre-commit": [
+12 -14
View File
@@ -1,18 +1,16 @@
import Analytics from 'analytics';
import googleAnalytics from '@analytics/google-analytics';
import { get } from 'svelte/store';
import { page } from '$app/stores';
import { user } from '$lib/stores/user';
import { growthEndpoint, Mode } from '$lib/constants';
import { ENV, MODE, VARS } from '$lib/system';
import googleAnalytics from '@analytics/google-analytics';
import { AppwriteException } from '@aw-labs/appwrite-console';
import Analytics from 'analytics';
import { get } from 'svelte/store';
const isDevelopment =
import.meta.env.DEV || import.meta.env?.VITE_VERCEL_ENV?.toString() === 'preview';
const analytics = Analytics({
app: 'appwrite',
plugins: [
googleAnalytics({
measurementIds: [import.meta.env.VITE_GA_PROJECT?.toString() || 'G-R4YJ9JN8L4']
measurementIds: [VARS.GOOGLE_ANALYTICS || 'G-R4YJ9JN8L4']
})
]
});
@@ -32,7 +30,7 @@ export function trackEvent(name: string, data: object = null): void {
};
}
if (isDevelopment) {
if (ENV.DEV || ENV.PREVIEW) {
console.debug(`[Analytics] Event ${name} ${path}`, data);
} else {
analytics.track(name, { ...data, path });
@@ -54,7 +52,7 @@ export function trackPageView(path: string) {
return;
}
if (isDevelopment) {
if (ENV.DEV || ENV.PREVIEW) {
console.debug(`[Analytics] Pageview ${path}`);
} else {
analytics.page({
@@ -64,14 +62,14 @@ export function trackPageView(path: string) {
}
function sendEventToGrowth(event: string, path: string, data: object = null): void {
if (!growthEndpoint) return;
if (!VARS.GROWTH_ENDPOINT) return;
const userStore = get(user);
let email: string, name: string;
if (userStore) {
email = userStore.email;
name = userStore.name;
}
fetch(`${growthEndpoint}/analytics`, {
fetch(`${VARS.GROWTH_ENDPOINT}/analytics`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
@@ -80,7 +78,7 @@ function sendEventToGrowth(event: string, path: string, data: object = null): vo
action: event,
label: event,
url: window.location.origin + path,
account: import.meta.env.VITE_CONSOLE_MODE?.toString() || Mode.SELF_HOSTED,
account: MODE,
data: {
email,
name,
@@ -90,8 +88,8 @@ function sendEventToGrowth(event: string, path: string, data: object = null): vo
});
}
function isTrackingAllowed() {
if (import.meta.env?.VITEST) {
export function isTrackingAllowed() {
if (ENV.TEST) {
return;
}
if (window.navigator?.doNotTrack) {
+1 -1
View File
@@ -32,7 +32,7 @@
class:icon-exclamation={type === 'warning'}
class:icon-exclamation-circle={type === 'error'}
aria-hidden="true" />
<div class="alert-content">
<div class="alert-content" data-private>
{#if $$slots.title}
<h6 class="alert-title">
<slot name="title" />
+2 -1
View File
@@ -10,7 +10,8 @@
<ul
class="grid-box common-section u-margin-block-start-32"
style={`--grid-gap:1.5rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}>
style={`--grid-gap:1.5rem; --grid-item-size:${total > 3 ? '22rem' : '25rem'};`}
data-private>
<slot />
{#if total > 3 ? total < limit + offset : total % 2 !== 0}
+1 -1
View File
@@ -28,7 +28,7 @@
});
</script>
<section class="box u-overflow-hidden " class:common-section={!noMargin}>
<section class="box u-overflow-hidden" class:common-section={!noMargin}>
<div
class="controls u-position-absolute u-inset-inline-end-8 u-inset-block-start-8 u-flex u-gap-8">
{#if label}
+1 -1
View File
@@ -31,7 +31,7 @@
<div class="options-list">
<button
type="button"
class="input-button"
class="options-list-button"
aria-label="Click to copy."
on:click={handleCopy}
on:mouseenter={() => setTimeout(() => (content = 'Click to copy'))}
+1 -1
View File
@@ -24,7 +24,7 @@
<section
class:u-overflow-y-auto={scrollable}
class:u-max-height-200={scrollable}
class="drop-section ">
class="drop-section">
<ul class="drop-list">
<slot name="list" />
</ul>
-1
View File
@@ -20,7 +20,6 @@
async function handleSubmit() {
try {
await feedback.submitFeedback('feedback-nps', message, name, email, value);
console.log(value, message);
feedback.switchType('general');
} catch (error) {
feedback.switchType('general');
+1 -1
View File
@@ -81,7 +81,7 @@
<form class="modal-form" role="form" on:submit|preventDefault>
<header class="modal-header">
{#if warning}
<div class="avatar is-color-orange is-medium">
<div class="avatar is-warning is-medium">
<span class="icon-exclamation" aria-hidden="true" />
</div>
{/if}
+2 -2
View File
@@ -68,13 +68,13 @@
{@const isAnonymous = !data.email && !data.phone && isUser}
<div class="user-profile">
{#if isAnonymous}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-anonymous" aria-hidden="true" />
</div>
{:else if data.name}
<AvatarInitials name={data.name} size={40} />
{:else}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-minus-sm" aria-hidden="true" />
</div>
{/if}
+2 -2
View File
@@ -96,7 +96,7 @@
<div class="u-x-small">{user.$id}</div>
</div>
{:else}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-minus-sm" aria-hidden="true" />
</div>
<div class="u-line-height-1-5">
@@ -107,7 +107,7 @@
</div>
{/if}
{:else}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-anonymous" aria-hidden="true" />
</div>
<div class="u-line-height-1-5">
+1 -1
View File
@@ -8,7 +8,7 @@
export let copyEvent: string = null;
</script>
<div class="interactive-text-output" class:is-textarea={show}>
<div class="interactive-text-output" class:is-textarea={show} data-private>
{#if show}
<span class="text u-line-height-1-5 u-break-all">{value}</span>
{:else}
+1 -1
View File
@@ -1,3 +1,3 @@
<ul class="grid-box common-section" style="--grid-item-size:25rem;">
<ul class="grid-box common-section" style="--grid-item-size:25rem;" data-private>
<slot />
</ul>
-7
View File
@@ -2,13 +2,6 @@ export const PAGE_LIMIT = 12; // default page limit
export const CARD_LIMIT = 6; // default card limit
export const INTERVAL = 5 * 60000; // default interval to check for feedback
export enum Mode {
CLOUD = 'cloud',
SELF_HOSTED = 'self-hosted'
}
export const growthEndpoint = import.meta.env.VITE_APPWRITE_GROWTH_ENDPOINT;
export enum Dependencies {
ORGANIZATION = 'dependency:organization',
PROJECT = 'dependency:project',
+1
View File
@@ -23,3 +23,4 @@ export { default as InputURL } from './inputURL.svelte';
export { default as InputId } from './inputId.svelte';
export { default as InputSecret } from './inputSecret.svelte';
export { default as Helper } from './helper.svelte';
export { default as Label } from './label.svelte';
+3 -6
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let optionalText: string | undefined = undefined;
@@ -27,12 +27,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>
<Label {required} {optionalText} hide={!showLabel} for={id}>
{label}
{#if optionalText}
<span class="optional">{optionalText}</span>
{/if}
</label>
</Label>
<div class="input-text-wrapper">
<input
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -48,7 +48,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<input
{id}
+3 -6
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -39,12 +39,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>
<Label {required} {optionalText} hide={!showLabel} for={id}>
{label}
<span class:u-hide={!showLabel || !optionalText} class="optional">
{optionalText}
</span>
</label>
</Label>
<div class="input-text-wrapper" style="--amount-of-buttons:1; --button-size: 1rem">
<input
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -47,7 +47,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<input
{id}
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -41,7 +41,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<input
{id}
+2 -2
View File
@@ -63,7 +63,7 @@
<p class="text">{label}</p>
{/if}
<div
class="box is-no-shadow u-padding-24"
class="box is-no-shadow u-padding-24"
style="--box-border-radius:var(--border-radius-xsmall); z-index: 1"
class:is-border-dashed={!hovering}
class:is-hover-with-file={hovering}
@@ -77,7 +77,7 @@
</div>
<div class="u-min-width-0 u-text-center">
<h5 class="upload-file-box-title heading-level-7 u-inline">
<span class="is-only-desktop">Drag and drop files here to upload</span>
<span class="is-only-desktop">Drag and drop a file here to upload</span>
<span class="is-not-desktop">Upload a File</span>
</h5>
{#if allowedFileExtensions?.length}
+3 -6
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let optionalText: string | undefined = undefined;
@@ -52,12 +52,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>
<Label {required} {optionalText} hide={!showLabel} for={id}>
{label}
{#if optionalText}
<span class="optional">{optionalText}</span>
{/if}
</label>
</Label>
<div class="input-text-wrapper">
<input
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let id: string;
export let label: string;
@@ -47,7 +47,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
{#if showInPlainText}
<input
+4 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -46,7 +46,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<input
{id}
+4 -2
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -40,7 +40,9 @@
bind:group
bind:this={element}
on:invalid={handleInvalid} />
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
</div>
{#if error}
<Helper type="warning">{error}</Helper>
+6 -3
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { FormItem } from '.';
import { FormItem, Label } from '.';
export let id: string;
export let label: string;
@@ -11,7 +11,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper" style=" --amount-of-buttons: 1;">
{#if showInPlainText}
<div
@@ -19,7 +22,7 @@
role="textbox"
{id}
aria-placeholder={placeholder}
class="input-text is-resizable"
class="input-text is-resizable"
bind:textContent={value} />
{:else}
<input
+3 -6
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let id: string;
export let label: string;
@@ -41,12 +41,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>
<Label {required} {optionalText} hide={!showLabel} for={id}>
{label}
{#if optionalText}
<span class="optional">{optionalText}</span>
{/if}
</label>
</Label>
<div class="select">
<select
+5 -2
View File
@@ -1,7 +1,7 @@
<script lang="ts">
import { last } from '$lib/helpers/array';
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let id: string;
export let label: string;
@@ -77,7 +77,10 @@
value={tags.join(',')}
{required}
on:invalid={handleInvalid} />
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<div class="tags-input">
<div class="tags">
+10 -15
View File
@@ -1,7 +1,6 @@
<script lang="ts">
import { tooltip as tooltipAction } from '$lib/actions/tooltip';
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let optionalText: string | undefined = undefined;
@@ -43,20 +42,9 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>
<Label {required} {tooltip} {optionalText} hide={!showLabel} for={id}>
{label}
{#if tooltip}
<span
class="icon-info"
aria-hidden="true"
use:tooltipAction={{
content: tooltip
}} />
{/if}
{#if optionalText}
<span class="optional">{optionalText}</span>
{/if}
</label>
</Label>
<div class="input-text-wrapper">
<input
@@ -72,6 +60,13 @@
bind:value
bind:this={element}
on:invalid={handleInvalid} />
{#if maxlength}
<span class="text-counter">
<span class="text-counter-count">{value?.length ?? 0}</span>
<span class="text-counter-separator" />
<span class="text-counter-max">{maxlength}</span>
</span>
{/if}
</div>
{#if error}
<Helper type="warning">{error}</Helper>
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -37,7 +37,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<textarea
{id}
+5 -2
View File
@@ -1,6 +1,6 @@
<script lang="ts">
import { onMount } from 'svelte';
import { FormItem, Helper } from '.';
import { FormItem, Helper, Label } from '.';
export let label: string;
export let showLabel = true;
@@ -50,7 +50,10 @@
</script>
<FormItem>
<label class:u-hide={!showLabel} class="label" for={id}>{label}</label>
<Label {required} hide={!showLabel} for={id}>
{label}
</Label>
<div class="input-text-wrapper">
<input
{id}
+33
View File
@@ -0,0 +1,33 @@
<script lang="ts">
interface $$Props extends Partial<HTMLLabelElement> {
required?: boolean;
optionalText?: string | undefined;
hide?: boolean;
tooltip?: string;
for?: string;
}
export let required: $$Props['required'] = false;
export let optionalText: $$Props['optionalText'] = undefined;
export let hide: $$Props['hide'] = false;
export let tooltip: $$Props['tooltip'] = null;
</script>
<label class:is-required={required} class:u-hide={hide} class="label" {...$$restProps}>
<slot />
</label>
{#if optionalText}
<span class="optional">{optionalText}</span>
{/if}
{#if tooltip}
<button class="tooltip" aria-label="variables info">
<span class="icon-info" aria-hidden="true" />
<span class="tooltip-popup" role="tooltip">
<p class="text">
{tooltip}
</p>
</span>
</button>
{/if}
+1 -1
View File
@@ -3,6 +3,6 @@
export let title: string;
</script>
<div class="table-col" data-title={title} role="cell">
<div class="table-col" data-title={title} role="cell" data-private>
<a role="button" tabindex="0" class="link" {href}><slot /></a>
</div>
+2 -1
View File
@@ -10,6 +10,7 @@
class:is-only-desktop={onlyDesktop}
class:u-overflow-visible={showOverflow}
data-title={title}
role="cell">
role="cell"
data-private>
<Trim><slot /></Trim>
</div>
+2 -1
View File
@@ -7,6 +7,7 @@
class="table is-selected-columns-mobile"
class:u-margin-block-start-32={!noMargin}
class:is-remove-outer-styles={noStyles}
role="table">
role="table"
data-private>
<slot />
</div>
+1 -1
View File
@@ -1,5 +1,5 @@
<!-- TODO: is 100% height needed? -->
<div class="table-with-scroll">
<div class="table-with-scroll" data-private>
<div class="table-wrapper">
<div class="table is-remove-outer-styles">
<ul class="table-thead">
+1 -1
View File
@@ -2,7 +2,7 @@
export let isSticky = false;
</script>
<div class="table-with-scroll u-margin-block-start-32">
<div class="table-with-scroll u-margin-block-start-32" data-private>
<div class="table-wrapper">
<table class="table" class:is-sticky-scroll={isSticky}>
<slot />
+7 -5
View File
@@ -1,6 +1,7 @@
import prettyBytes from 'pretty-bytes';
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'] as const;
export type Size = (typeof sizes)[number];
export function calculateSize(bytes: number, decimals = 1) {
if (bytes === 0) return '0 Bytes';
@@ -13,18 +14,19 @@ export function calculateSize(bytes: number, decimals = 1) {
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}
export function sizeToBytes(value: number, unit: string) {
export function sizeToBytes(value: number, unit: Size) {
const index = sizes.indexOf(unit);
return value * Math.pow(1024, index);
}
export function bytesToSize(value: number, unit: string) {
export function bytesToSize(value: number, unit: Size) {
const index = sizes.indexOf(unit);
return value / Math.pow(1024, index);
}
export function humanFileSize(bytes: number): {
value: string;
unit: string;
unit: Size;
} {
const value = prettyBytes(bytes, {
locale: 'en'
@@ -32,6 +34,6 @@ export function humanFileSize(bytes: number): {
return {
value: value[0],
unit: value[1]
unit: value[1] as Size
};
}
+77
View File
@@ -0,0 +1,77 @@
import { derived, writable } from 'svelte/store';
import { last } from './array';
import { withPrevious } from './withPrevious';
export type Unit<T = string> = {
name: T;
value: number;
};
/**
* Creates a value-unit pair. When the unit is changed, the value is updated
* to reflect the new unit.
*
* @export
* @template T = string
* @param {number} [initialValue=0]
* @param {Unit<T>[]} units
*/
export function createValueUnitPair<T = string>(initialValue = 0, units: Unit<T>[]) {
if (!units.some((u) => u.value === 1)) {
throw new Error('Units must have a value of 1');
}
if (units.some((u) => u.value < 1)) {
throw new Error('Units must have a value greater than 1');
}
// Highest to lowest
const sortedUnits = units.sort((a, b) => b.value - a.value);
const unit = withPrevious(last(sortedUnits).name);
const value = writable(initialValue);
const baseValue = derived([value, unit], ([$value, $unit]) => {
return $value * units.find((u) => u.name === $unit).value;
});
for (const u of sortedUnits) {
if (initialValue >= u.value && initialValue % u.value === 0) {
unit.set(u.name, { updateAll: true });
value.set(initialValue / u.value);
break;
}
}
unit.subscribe((newValue, prev) => {
value.update((v) => {
const unitInBase = v * units.find((u) => u.name === prev).value;
return unitInBase / units.find((u) => u.name === newValue).value;
});
});
return {
value,
unit,
baseValue
};
}
export function createTimeUnitPair(initialValue = 0) {
const units: Unit[] = [
{ name: 'Days', value: 86400 },
{ name: 'Hours', value: 3600 },
{ name: 'Minutes', value: 60 },
{ name: 'Seconds', value: 1 }
];
return { ...createValueUnitPair(initialValue, units), units };
}
export function createByteUnitPair(initialValue = 0) {
const units: Unit[] = [
{ name: 'Bytes', value: 1 },
{ name: 'Kilobytes', value: 1024 },
{ name: 'Megabytes', value: 1024 ** 2 },
{ name: 'Gigabytes', value: 1024 ** 3 }
];
return { ...createValueUnitPair(initialValue, units), units };
}
+38
View File
@@ -0,0 +1,38 @@
type SetterOptions = {
updateAll?: boolean;
};
type Listener<T> = (value: T, previous: T | null) => void;
export function withPrevious<T>(value: T) {
let current = value;
let previous: T | null = null;
let subscribers: Array<Listener<T>> = [];
const subscribe = (listener: Listener<T>) => {
subscribers.push(listener);
listener(current, previous);
return () => {
subscribers = subscribers.filter((s) => s !== listener);
};
};
const set = (value: T, options?: SetterOptions) => {
previous = options?.updateAll ? value : current;
current = value;
subscribers.forEach((s) => s(current, previous));
};
const getPrevious = () => previous;
const currentStore = {
subscribe,
set,
getPrevious
};
return currentStore;
}
+17 -28
View File
@@ -1,5 +1,5 @@
<script lang="ts">
import { EmptySearch, Pagination, Trim } from '$lib/components';
import { AvatarInitials, EmptySearch, Pagination, Trim } from '$lib/components';
import {
TableBody,
TableHeader,
@@ -11,24 +11,19 @@
} from '$lib/elements/table';
import { Container } from '$lib/layout';
import { toLocaleDateTime } from '$lib/helpers/date';
import { sdkForConsole } from '$lib/stores/sdk';
import { PAGE_LIMIT } from '$lib/constants';
import type { Models } from '@aw-labs/appwrite-console';
export let logs: Models.LogList;
export let path: string;
export let offset = 0;
const getBrowser = (clientCode: string) => {
return sdkForConsole.avatars.getBrowser(clientCode, 40, 40);
};
</script>
<Container>
{#if logs.total}
<TableScroll>
<TableHeader>
<TableCellHead width={150}>Client</TableCellHead>
<TableCellHead width={100}>User</TableCellHead>
<TableCellHead width={100}>Event</TableCellHead>
<TableCellHead width={80}>Location</TableCellHead>
<TableCellHead width={90}>IP</TableCellHead>
@@ -38,28 +33,22 @@
{#each logs.logs as log}
<TableRow>
<TableCell title="Client">
{#if log.clientName}
<div class="u-flex u-cross-center u-gap-12">
<div class="avatar is-small">
<img
height="20"
width="20"
src={getBrowser(log.clientCode).toString()}
alt={log.clientName} />
<div class="u-flex u-cross-center u-gap-12">
{#if log.userEmail}
{#if log.userName}
<AvatarInitials size={32} name={log.userName} />
<Trim>{log.userName}</Trim>
{:else}
<AvatarInitials size={32} name={log.userEmail} />
<Trim>{log.userEmail}</Trim>
{/if}
{:else}
<div class="avatar is-size-small">
<span class="icon-anonymous" aria-hidden="true" />
</div>
<Trim>
{log.clientName}
{log.clientVersion}
on {log.osName}
{log.osVersion}
</Trim>
</div>
{:else}
<div class="u-flex u-cross-center u-gap-12">
<span class="avatar is-color-empty" />
<p class="text u-trim">Unknown</p>
</div>
{/if}
<span class="text u-trim">{log.userName ?? 'Anonymous'}</span>
{/if}
</div>
</TableCell>
<TableCellText title="Event">{log.event}</TableCellText>
+1 -1
View File
@@ -18,7 +18,7 @@
<nav class="breadcrumbs is-only-desktop" aria-label="breadcrumb">
<ol class="breadcrumbs-list">
{#each breadcrumbs as breadcrumb}
<li class="breadcrumbs-item">
<li class="breadcrumbs-item" data-private>
{#if breadcrumb.href}
<a href={breadcrumb.href} title={breadcrumb.title} on:click={track}>
{breadcrumb.title}
+1 -1
View File
@@ -10,7 +10,7 @@
<span class="icon-cheveron-left" aria-hidden="true" />
</a>
{/if}
<span class="text u-trim-1">
<span class="text u-trim-1" data-private>
<slot />
</span>
</Heading>
+55 -34
View File
@@ -1,47 +1,68 @@
<footer class="main-footer">
<div class="container">
<ul class="inline-links is-center">
<script>
import { Mode, MODE } from '$lib/system';
import { version } from '$routes/console/store';
const currentYear = new Date().getFullYear();
</script>
<footer class="main-footer u-cross-center">
<div class="main-footer-start">
<ul class="inline-links is-no-padding-first-and-last u-x-small">
<li class="inline-links-item">
<a
class="link"
href="https://github.com/appwrite/appwrite"
target="_blank"
rel="noopener noreferrer">
<span class="icon-github-circled" aria-hidden="true" />
<span class="text">GitHub</span>
<div class="u-flex u-cross-center u-gap-8">
{#if MODE === Mode.CLOUD}
<span class="icon-cloud" />
{/if}
{#if $version}
<a
class="text"
href="https://github.com/appwrite/appwrite/blob/master/CHANGES.md"
target="_blank"
rel="noreferrer">
Version {$version}
</a>
{/if}
</div>
</li>
<li class="inline-links-item">
<a href="https://appwrite.io/policy/terms" target="_blank" rel="noreferrer">
<span class="text">Terms</span>
</a>
</li>
<li class="inline-links-item">
<a
class="link"
href="https://appwrite.io/discord"
target="_blank"
rel="noopener noreferrer">
<span class="icon-discord" aria-hidden="true" />
<span class="text">Discord</span>
<a href="https://appwrite.io/policy/privacy" target="_blank" rel="noreferrer">
<span class="text">Privacy</span>
</a>
</li>
</ul>
</div>
<div class="main-footer-end">
<ul class="inline-links is-no-padding-first-and-last u-x-small">
<li class="inline-links-item">
<a
class="link"
href="https://github.com/appwrite/appwrite/issues/new"
target="_blank"
rel="noopener noreferrer">
<span class="text">Open an Issue</span>
</a>
<span class="text">ⓒ {currentYear} Appwrite. All rights reserved.</span>
</li>
<li class="inline-links-item">
<a
class="link"
href="https://appwrite.io/docs"
target="_blank"
rel="noopener noreferrer">
<span class="text">Docs</span>
<li class="inline-links-item u-flex u-gap-8">
<a href="https://github.com/appwrite/appwrite" target="_blank" rel="noreferrer">
<span class="icon-github" aria-hidden="true" aria-label="Appwrite on Github" />
</a>
<a href="https://appwrite.io/discord" target="_blank" rel="noreferrer">
<span
class="icon-discord"
aria-hidden="true"
aria-label="Appwrite on Discord" />
</a>
</li>
<li class="inline-links-item">
<span class="text">API:0.13.2</span>
</li>
</ul>
</div>
</footer>
<style lang="scss">
.main-footer {
margin-block-start: auto;
}
[class^='icon-']:not(:hover) {
color: hsl(var(--color-neutral-50));
}
</style>
+1 -1
View File
@@ -47,7 +47,7 @@
<svelte:window on:keydown={handleKeydown} />
{#if $log.data}
<section class="cover-frame">
<section class="cover-frame" data-private>
<header class="cover-frame-header u-flex u-gap-16 u-main-space-between u-cross-center">
<h1 class="body-text-1">Function ID: {$log.func.$id}</h1>
<button
+13 -6
View File
@@ -41,7 +41,6 @@
<main
class:grid-with-side={showSideNavigation}
class:grid={!showSideNavigation}
class:is-open={isOpen}
class:u-hide={$wizard.show || $log.show}>
<header class="main-header u-padding-inline-end-0">
@@ -54,9 +53,11 @@
</button>
<slot name="header" />
</header>
<nav class="main-side">
<slot name="side" />
</nav>
{#if showSideNavigation}
<nav class="main-side">
<slot name="side" />
</nav>
{/if}
<section class="main-content">
{#if $page.data?.header}
<svelte:component this={$page.data.header} />
@@ -64,11 +65,17 @@
<slot />
</section>
<slot name="footer" />
</main>
<style>
.grid-with-side {
<style lang="scss">
main {
min-height: 100vh;
&:not(.grid-with-side) {
display: flex;
flex-direction: column;
}
}
.main-side {
+31
View File
@@ -0,0 +1,31 @@
import type { Models, Payload } from '@aw-labs/appwrite-console';
import { Service } from './service';
export class Project extends Service {
/**
* Get usage stats for a project
*
*
* @param {string} range
* @throws {AppwriteException}
* @returns {Promise}
*/
async getUsage(range?: string): Promise<Models.UsageProject> {
const path = '/project/usage';
const payload: Payload = {};
if (typeof range !== 'undefined') {
payload['range'] = range;
}
const uri = new URL(this.client.config.endpoint + path);
return await this.client.call(
'get',
uri,
{
'content-type': 'application/json'
},
payload
);
}
}
+28
View File
@@ -0,0 +1,28 @@
import type { Client, Payload } from '@aw-labs/appwrite-console';
export class Service {
static CHUNK_SIZE = 5 * 1024 * 1024; // 5MB
client: Client;
constructor(client: Client) {
this.client = client;
}
static flatten(data: Payload, prefix = ''): Payload {
let output: Payload = {};
for (const key in data) {
const value = data[key];
const finalKey = prefix ? `${prefix}[${key}]` : key;
if (Array.isArray(value)) {
output = Object.assign(output, this.flatten(value, finalKey));
} else {
output[finalKey] = value;
}
}
return output;
}
}
+3 -3
View File
@@ -1,5 +1,5 @@
import { browser } from '$app/environment';
import { growthEndpoint } from '$lib/constants';
import { VARS } from '$lib/system';
import { writable } from 'svelte/store';
export type AppStore = {
@@ -64,8 +64,8 @@ function createFeedbackStore() {
email?: string,
value?: number
) => {
if (!growthEndpoint) return;
const response = await fetch(`${growthEndpoint}/feedback`, {
if (!VARS.GROWTH_ENDPOINT) return;
const response = await fetch(`${VARS.GROWTH_ENDPOINT}/feedback`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
+3 -4
View File
@@ -1,3 +1,5 @@
import { Project } from '$lib/sdk/project';
import { VARS } from '$lib/system';
import {
Account,
Avatars,
@@ -12,10 +14,8 @@ import {
Users
} from '@aw-labs/appwrite-console';
import { Project } from './project';
const endpoint = VARS.APPWRITE_ENDPOINT ?? `${globalThis?.location?.origin}/v1`;
const endpoint =
import.meta.env.VITE_APPWRITE_ENDPOINT?.toString() ?? `${globalThis?.location?.origin}/v1`;
const clientConsole = new Client();
clientConsole.setEndpoint(endpoint).setProject('console');
@@ -45,7 +45,6 @@ const sdkForProject = {
health: new Health(clientProject),
locale: new Locale(clientProject),
project: new Project(clientProject),
projects: new Projects(clientProject),
storage: new Storage(clientProject),
teams: new Teams(clientProject),
users: new Users(clientProject)
+25
View File
@@ -0,0 +1,25 @@
export enum Mode {
CLOUD = 'cloud',
SELF_HOSTED = 'self-hosted'
}
export const VARS = {
APPWRITE_ENDPOINT: import.meta.env?.VITE_APPWRITE_ENDPOINT?.toString() as string | undefined,
GROWTH_ENDPOINT: import.meta.env?.VITE_APPWRITE_GROWTH_ENDPOINT?.toString() as
| string
| undefined,
CONSOLE_MODE: import.meta.env?.VITE_CONSOLE_MODE?.toString() as string | undefined,
VERCEL_ENV: import.meta.env?.VITE_VERCEL_ENV?.toString() as string | undefined,
GOOGLE_ANALYTICS: import.meta.env?.VITE_GA_PROJECT?.toString() as string | undefined
};
export const ENV = {
DEV: import.meta.env.DEV,
PROD: import.meta.env.PROD,
PREVIEW: VARS.VERCEL_ENV === 'preview',
TEST: !!import.meta.env?.VITEST
};
export const MODE = VARS.CONSOLE_MODE === Mode.CLOUD ? Mode.CLOUD : Mode.SELF_HOSTED;
export const isCloud = MODE === Mode.CLOUD;
export const isSelfHosted = MODE === Mode.SELF_HOSTED;
+12
View File
@@ -0,0 +1,12 @@
<script>
import { page } from '$app/stores';
import { Heading } from '$lib/components';
import { Button } from '$lib/elements/forms';
import { Unauthenticated } from '$lib/layout';
</script>
<Unauthenticated>
<Heading size="1" tag="h3">{$page.status}</Heading>
<Heading size="3" tag="h4">{$page.error.message}</Heading>
<Button href="/">Back to the console</Button>
</Unauthenticated>
+29 -19
View File
@@ -1,19 +1,21 @@
<script lang="ts">
import { browser } from '$app/environment';
import { afterNavigate, goto } from '$app/navigation';
import { page } from '$app/stores';
import { user } from '$lib/stores/user';
import { onCLS, onFID, onLCP, onFCP, onINP, onTTFB } from 'web-vitals';
import { reportWebVitals } from '$lib/helpers/vitals';
import { onMount } from 'svelte';
import { base } from '$app/paths';
import { browser, dev } from '$app/environment';
import { page } from '$app/stores';
import { isTrackingAllowed, trackPageView } from '$lib/actions/analytics';
import { reportWebVitals } from '$lib/helpers/vitals';
import { Notifications, Progress } from '$lib/layout';
import { app } from '$lib/stores/app';
import { Progress, Notifications } from '$lib/layout';
import { loading } from './store';
import Loading from './loading.svelte';
import { trackPageView } from '$lib/actions/analytics';
import { user } from '$lib/stores/user';
import { ENV, isCloud } from '$lib/system';
import * as Sentry from '@sentry/svelte';
import LogRocket from 'logrocket';
import { BrowserTracing } from '@sentry/tracing';
import { onMount } from 'svelte';
import { onCLS, onFCP, onFID, onINP, onLCP, onTTFB } from 'web-vitals';
import Loading from './loading.svelte';
import { loading } from './store';
if (browser) {
window.VERCEL_ANALYTICS_ID = import.meta.env.VERCEL_ANALYTICS_ID?.toString() ?? false;
@@ -23,7 +25,7 @@
/**
* Reporting Web Vitals.
*/
if (!dev && window.VERCEL_ANALYTICS_ID) {
if (ENV.PROD && window.VERCEL_ANALYTICS_ID) {
onCLS(reportWebVitals);
onFID(reportWebVitals);
onLCP(reportWebVitals);
@@ -32,15 +34,26 @@
onTTFB(reportWebVitals);
}
/**
* Sentry Error Logging
*/
if (!dev) {
if (ENV.PROD) {
/**
* Sentry Error Logging
*/
Sentry.init({
dsn: 'https://c7ce178bdedd486480317b72f282fd39@o1063647.ingest.sentry.io/4504158071422976',
integrations: [new BrowserTracing()],
tracesSampleRate: 1.0
});
/**
* LogRocket
*/
if (isCloud && isTrackingAllowed()) {
LogRocket.init('rgthvf/appwrite', {
dom: {
inputSanitizer: true
}
});
}
}
/**
@@ -59,10 +72,7 @@
}
loading.set(false);
} else {
if (
acceptedRoutes.includes($page.url.pathname) ||
$page.url.pathname.startsWith('/register/invite')
) {
if (acceptedRoutes.some((n) => $page.url.pathname.startsWith(n))) {
await goto(`${base}${$page.url.pathname}${$page.url.search}`);
} else {
await goto(`${base}/login`, {
+7 -4
View File
@@ -1,6 +1,7 @@
import '@appwrite.io/pink';
import '@appwrite.io/pink-icons';
import 'tippy.js/dist/tippy.css';
import LogRocket from 'logrocket';
import { sdkForConsole } from '$lib/stores/sdk';
import { redirect } from '@sveltejs/kit';
import { Dependencies } from '$lib/constants';
@@ -13,6 +14,11 @@ export const load: LayoutLoad = async ({ depends, url }) => {
try {
const account = await sdkForConsole.account.get();
LogRocket.identify(account.$id, {
name: account.name,
email: account.email
});
return {
account,
organizations: sdkForConsole.teams.list()
@@ -28,10 +34,7 @@ export const load: LayoutLoad = async ({ depends, url }) => {
'/auth/oauth2/failure'
];
if (
!acceptedRoutes.includes(url.pathname) &&
!url.pathname.startsWith('/register/invite')
) {
if (!acceptedRoutes.some((n) => url.pathname.startsWith(n))) {
throw redirect(303, '/login');
}
}
+13 -16
View File
@@ -1,18 +1,19 @@
<script lang="ts">
import Shell from '$lib/layout/shell.svelte';
import SideNavigation from '$lib/layout/navigation.svelte';
import Header from '$lib/layout/header.svelte';
import { newOrgModal } from '$lib/stores/organization';
import Create from './createOrganization.svelte';
import { page } from '$app/stores';
import { beforeNavigate } from '$app/navigation';
import { wizard } from '$lib/stores/wizard';
import { page } from '$app/stores';
import { INTERVAL } from '$lib/constants';
import { Logs } from '$lib/layout';
import Footer from '$lib/layout/footer.svelte';
import Header from '$lib/layout/header.svelte';
import SideNavigation from '$lib/layout/navigation.svelte';
import Shell from '$lib/layout/shell.svelte';
import { feedback } from '$lib/stores/app';
import { log } from '$lib/stores/logs';
import { newOrgModal } from '$lib/stores/organization';
import { wizard } from '$lib/stores/wizard';
import { onMount } from 'svelte';
import { loading } from '../store';
import { feedback } from '$lib/stores/app';
import { INTERVAL } from '$lib/constants';
import Create from './createOrganization.svelte';
onMount(() => {
loading.set(false);
@@ -50,14 +51,10 @@
!$page?.params.organization &&
!$page.url.pathname.includes('/console/account') &&
!$page.url.pathname.includes('/console/onboarding')}>
<svelte:fragment slot="header">
<Header />
</svelte:fragment>
<svelte:fragment slot="side">
<SideNavigation />
</svelte:fragment>
<Header slot="header" />
<SideNavigation slot="side" />
<slot />
<footer class="main-footer" />
<Footer slot="footer" />
</Shell>
{#if $wizard.show && $wizard.component}
+11
View File
@@ -0,0 +1,11 @@
import { sdkForConsole } from '$lib/stores/sdk';
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
const response = await fetch(`${sdkForConsole.client.config.endpoint}/health/version`);
const data = await response.json();
return {
version: data?.version ?? null
};
};
+1 -11
View File
@@ -85,13 +85,7 @@
<svelte:fragment slot="aside">
<ul>
<InputText
id="name"
label="Name"
placeholder="Enter name"
autocomplete={false}
autofocus={true}
bind:value={name} />
<InputText id="name" label="Name" placeholder="Enter name" bind:value={name} />
</ul>
</svelte:fragment>
@@ -110,14 +104,12 @@
id="email"
label="Email"
placeholder="Enter email"
autocomplete={false}
bind:value={email} />
{#if email !== $user.email && email}
<InputPassword
id="emailPassword"
label="Password"
placeholder="Enter password"
autocomplete={false}
showPasswordButton={true}
bind:value={emailPassword} />
{/if}
@@ -144,14 +136,12 @@
id="oldPassword"
label="Old password"
placeholder="Enter password"
autocomplete={false}
showPasswordButton={true}
bind:value={oldPassword} />
<InputPassword
id="newPassword"
label="New password"
placeholder="Enter password"
autocomplete={false}
showPasswordButton={true}
bind:value={newPassword} />
</FormList>
@@ -51,7 +51,7 @@
{organization.name}
</svelte:fragment>
{#await avatarList}
<span class="avatar is-color-empty" />
<span class="avatar is-color-empty" />
{:then avatars}
<AvatarGroup size={40} {avatars} />
{/await}
@@ -79,7 +79,7 @@
{session.osVersion}
</Trim>
{:else}
<span class="avatar is-color-empty" />
<span class="avatar is-color-empty" />
<p class="text u-trim">Unknown</p>
{/if}
<div class="is-only-desktop">
@@ -47,7 +47,7 @@
<svelte:fragment slot="header">
{isUser ? 'Leave Organization' : 'Delete Member'}
</svelte:fragment>
<p>
<p data-private>
{isUser
? `Are you sure you want to leave '${selectedMember?.teamName}'?`
: `Are you sure you want to delete ${selectedMember?.userName} from '${selectedMember?.teamName}'?`}
@@ -63,12 +63,12 @@
<AvatarInitials size={32} name={user.name} />
<span class="text u-trim">{user.name}</span>
{:else}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-minus-sm" aria-hidden="true" />
</div>
{/if}
{:else}
<div class="avatar is-size-small ">
<div class="avatar is-size-small">
<span class="icon-anonymous" aria-hidden="true" />
</div>
<span class="text u-trim">{user.name}</span>
@@ -4,62 +4,18 @@
import { CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, InputNumber, InputSelect } from '$lib/elements/forms';
import { secsToUnit, timeToSeconds } from '$lib/helpers/timeConversion';
import { createTimeUnitPair } from '$lib/helpers/unit';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { writable } from 'svelte/store';
import { project } from '../../store';
enum Period {
Days = 'd',
Hours = 'h',
Minutes = 'm',
Seconds = 's'
}
const projectId = $project.$id;
const options = [
{ label: 'days', value: Period.Days },
{ label: 'hours', value: Period.Hours },
{ label: 'minutes', value: Period.Minutes },
{ label: 'seconds', value: Period.Seconds }
];
let period = writable(Period.Seconds);
function getInitialTime() {
let t = $project.authDuration;
if (t >= 86400 && t % 86400 === 0) {
$period = Period.Days;
return t / 86400;
}
if (t >= 3600 && t % 3600 === 0) {
$period = Period.Hours;
return t / 3600;
}
if (t >= 60 && t % 60 === 0) {
$period = Period.Minutes;
return t / 60;
}
return t;
}
let time = getInitialTime();
let timeInSecs = timeToSeconds(time, $period);
period.subscribe((p) => {
time = secsToUnit(timeInSecs, p);
});
$: timeInSecs = timeToSeconds(time, $period);
const { value, unit, baseValue, units } = createTimeUnitPair($project.authDuration);
const options = units.map((v) => ({ label: v.name, value: v.name }));
async function updateSessionLength() {
try {
await sdkForConsole.projects.updateAuthDuration(projectId, timeInSecs);
await sdkForConsole.projects.updateAuthDuration(projectId, $baseValue);
invalidate(Dependencies.PROJECT);
addNotification({
@@ -87,15 +43,15 @@
<svelte:fragment slot="aside">
<form class="form u-grid u-gap-16">
<ul class="form-list is-multiple">
<InputNumber id="length" label="Length" bind:value={time} />
<InputSelect id="period" label="Time Period" bind:value={$period} {options} />
<InputNumber id="length" label="Length" bind:value={$value} min={0} />
<InputSelect id="period" label="Time Period" bind:value={$unit} {options} />
</ul>
</form>
</svelte:fragment>
<svelte:fragment slot="actions">
<Button
disabled={timeToSeconds(time, $period) === $project.authDuration}
disabled={$baseValue === $project.authDuration}
on:click={() => {
updateSessionLength();
}}>
@@ -69,7 +69,7 @@
type="radio"
bind:group={isLimited}
value={false} />
<div class="choice-item-content u-cross-child-center">
<div class="choice-item-content u-cross-child-center">
<div class="choice-item-title">Unlimited</div>
</div>
<Pill>recommended</Pill>
@@ -39,7 +39,7 @@
<Modal bind:show={showDelete} on:submit={deleteMembership} warning>
<svelte:fragment slot="header">Delete Member</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>{selectedMembership.userName}</b> from '{selectedMembership.teamName}'?
</p>
<svelte:fragment slot="footer">
@@ -30,8 +30,7 @@
<Modal bind:show={showDelete} on:submit={deleteTeam} warning>
<svelte:fragment slot="header">Delete Team</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>{team.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -55,7 +55,7 @@
<TableRowLink
href={`${base}/console/project-${project}/auth/user-${membership.userId}`}>
<TableCellText title="Name">
<div class="u-flex u-gap-12">
<div class="u-flex u-gap-12 u-cross-center">
<AvatarInitials size={32} name={membership.userName} />
<span>{membership.userName ? membership.userName : 'n/a'}</span>
</div>
@@ -33,11 +33,11 @@
{/if}
</svelte:fragment>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">
<h6 class="u-bold u-trim-1" data-private>
{$user.name || $user.email || $user.phone || 'Anonymous'}
</h6>
</svelte:fragment>
<p class="u-trim-1">
<p class="u-trim-1" data-private>
{$user.email && $user.phone
? [$user.email, $user.phone].join(',')
: $user.email || $user.phone}
@@ -33,7 +33,7 @@
<Modal bind:show={showDeleteAll} on:submit={deleteAllSessions} warning>
<svelte:fragment slot="header">Delete All Sessions</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>all of {$user.name}'s sessions?</b>
</p>
<svelte:fragment slot="footer">
@@ -43,7 +43,7 @@
<Modal bind:show={showDelete} on:submit={deleteMembership} warning>
<svelte:fragment slot="header">Delete Member</svelte:fragment>
{#if selectedMembership}
<p>
<p data-private>
Are you sure you want to delete <b>{selectedMembership.userName}</b> from '{selectedMembership.teamName}'?
</p>
{/if}
@@ -34,7 +34,7 @@
<Modal bind:show={showDelete} on:submit={deleteUser} warning>
<svelte:fragment slot="header">Delete User</svelte:fragment>
<p>Are you sure you want to delete <b>{$user.name}</b> from '{$project.name}'?</p>
<p data-private>Are you sure you want to delete <b>{$user.name}</b> from '{$project.name}'?</p>
<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
<Button secondary submit>Delete</Button>
@@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/stores';
import { base } from '$app/paths';
import { Empty, AvatarInitials } from '$lib/components';
import { AvatarInitials, EmptySearch } from '$lib/components';
import {
Table,
TableHeader,
@@ -41,7 +41,7 @@
<TableRowLink
href={`${base}/console/project-${project}/auth/teams/team-${membership.teamId}`}>
<TableCellText title="Name">
<div class="u-flex u-gap-12">
<div class="u-flex u-cross-center u-gap-12">
<AvatarInitials size={32} name={membership.teamName} />
<span>{membership.teamName ? membership.teamName : 'n/a'}</span>
</div>
@@ -67,19 +67,15 @@
</TableBody>
</Table>
{:else}
<Empty single>
<p>No memberships available</p>
<Button
external
secondary
href="https://appwrite.io/docs/server/users?sdk=nodejs-default#usersListMemberships">
Documentation
</Button>
</Empty>
<EmptySearch>
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
<p class="text u-line-height-1-5">No memberships available</p>
<Button external secondary href="https://appwrite.io/docs/client/teams">
Documentation
</Button>
</div>
</EmptySearch>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {data.memberships.total}</p>
</div>
</Container>
<DeleteMembership {selectedMembership} bind:showDelete />
@@ -1,5 +1,5 @@
<script lang="ts">
import { Empty } from '$lib/components';
import { EmptySearch } from '$lib/components';
import {
Table,
TableBody,
@@ -28,7 +28,7 @@
<Container>
{#if data.sessions.total}
<div class="u-flex u-main-end common-section">
<div class="u-flex u-main-end common-section">
<Button secondary on:click={() => (showDeleteAll = true)}>
<span class="text">Delete All</span>
</Button>
@@ -84,19 +84,18 @@
</TableBody>
</Table>
{:else}
<Empty single>
<p>No sessions available</p>
<Button
external
secondary
href="https://appwrite.io/docs/server/auth?sdk=nodejs-default#usersGetSessions">
Documentation
</Button>
</Empty>
<EmptySearch>
<div class="u-flex u-flex-vertical u-cross-center u-gap-24">
<p class="text u-line-height-1-5">No sessions available</p>
<Button
external
secondary
href="https://appwrite.io/docs/server/auth?sdk=nodejs-default#usersGetSessions">
Documentation
</Button>
</div>
</EmptySearch>
{/if}
<div class="u-flex u-margin-block-start-32 u-main-space-between">
<p class="text">Total results: {data.sessions.total}</p>
</div>
</Container>
<DeleteSessions {selectedSessionId} bind:showDelete />
@@ -38,7 +38,7 @@
<Heading tag="h6" size="7">Update Name</Heading>
<svelte:fragment slot="aside">
<ul>
<ul data-private>
<InputText
id="name"
label="Name"
@@ -74,7 +74,7 @@
</script>
<CardGrid>
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16" data-private>
{#if $user.email || $user.phone}
{#if $user.name}
<AvatarInitials size={48} name={$user.name} />
@@ -92,7 +92,7 @@
</div>
<svelte:fragment slot="aside">
<div class="u-flex u-main-space-between">
<div>
<div data-private>
{#if $user.email}
<p class="title">{$user.email}</p>
{/if}
@@ -57,7 +57,7 @@
formattedColumn = column;
} else if (Array.isArray(column)) {
formattedColumn = formatArray(column);
} else if (!column) {
} else if (column === null) {
formattedColumn = 'n/a';
} else {
formattedColumn = `${column}`;
@@ -44,7 +44,7 @@
<Modal warning={true} bind:show={showDelete} on:submit={handleDelete}>
<svelte:fragment slot="header">Delete Attribute</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>'{selectedAttribute?.key}' from {$collection?.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -54,6 +54,11 @@
key = null;
selectedOption = null;
$option = null;
data = {
required: false,
array: false,
default: null
};
}
</script>
@@ -10,7 +10,7 @@
export let attributes: Attributes[] = [];
export let formValues: object = {};
export let customId: string | null | undefined = undefined;
export let gap: '16' | '32' = '32';
export let gap: '16' | '24' | '32' = '32';
let showCustomId = false;
@@ -37,7 +37,7 @@
{#if attributes.length}
<ul class={`form-list u-gap-${gap}`}>
{#each attributes as attribute}
{@const label = attribute.required ? `${attribute.key}*` : attribute.key}
{@const label = attribute.key}
{#if attribute.array}
{#if formValues[attribute.key].length === 0}
<div class="u-flex u-cross-center u-main-space-between">
@@ -71,6 +71,7 @@
</div>
<div class="form-item-part u-cross-child-end">
<Button
noMargin
text
on:click={() => removeArrayItem(attribute.key, index)}>
<span class="icon-x" aria-hidden="true" />
@@ -78,11 +79,11 @@
</div>
</li>
{/each}
<Button text noMargin on:click={() => addArrayItem(attribute.key)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text"> Add item</span>
</Button>
</ul>
<Button text noMargin on:click={() => addArrayItem(attribute.key)}>
<span class="icon-plus" aria-hidden="true" />
<span class="text"> Add item</span>
</Button>
{/if}
{:else}
<FormList>
@@ -13,6 +13,7 @@
{id}
{label}
{optionalText}
placeholder="Enter string"
showLabel={!!label?.length}
required={attribute.required}
maxlength={attribute.size}
@@ -42,7 +42,7 @@
<Modal warning={true} on:submit={handleDelete} bind:show={showDelete}>
<svelte:fragment slot="header">Delete Document</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>the document from {$collection.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -42,7 +42,7 @@
<Modal warning={true} on:submit={handleDelete} bind:show={showDelete}>
<svelte:fragment slot="header">Delete Index</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>'{selectedIndex.key}' from {$collection.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -38,7 +38,7 @@
<Modal warning={true} bind:show={showDelete} on:submit={handleDelete}>
<svelte:fragment slot="header">Delete Collection</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete <b>{$collection.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -13,5 +13,6 @@
<AttributeForm
bind:formValues={$createDocument.document}
attributes={$createDocument.attributes}
bind:customId={$createDocument.id} />
bind:customId={$createDocument.id}
gap="24" />
</WizardStep>
@@ -34,7 +34,7 @@
<Modal warning={true} bind:show={showDelete} on:submit={handleDelete}>
<svelte:fragment slot="header">Delete Database</svelte:fragment>
<p class="text">
<p class="text" data-private>
Are you sure you want to delete <b>{$database.name}</b>?
</p>
<svelte:fragment slot="footer">
@@ -1,17 +1,17 @@
<script lang="ts">
import { page } from '$app/stores';
import { Card, CardGrid, Box, Heading, AvatarInitials } from '$lib/components';
import { Container } from '$lib/layout';
import { Button, InputText, Helper } from '$lib/elements/forms';
import { sdkForProject } from '$lib/stores/sdk';
import { addNotification } from '$lib/stores/notifications';
import { database } from '../store';
import { onMount } from 'svelte';
import { toLocaleDateTime } from '$lib/helpers/date';
import { invalidate } from '$app/navigation';
import { page } from '$app/stores';
import { Submit, trackError, trackEvent } from '$lib/actions/analytics';
import { Box, Card, CardGrid, Heading } from '$lib/components';
import { Dependencies } from '$lib/constants';
import { Button, Helper, InputText } from '$lib/elements/forms';
import { toLocaleDateTime } from '$lib/helpers/date';
import { Container } from '$lib/layout';
import { addNotification } from '$lib/stores/notifications';
import { sdkForProject } from '$lib/stores/sdk';
import { onMount } from 'svelte';
import Delete from '../delete.svelte';
import { Submit, trackEvent, trackError } from '$lib/actions/analytics';
import { database } from '../store';
let showDelete = false;
let showError: false | 'name' | 'email' | 'password' = false;
@@ -51,7 +51,6 @@
<div class="common-section grid-1-2">
<div class="grid-1-2-col-1">
<div class="grid-1-2-col-1 u-flex u-cross-center u-gap-16">
<AvatarInitials size={48} name={$database.name} />
<Heading tag="h6" size="7">{$database.name}</Heading>
</div>
</div>
@@ -99,9 +98,6 @@
</p>
<svelte:fragment slot="aside">
<Box>
<svelte:fragment slot="image">
<AvatarInitials size={48} name={$database.name} />
</svelte:fragment>
<svelte:fragment slot="title">
<h6 class="u-bold u-trim-1">{$database.name}</h6>
<span>Last Updated: {toLocaleDateTime($database.$updatedAt)}</span>
@@ -36,7 +36,7 @@
<Modal bind:show={showDelete} on:submit={handleSubmit} warning>
<svelte:fragment slot="header">Delete Deployment</svelte:fragment>
<p>Are you sure you want to delete this deployment?</p>
<p data-private>Are you sure you want to delete this deployment?</p>
<svelte:fragment slot="footer">
<Button text on:click={() => (showDelete = false)}>Cancel</Button>
<Button secondary submit>Delete</Button>
@@ -33,7 +33,7 @@
<Modal bind:show={showDelete} on:submit={handleSubmit} warning>
<svelte:fragment slot="header">Delete Function</svelte:fragment>
<p>
<p data-private>
Are you sure you want to delete this function and all associated deployments from your
project?
</p>
@@ -5,9 +5,7 @@
import { BarChart } from '$lib/charts';
import { page } from '$app/stores';
import type { PageData } from './$types';
export let data: PageData;
$: count = data.executionsTotal;
</script>
@@ -10,6 +10,7 @@ export const load: PageLoad = async ({ params, parent }) => {
);
return {
executionsTotal: response.executionsTotal as unknown as Models.Metric[]
count: response.executionsTotal as unknown as Models.Metric[],
errors: response.executionsFailure as unknown as Models.Metric[]
};
};
@@ -65,8 +65,7 @@
<Onboard {projectId} />
{:else}
{#if $usage}
{@const storage = humanFileSize(total($usage.filesStorage))}
{@const storage = humanFileSize(total($usage.storage) ?? 0)}
<section class="common-section">
<div class="grid-dashboard-1s-2m-6l">
<div class="card is-2-columns-medium-screen is-3-columns-large-screen">
@@ -90,14 +89,14 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(total($usage.documentsTotal))}
{format(total($usage.documents) ?? 0)}
</div>
<div>Documents</div>
</div>
<div class="grid-item-1-end-end">
<div class="text">
Databases: {format(total($usage.databasesTotal))}
Databases: {format(total($usage.databases) ?? 0)}
</div>
</div>
</div>
@@ -125,7 +124,7 @@
<div class="grid-item-1-end-end">
<div class="text">
Buckets: {format(total($usage.bucketsTotal))}
Buckets: {format(total($usage.buckets) ?? 0)}
</div>
</div>
</div>
@@ -145,7 +144,7 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(total($usage.usersTotal))}
{format(total($usage.users) ?? 0)}
</div>
<div>Users</div>
</div>
@@ -166,7 +165,7 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(total($usage.executionsTotal))}
{format(total($usage.executions) ?? 0)}
</div>
<div>Executions</div>
</div>
@@ -81,5 +81,5 @@
</svelte:fragment>
</InputSelect>
{#if expirationSelect === 'custom'}
<InputDateTime id="expire" label="" bind:value={expirationCustom} showLabel={false} />
<InputDateTime required id="expire" label="" bind:value={expirationCustom} showLabel={false} />
{/if}
@@ -97,7 +97,8 @@
{#if data.platforms.platforms.length}
<div
class="grid-box u-margin-block-start-32"
style="--grid-gap:1.5rem; --grid-item-size:20rem;">
style="--grid-gap:1.5rem; --grid-item-size:20rem;"
data-private>
{#each data.platforms.platforms as platform}
<a class="card" href={`${path}/${platform.$id}`}>
<div class="grid-item-1" style="min-block-size: calc(182 / 16 * 1rem)">
@@ -26,16 +26,7 @@
async function beforeSubmit() {
if ($createPlatform.$id) {
await sdkForConsole.projects.updatePlatform(
projectId,
$createPlatform.$id,
$createPlatform.name,
$createPlatform.key,
$createPlatform.store,
$createPlatform.hostname
);
return;
await sdkForConsole.projects.deletePlatform(projectId, $createPlatform.$id);
}
const response = await sdkForConsole.projects.createPlatform(
@@ -52,6 +43,7 @@
});
$createPlatform.$id = response.$id;
$createPlatform.type = platform;
}
</script>
@@ -22,7 +22,13 @@
Web = 'flutter-web'
}
let platform: Platform = Platform.Android;
function isPlatform(value: string): value is Platform {
return Object.values(Platform).includes(value as Platform);
}
let platform: Platform = isPlatform($createPlatform.type)
? $createPlatform.type
: Platform.Android;
const projectId = $page.params.project;
const suggestions = ['*.vercel.app', '*.netlify.app', '*.gitpod.io'];
@@ -81,16 +87,7 @@
async function beforeSubmit() {
if ($createPlatform.$id) {
await sdkForConsole.projects.updatePlatform(
projectId,
$createPlatform.$id,
$createPlatform.name,
$createPlatform.key,
$createPlatform.store,
$createPlatform.hostname
);
return;
await sdkForConsole.projects.deletePlatform(projectId, $createPlatform.$id);
}
const response = await sdkForConsole.projects.createPlatform(
@@ -107,6 +104,7 @@
});
$createPlatform.$id = response.$id;
$createPlatform.type = platform;
}
</script>
@@ -136,7 +134,7 @@
button
on:click={() => (platform = Platform.Macos)}
selected={platform === Platform.Macos}>
Mac OS
macOS
</Pill>
<Pill
button
@@ -1,7 +1,8 @@
import { sdkForProject } from '$lib/stores/sdk';
import { sdkForConsole, sdkForProject } from '$lib/stores/sdk';
import { cachedStore } from '$lib/helpers/cache';
import type { Models } from '@aw-labs/appwrite-console';
import { writable, type Writable } from 'svelte/store';
import { isCloud } from '$lib/system';
export const usage = cachedStore<
Models.UsageProject,
@@ -10,8 +11,10 @@ export const usage = cachedStore<
}
>('projectUsage', function ({ set }) {
return {
load: async (range) => {
const usages = await sdkForProject.project.getUsage(range);
load: async (projectId, range) => {
const usages = isCloud
? await sdkForProject.project.getUsage(range)
: await sdkForConsole.projects.getUsage(projectId, range);
set(usages);
}
};
@@ -35,7 +35,7 @@
<Modal bind:show={showDelete} on:submit={deleteDomain} warning>
<svelte:fragment slot="header">Delete Domain</svelte:fragment>
{#if selectedDomain}
<p>
<p data-private>
Are you sure you want to delete <b>{selectedDomain.domain}</b> from '{$project.name}'?
</p>
{/if}
@@ -33,7 +33,7 @@
required
bind:value={$domain.domain} />
</FormList>
<div class="common-section ">
<div class="common-section">
<p>
You can find a list of domain providers and their DNS setting documentation <a
class="link"
Loaded 100 of 118 files, more files were not shown because too many files have changed in this diff. Show more