Merge branch 'main' of github.com:appwrite/console into billing

This commit is contained in:
Arman
2023-02-24 12:05:53 +01:00
31 changed files with 1025 additions and 173 deletions
+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
+64 -64
View File
@@ -25,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",
@@ -34,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",
@@ -50,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"
@@ -1987,17 +1987,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"
@@ -2008,9 +2008,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"
@@ -2529,23 +2529,23 @@
}
},
"node_modules/@vitest/expect": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.28.4.tgz",
"integrity": "sha512-JqK0NZ4brjvOSL8hXAnIsfi+jxDF7rH/ZWCGCt0FAqRnVFc1hXsfwXksQvEnKqD84avRt3gmeXoK4tNbmkoVsQ==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-0.28.5.tgz",
"integrity": "sha512-gqTZwoUTwepwGIatnw4UKpQfnoyV0Z9Czn9+Lo2/jLIt4/AXLTn+oVZxlQ7Ng8bzcNkR+3DqLJ08kNr8jRmdNQ==",
"dev": true,
"dependencies": {
"@vitest/spy": "0.28.4",
"@vitest/utils": "0.28.4",
"@vitest/spy": "0.28.5",
"@vitest/utils": "0.28.5",
"chai": "^4.3.7"
}
},
"node_modules/@vitest/runner": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.28.4.tgz",
"integrity": "sha512-Q8UV6GjDvBSTfUoq0QXVCNpNOUrWu4P2qvRq7ssJWzn0+S0ojbVOxEjMt+8a32X6SdkhF8ak+2nkppsqV0JyNQ==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-0.28.5.tgz",
"integrity": "sha512-NKkHtLB+FGjpp5KmneQjTcPLWPTDfB7ie+MmF1PnUBf/tGe2OjGxWyB62ySYZ25EYp9krR5Bw0YPLS/VWh1QiA==",
"dev": true,
"dependencies": {
"@vitest/utils": "0.28.4",
"@vitest/utils": "0.28.5",
"p-limit": "^4.0.0",
"pathe": "^1.1.0"
}
@@ -2578,18 +2578,18 @@
}
},
"node_modules/@vitest/spy": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.28.4.tgz",
"integrity": "sha512-8WuhfXLlvCXpNXEGJW6Gc+IKWI32435fQJLh43u70HnZ1otJOa2Cmg2Wy2Aym47ZnNCP4NolF+8cUPwd0MigKQ==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-0.28.5.tgz",
"integrity": "sha512-7if6rsHQr9zbmvxN7h+gGh2L9eIIErgf8nSKYDlg07HHimCxp4H6I/X/DPXktVPPLQfiZ1Cw2cbDIx9fSqDjGw==",
"dev": true,
"dependencies": {
"tinyspy": "^1.0.2"
}
},
"node_modules/@vitest/ui": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.28.4.tgz",
"integrity": "sha512-LQfCCFc17n49mwtraV9/NAWl2DUqJS/9ZEa3fqJjoYO+HowdseQ5jvWflpzliCyfrIAh6cXVo1bNzHnDXe0cbw==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-0.28.5.tgz",
"integrity": "sha512-hzzZzv38mH/LMFh54QEJpWFuGixZZBOD+C0fHU81d1lsvochPwNZhWJbuRJQNyZLSMZYCYW4hF6PpNQJXDHDmg==",
"dev": true,
"dependencies": {
"fast-glob": "^3.2.12",
@@ -2600,9 +2600,9 @@
}
},
"node_modules/@vitest/utils": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.28.4.tgz",
"integrity": "sha512-l2QztOLdc2LkR+w/lP52RGh8hW+Ul4KESmCAgVE8q737I7e7bQoAfkARKpkPJ4JQtGpwW4deqlj1732VZD7TFw==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-0.28.5.tgz",
"integrity": "sha512-UyZdYwdULlOa4LTUSwZ+Paz7nBHGTT72jKwdFSV4IjHF1xsokp+CabMdhjvVhYwkLfO88ylJT46YMilnkSARZA==",
"dev": true,
"dependencies": {
"cli-truncate": "^3.1.0",
@@ -3548,9 +3548,9 @@
}
},
"node_modules/devalue": {
"version": "4.2.3",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-4.2.3.tgz",
"integrity": "sha512-JG6Q248aN0pgFL57e3zqTVeFraBe+5W2ugvv1mLXsJP6YYIYJhRZhAl7QP8haJrqob6X10F9NEkuCvNILZTPeQ==",
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/devalue/-/devalue-4.3.0.tgz",
"integrity": "sha512-n94yQo4LI3w7erwf84mhRUkUJfhLoCZiLyoOZ/QFsDbcWNZePrLwbQpvZBUG2TNxwV3VjCKPxkiiQA6pe3TrTA==",
"dev": true
},
"node_modules/diff": {
@@ -6455,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": {
@@ -6940,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": {
@@ -8131,9 +8131,9 @@
}
},
"node_modules/tinyspy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.0.2.tgz",
"integrity": "sha512-bSGlgwLBYf7PnUsQ6WOc6SJ3pGOcd+d8AA6EUnLDDM0kWEstC1JIlSZA3UNliDXhd9ABoS7hiRBDCu+XP/sf1Q==",
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/tinyspy/-/tinyspy-1.1.1.tgz",
"integrity": "sha512-UVq5AXt/gQlti7oxoIg5oi/9r0WpF7DGEVwXgqWSMmyN16+e3tl5lIvTaOpJ3TAtu5xFzWccFRM4R5NaWHF+4g==",
"dev": true,
"engines": {
"node": ">=14.0.0"
@@ -8292,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": {
@@ -8393,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",
@@ -8442,9 +8442,9 @@
}
},
"node_modules/vite-node": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.28.4.tgz",
"integrity": "sha512-KM0Q0uSG/xHHKOJvVHc5xDBabgt0l70y7/lWTR7Q0pR5/MrYxadT+y32cJOE65FfjGmJgxpVEEY+69btJgcXOQ==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/vite-node/-/vite-node-0.28.5.tgz",
"integrity": "sha512-LmXb9saMGlrMZbXTvOveJKwMTBTNUH66c8rJnQ0ZPNX+myPEol64+szRzXtV5ORb0Hb/91yq+/D3oERoyAt6LA==",
"dev": true,
"dependencies": {
"cac": "^6.7.14",
@@ -8491,18 +8491,18 @@
}
},
"node_modules/vitest": {
"version": "0.28.4",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-0.28.4.tgz",
"integrity": "sha512-sfWIy0AdlbyGRhunm+TLQEJrFH9XuRPdApfubsyLcDbCRrUX717BRQKInTgzEfyl2Ipi1HWoHB84Nqtcwxogcg==",
"version": "0.28.5",
"resolved": "https://registry.npmjs.org/vitest/-/vitest-0.28.5.tgz",
"integrity": "sha512-pyCQ+wcAOX7mKMcBNkzDwEHRGqQvHUl0XnoHR+3Pb1hytAHISgSxv9h0gUiSiYtISXUU3rMrKiKzFYDrI6ZIHA==",
"dev": true,
"dependencies": {
"@types/chai": "^4.3.4",
"@types/chai-subset": "^1.3.3",
"@types/node": "*",
"@vitest/expect": "0.28.4",
"@vitest/runner": "0.28.4",
"@vitest/spy": "0.28.4",
"@vitest/utils": "0.28.4",
"@vitest/expect": "0.28.5",
"@vitest/runner": "0.28.5",
"@vitest/spy": "0.28.5",
"@vitest/utils": "0.28.5",
"acorn": "^8.8.1",
"acorn-walk": "^8.2.0",
"cac": "^6.7.14",
@@ -8518,7 +8518,7 @@
"tinypool": "^0.3.1",
"tinyspy": "^1.0.2",
"vite": "^3.0.0 || ^4.0.0",
"vite-node": "0.28.4",
"vite-node": "0.28.5",
"why-is-node-running": "^2.2.2"
},
"bin": {
+3 -3
View File
@@ -36,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",
@@ -45,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",
@@ -61,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": [
+4 -4
View File
@@ -1,10 +1,10 @@
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 { AppwriteException } from '@aw-labs/appwrite-console';
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 analytics = Analytics({
app: 'appwrite',
+1 -1
View File
@@ -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}
+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/console" 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>
+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 {
+5 -2
View File
@@ -6,6 +6,9 @@
import { base } from '$app/paths';
import { user } from '$lib/stores/user';
export let imgLight = LoginLight;
export let imgDark = LoginDark;
const technologies = [
'js',
'flutter',
@@ -38,9 +41,9 @@
class="container u-margin-block-start-20 is-not-mobile"
style="--p-container-max-size: var(--container-size-large);">
{#if $app.themeInUse === 'dark'}
<img src={LoginDark} alt="" class="u-only-dark" />
<img src={imgDark} alt="" class="u-only-dark" />
{:else}
<img src={LoginLight} alt="" class="u-only-light" />
<img src={imgLight} alt="" class="u-only-light" />
{/if}
</div>
+1 -1
View File
@@ -58,7 +58,7 @@
</div>
<Card>
{#if count}
<Heading tag="h6" size="6">{last(count).value}</Heading>
<Heading tag="h6" size="6">{total(count)}</Heading>
<p>{countMetadata.title}</p>
<div class="u-margin-block-start-16" />
<div class="chart-container">
+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 -1
View File
@@ -1,3 +1,4 @@
import { Project } from '$lib/sdk/project';
import { VARS } from '$lib/system';
import {
Account,
@@ -15,6 +16,7 @@ import {
import { Billing } from './billing';
const endpoint = VARS.APPWRITE_ENDPOINT ?? `${globalThis?.location?.origin}/v1`;
const clientConsole = new Client();
clientConsole.setEndpoint(endpoint).setProject('console');
@@ -44,7 +46,7 @@ const sdkForProject = {
functions: new Functions(clientProject),
health: new Health(clientProject),
locale: new Locale(clientProject),
projects: new Projects(clientProject),
project: new Project(clientProject),
storage: new Storage(clientProject),
teams: new Teams(clientProject),
users: new Users(clientProject)
+2
View File
@@ -22,3 +22,5 @@ export const ENV = {
};
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>
+12 -12
View File
@@ -1,19 +1,19 @@
<script lang="ts">
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 { app } from '$lib/stores/app';
import { Progress, Notifications } from '$lib/layout';
import { loading } from './store';
import Loading from './loading.svelte';
import { afterNavigate, goto } from '$app/navigation';
import { base } from '$app/paths';
import { page } from '$app/stores';
import { trackPageView } from '$lib/actions/analytics';
import { reportWebVitals } from '$lib/helpers/vitals';
import { Notifications, Progress } from '$lib/layout';
import { app } from '$lib/stores/app';
import { user } from '$lib/stores/user';
import * as Sentry from '@sentry/svelte';
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;
@@ -59,7 +59,7 @@
}
loading.set(false);
} else {
if (acceptedRoutes.includes($page.url.pathname)) {
if (acceptedRoutes.some((n) => $page.url.pathname.startsWith(n))) {
await goto(`${base}${$page.url.pathname}${$page.url.search}`);
} else {
await goto(`${base}/login`, {
+1 -1
View File
@@ -28,7 +28,7 @@ export const load: LayoutLoad = async ({ depends, url }) => {
'/auth/oauth2/failure'
];
if (!acceptedRoutes.includes(url.pathname)) {
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
};
};
@@ -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}`;
@@ -1,7 +1,7 @@
<script lang="ts">
import { Container } from '$lib/layout';
import { Card, SecondaryTabsItem, SecondaryTabs, Heading } from '$lib/components';
import { last } from '$lib/layout/usage.svelte';
import { total } from '$lib/layout/usage.svelte';
import { BarChart } from '$lib/charts';
import { page } from '$app/stores';
import type { PageData } from './$types';
@@ -35,7 +35,7 @@
</div>
{#if count}
<Card>
<Heading tag="h6" size="6">{last(count).value}</Heading>
<Heading tag="h6" size="6">{total(count)}</Heading>
<p>Executions</p>
<div class="u-margin-block-start-16" />
<BarChart
@@ -49,7 +49,7 @@
{/if}
{#if errors}
<Card>
<Heading tag="h6" size="6">{last(errors).value}</Heading>
<Heading tag="h6" size="6">{total(errors)}</Heading>
<p>Errors</p>
<div class="u-margin-block-start-16" />
<BarChart
@@ -11,6 +11,6 @@ export const load: PageLoad = async ({ params, parent }) => {
return {
count: response.executionsTotal as unknown as Models.Metric[],
errors: response.buildsFailure as unknown as Models.Metric[]
errors: response.executionsFailure as unknown as Models.Metric[]
};
};
@@ -67,7 +67,7 @@
<Onboard {projectId} />
{:else}
{#if $usage}
{@const storage = humanFileSize(last($usage.storage)?.value ?? 0)}
{@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">
@@ -91,14 +91,14 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.documents)?.value ?? 0)}
{format(total($usage.documents) ?? 0)}
</div>
<div>Documents</div>
</div>
<div class="grid-item-1-end-end">
<div class="text">
Databases: {format(last($usage.databases)?.value ?? 0)}
Databases: {format(total($usage.databases) ?? 0)}
</div>
</div>
</div>
@@ -126,7 +126,7 @@
<div class="grid-item-1-end-end">
<div class="text">
Buckets: {format(last($usage.buckets)?.value ?? 0)}
Buckets: {format(total($usage.buckets) ?? 0)}
</div>
</div>
</div>
@@ -146,7 +146,7 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.users)?.value ?? 0)}
{format(total($usage.users) ?? 0)}
</div>
<div>Users</div>
</div>
@@ -167,7 +167,7 @@
<div class="grid-item-1-end-start">
<div class="heading-level-4">
{format(last($usage.executions)?.value ?? 0)}
{format(total($usage.executions) ?? 0)}
</div>
<div>Executions</div>
</div>
@@ -134,7 +134,7 @@
button
on:click={() => (platform = Platform.Macos)}
selected={platform === Platform.Macos}>
Mac OS
macOS
</Pill>
<Pill
button
@@ -1,7 +1,8 @@
import { sdkForConsole } 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,
@@ -11,7 +12,9 @@ export const usage = cachedStore<
>('projectUsage', function ({ set }) {
return {
load: async (projectId, range) => {
const usages = await sdkForConsole.projects.getUsage(projectId, range);
const usages = isCloud
? await sdkForProject.project.getUsage(range)
: await sdkForConsole.projects.getUsage(projectId, range);
set(usages);
}
};
+4
View File
@@ -0,0 +1,4 @@
import { page } from '$app/stores';
import { derived } from 'svelte/store';
export const version = derived(page, ($page) => $page.data.version as string | null);
+9
View File
@@ -0,0 +1,9 @@
import { isSelfHosted } from '$lib/system';
import { error } from '@sveltejs/kit';
import type { LayoutLoad } from './$types';
export const load: LayoutLoad = async () => {
if (isSelfHosted) {
throw error(501, 'Not available in self hosted.');
}
};
@@ -0,0 +1,147 @@
<script lang="ts">
import { goto, invalidate } from '$app/navigation';
import { base } from '$app/paths';
import {
Button,
Form,
FormItem,
InputChoice,
InputEmail,
InputPassword,
InputText
} from '$lib/elements/forms';
import { addNotification } from '$lib/stores/notifications';
import { sdkForConsole } from '$lib/stores/sdk';
import { Unauthenticated } from '$lib/layout';
import FormList from '$lib/elements/forms/formList.svelte';
import { Dependencies } from '$lib/constants';
import { trackEvent } from '$lib/actions/analytics';
import { onMount } from 'svelte';
import { page } from '$app/stores';
import LoginLight from '$lib/images/login/login-light-mode.svg';
import LoginDark from '$lib/images/login/login-dark-mode.svg';
import { isCloud } from '$lib/system';
let slug = $page.params.slug;
let imgLight = LoginLight;
let imgDark = LoginDark;
onMount(async () => {
if (isCloud) {
switch (slug) {
case 'mlh':
imgDark = (await import('./mlh-dark.svg')).default;
imgLight = (await import('./mlh-light.svg')).default;
title = 'Welcome MLH Hackers!';
break;
case 'appwrite':
imgDark = (await import('$lib/images/appwrite.svg')).default;
imgLight = (await import('$lib/images/appwrite.svg')).default;
title = 'Welcome Appwriters!';
break;
}
}
});
let name: string, mail: string, pass: string, code: string, disabled: boolean;
let title = 'Sign up';
let terms = false;
async function invite() {
try {
disabled = true;
const res = await fetch(`${sdkForConsole.client.config.endpoint}/account/invite`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
userId: 'unique()',
email: mail,
password: pass,
code: code,
name: name ?? ''
})
});
if (!res.ok) {
throw new Error((await res.json()).message);
} else {
await sdkForConsole.account.createEmailSession(mail, pass);
await sdkForConsole.account.updatePrefs({ code });
await invalidate(Dependencies.ACCOUNT);
await goto(`${base}/console`);
trackEvent('submit_account_create', { code: code });
}
} catch (error) {
disabled = false;
addNotification({
type: 'error',
message: error.message
});
}
}
</script>
<svelte:head>
<title>Sign up - Appwrite</title>
</svelte:head>
<Unauthenticated {imgLight} {imgDark}>
<svelte:fragment slot="title">{title}</svelte:fragment>
<svelte:fragment>
<Form on:submit={invite}>
<FormList>
<InputText
id="name"
label="Name"
placeholder="Your name"
autofocus={true}
bind:value={name} />
<InputEmail
id="email"
label="Email"
placeholder="Your email"
required={true}
bind:value={mail} />
<InputPassword
id="password"
label="Password"
placeholder="Your password"
required={true}
showPasswordButton={true}
bind:value={pass} />
<InputText
id="Code"
label="Code"
placeholder="Your code"
required={true}
bind:value={code} />
<InputChoice required value={terms} id="terms" label="terms" showLabel={false}>
By registering, you agree that you have read, understand, and acknowledge our <a
class="link"
href="https://appwrite.io/policy/privacy"
target="_blank"
rel="noopener noreferrer">
Privacy Policy</a>
and accept our
<a
class="link"
href="https://appwrite.io/policy/terms"
target="_blank"
rel="noopener noreferrer">General Terms of Use</a
>.</InputChoice>
<FormItem>
<Button fullWidth submit {disabled}>Sign up</Button>
</FormItem>
</FormList>
</Form>
</svelte:fragment>
<svelte:fragment slot="links">
<li class="inline-links-item">
<span class="text">
Already got an account? <a class="link" href={`${base}/login`}>Sign in</a>
</span>
</li>
</svelte:fragment>
</Unauthenticated>
File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 66 KiB

+3
View File
@@ -10,6 +10,9 @@ const config = {
accessors: process.env.VITEST
},
kit: {
alias: {
$routes: './src/routes'
},
adapter: adapter({
fallback: 'index.html'
}),
+6 -7
View File
@@ -1,7 +1,7 @@
import { defineConfig } from 'vitest/config';
import { sveltekit } from '@sveltejs/kit/vite';
/** @type {import('vite').UserConfig} */
const config = {
const config = defineConfig({
plugins: [sveltekit()],
optimizeDeps: {
include: ['echarts', 'prismjs']
@@ -18,16 +18,15 @@ const config = {
server: {
port: 3000
}
};
});
/** @type {import('vite').UserConfig} */
const testConfig = {
const testConfig = defineConfig({
resolve: {
// hotfix for https://github.com/vitest-dev/vitest/issues/2834
conditions: ['browser']
},
test: {
include: ['tests/**/*.test.ts'],
include: ['tests/unit/**/*.test.ts'],
environment: 'jsdom',
globals: true,
threads: true,
@@ -36,7 +35,7 @@ const testConfig = {
inline: ['@analytics/type-utils']
}
}
};
});
export default process.env.VITEST
? {