Merge branch 'main' into feat-stripe-subscription-quickstart

This commit is contained in:
Vincent (Wen Yu) Ge
2024-01-03 15:37:23 -05:00
committed by GitHub
199 changed files with 7023 additions and 4273 deletions

View File

@@ -11,6 +11,7 @@
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write .",
"clean": "rm -rf node_modules && rm -rf .svelte_kit && pnpm i",
"test:integration": "playwright test",
"test:unit": "vitest",
"icons:build": "node ./src/icons/build.js",
@@ -48,7 +49,7 @@
"svgtofont": "^4.0.0",
"tslib": "^2.6.2",
"typescript": "^5.3.2",
"vite": "^4.5.0",
"vite": "^4.5.1",
"vite-plugin-dynamic-import": "^1.5.0",
"vite-plugin-image-optimizer": "^1.1.7",
"vitest": "^0.32.4"

48
pnpm-lock.yaml generated
View File

@@ -60,7 +60,7 @@ devDependencies:
version: 0.1.5(svelte@4.2.7)
'@sveltejs/kit':
specifier: ^1.27.6
version: 1.27.6(svelte@4.2.7)(vite@4.5.0)
version: 1.27.6(svelte@4.2.7)(vite@4.5.1)
'@types/compression':
specifier: ^1.7.5
version: 1.7.5
@@ -131,14 +131,14 @@ devDependencies:
specifier: ^5.3.2
version: 5.3.2
vite:
specifier: ^4.5.0
version: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
specifier: ^4.5.1
version: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
vite-plugin-dynamic-import:
specifier: ^1.5.0
version: 1.5.0
vite-plugin-image-optimizer:
specifier: ^1.1.7
version: 1.1.7(vite@4.5.0)
version: 1.1.7(vite@4.5.1)
vitest:
specifier: ^0.32.4
version: 0.32.4(sass@1.69.5)
@@ -1262,7 +1262,7 @@ packages:
'@rollup/plugin-commonjs': 25.0.7(rollup@3.29.4)
'@rollup/plugin-json': 6.0.1(rollup@3.29.4)
'@rollup/plugin-node-resolve': 15.2.3(rollup@3.29.4)
'@sveltejs/kit': 1.27.6(svelte@4.2.7)(vite@4.5.0)
'@sveltejs/kit': 1.27.6(svelte@4.2.7)(vite@4.5.1)
rollup: 3.29.4
dev: true
@@ -1271,7 +1271,7 @@ packages:
peerDependencies:
'@sveltejs/kit': ^1.5.0
dependencies:
'@sveltejs/kit': 1.27.6(svelte@4.2.7)(vite@4.5.0)
'@sveltejs/kit': 1.27.6(svelte@4.2.7)(vite@4.5.1)
dev: true
/@sveltejs/enhanced-img@0.1.5(svelte@4.2.7):
@@ -1285,7 +1285,7 @@ packages:
- svelte
dev: true
/@sveltejs/kit@1.27.6(svelte@4.2.7)(vite@4.5.0):
/@sveltejs/kit@1.27.6(svelte@4.2.7)(vite@4.5.1):
resolution: {integrity: sha512-GsjTkMbKzXdbeRg0tk8S7HNShQ4879ftRr0ZHaZfjbig1xQwG57Bvcm9U9/mpLJtCapLbLWUnygKrgcLISLC8A==}
engines: {node: ^16.14 || >=18}
hasBin: true
@@ -1294,7 +1294,7 @@ packages:
svelte: ^3.54.0 || ^4.0.0-next.0 || ^5.0.0-next.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.5.2(svelte@4.2.7)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.5.2(svelte@4.2.7)(vite@4.5.1)
'@types/cookie': 0.5.4
cookie: 0.5.0
devalue: 4.3.2
@@ -1308,12 +1308,12 @@ packages:
svelte: 4.2.7
tiny-glob: 0.2.9
undici: 5.26.5
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.5.2)(svelte@4.2.7)(vite@4.5.0):
/@sveltejs/vite-plugin-svelte-inspector@1.0.4(@sveltejs/vite-plugin-svelte@2.5.2)(svelte@4.2.7)(vite@4.5.1):
resolution: {integrity: sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
@@ -1321,30 +1321,30 @@ packages:
svelte: ^3.54.0 || ^4.0.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte': 2.5.2(svelte@4.2.7)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte': 2.5.2(svelte@4.2.7)(vite@4.5.1)
debug: 4.3.4
svelte: 4.2.7
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
transitivePeerDependencies:
- supports-color
dev: true
/@sveltejs/vite-plugin-svelte@2.5.2(svelte@4.2.7)(vite@4.5.0):
/@sveltejs/vite-plugin-svelte@2.5.2(svelte@4.2.7)(vite@4.5.1):
resolution: {integrity: sha512-Dfy0Rbl+IctOVfJvWGxrX/3m6vxPLH8o0x+8FA5QEyMUQMo4kGOVIojjryU7YomBAexOTAuYf1RT7809yDziaA==}
engines: {node: ^14.18.0 || >= 16}
peerDependencies:
svelte: ^3.54.0 || ^4.0.0 || ^5.0.0-next.0
vite: ^4.0.0
dependencies:
'@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.5.2)(svelte@4.2.7)(vite@4.5.0)
'@sveltejs/vite-plugin-svelte-inspector': 1.0.4(@sveltejs/vite-plugin-svelte@2.5.2)(svelte@4.2.7)(vite@4.5.1)
debug: 4.3.4
deepmerge: 4.3.1
kleur: 4.1.5
magic-string: 0.30.5
svelte: 4.2.7
svelte-hmr: 0.15.3(svelte@4.2.7)
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vitefu: 0.2.5(vite@4.5.0)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
vitefu: 0.2.5(vite@4.5.1)
transitivePeerDependencies:
- supports-color
dev: true
@@ -6187,7 +6187,7 @@ packages:
mlly: 1.4.2
pathe: 1.1.1
picocolors: 1.0.0
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
transitivePeerDependencies:
- '@types/node'
- less
@@ -6208,7 +6208,7 @@ packages:
magic-string: 0.30.5
dev: true
/vite-plugin-image-optimizer@1.1.7(vite@4.5.0):
/vite-plugin-image-optimizer@1.1.7(vite@4.5.1):
resolution: {integrity: sha512-KPJWndwqVi7Z2hYCudzKeNDw5U7w1DxAc266bqDBKV8taG8W3EtripFuUM4Y05IlFC19yBQndJCFA8+NJymH+w==}
engines: {node: '>=14'}
peerDependencies:
@@ -6216,11 +6216,11 @@ packages:
dependencies:
ansi-colors: 4.1.3
pathe: 1.1.1
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
dev: true
/vite@4.5.0(@types/node@20.9.3)(sass@1.69.5):
resolution: {integrity: sha512-ulr8rNLA6rkyFAlVWw2q5YJ91v098AFQ2R0PRFwPzREXOUJQPtFUG0t+/ZikhaOCDqFoDhN6/v8Sq0o4araFAw==}
/vite@4.5.1(@types/node@20.9.3)(sass@1.69.5):
resolution: {integrity: sha512-AXXFaAJ8yebyqzoNB9fu2pHoo/nWX+xZlaRwoeYUxEqBO+Zj4msE5G+BhGBll9lYEKv9Hfks52PAF2X7qDYXQA==}
engines: {node: ^14.18.0 || >=16.0.0}
hasBin: true
peerDependencies:
@@ -6256,7 +6256,7 @@ packages:
fsevents: 2.3.3
dev: true
/vitefu@0.2.5(vite@4.5.0):
/vitefu@0.2.5(vite@4.5.1):
resolution: {integrity: sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q==}
peerDependencies:
vite: ^3.0.0 || ^4.0.0 || ^5.0.0
@@ -6264,7 +6264,7 @@ packages:
vite:
optional: true
dependencies:
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
dev: true
/vitest@0.32.4(sass@1.69.5):
@@ -6319,7 +6319,7 @@ packages:
strip-literal: 1.3.0
tinybench: 2.5.1
tinypool: 0.5.0
vite: 4.5.0(@types/node@20.9.3)(sass@1.69.5)
vite: 4.5.1(@types/node@20.9.3)(sass@1.69.5)
vite-node: 0.32.4(@types/node@20.9.3)(sass@1.69.5)
why-is-node-running: 2.2.2
transitivePeerDependencies:

4
src/app.d.ts vendored
View File

@@ -4,7 +4,9 @@ declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
interface PageData {
changelogEntries: number;
}
// interface Platform {}
}
}

View File

@@ -25,7 +25,7 @@
},
{
"link": "/docs/getting-started-for-server",
"redirect": "/docs/getting-started-for-server"
"redirect": "/docs/quick-starts"
},
{
"link": "/docs/self-hosting",

View File

@@ -0,0 +1 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M11.513 3.038 C 11.411 3.057,11.226 3.102,11.102 3.138 C 10.832 3.216,4.728 6.259,4.360 6.498 C 3.689 6.935,3.225 7.619,3.063 8.408 C 2.968 8.871,2.968 15.129,3.063 15.592 C 3.221 16.362,3.673 17.039,4.324 17.482 C 4.447 17.566,5.986 18.353,7.744 19.231 C 10.129 20.422,11.016 20.847,11.240 20.904 C 11.450 20.957,11.679 20.980,12.000 20.980 C 12.321 20.980,12.550 20.957,12.760 20.904 C 12.984 20.847,13.871 20.422,16.256 19.231 C 18.014 18.353,19.553 17.566,19.676 17.481 C 20.331 17.033,20.779 16.361,20.937 15.592 C 21.032 15.129,21.032 8.871,20.937 8.408 C 20.775 7.619,20.314 6.941,19.639 6.499 C 19.495 6.404,17.956 5.618,16.219 4.751 C 13.975 3.631,12.979 3.153,12.780 3.103 C 12.460 3.022,11.789 2.988,11.513 3.038 M12.756 6.097 C 13.093 6.189,13.595 6.447,13.873 6.671 C 14.402 7.096,14.814 7.779,14.937 8.434 C 15.031 8.936,15.031 15.066,14.937 15.566 C 14.754 16.534,14.012 17.438,13.118 17.780 C 11.594 18.364,9.984 17.723,9.298 16.260 C 9.007 15.639,9.020 15.844,9.020 12.000 C 9.020 9.242,9.031 8.495,9.074 8.320 C 9.155 7.988,9.489 7.328,9.702 7.080 C 10.159 6.549,10.828 6.162,11.503 6.040 C 11.770 5.991,12.491 6.024,12.756 6.097 M11.600 7.555 C 11.196 7.688,10.850 7.982,10.654 8.360 C 10.550 8.561,10.540 8.616,10.540 9.000 C 10.540 9.394,10.548 9.435,10.667 9.660 C 10.820 9.951,11.065 10.193,11.360 10.346 C 11.561 10.450,11.616 10.460,12.000 10.460 C 12.384 10.460,12.439 10.450,12.640 10.346 C 12.935 10.193,13.180 9.951,13.333 9.660 C 13.452 9.435,13.460 9.394,13.460 9.000 C 13.460 8.606,13.452 8.565,13.333 8.340 C 13.179 8.048,12.933 7.805,12.640 7.657 C 12.456 7.564,12.360 7.543,12.060 7.534 C 11.862 7.528,11.655 7.537,11.600 7.555 " fill="#414146" stroke="none" fill-rule="evenodd"></path></svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@@ -30,11 +30,12 @@ $aw-icon-nuxt: "\ea1d";
$aw-icon-platform: "\ea1e";
$aw-icon-plus: "\ea1f";
$aw-icon-product-hunt: "\ea20";
$aw-icon-rest: "\ea21";
$aw-icon-search: "\ea22";
$aw-icon-star: "\ea23";
$aw-icon-system: "\ea24";
$aw-icon-twitter: "\ea25";
$aw-icon-vue: "\ea26";
$aw-icon-x: "\ea27";
$aw-icon-youtube: "\ea28";
$aw-icon-refine: "\ea21";
$aw-icon-rest: "\ea22";
$aw-icon-search: "\ea23";
$aw-icon-star: "\ea24";
$aw-icon-system: "\ea25";
$aw-icon-twitter: "\ea26";
$aw-icon-vue: "\ea27";
$aw-icon-x: "\ea28";
$aw-icon-youtube: "\ea29";

View File

@@ -49,11 +49,12 @@
.aw-icon-platform:before { content: "\ea1e"; }
.aw-icon-plus:before { content: "\ea1f"; }
.aw-icon-product-hunt:before { content: "\ea20"; }
.aw-icon-rest:before { content: "\ea21"; }
.aw-icon-search:before { content: "\ea22"; }
.aw-icon-star:before { content: "\ea23"; }
.aw-icon-system:before { content: "\ea24"; }
.aw-icon-twitter:before { content: "\ea25"; }
.aw-icon-vue:before { content: "\ea26"; }
.aw-icon-x:before { content: "\ea27"; }
.aw-icon-youtube:before { content: "\ea28"; }
.aw-icon-refine:before { content: "\ea21"; }
.aw-icon-rest:before { content: "\ea22"; }
.aw-icon-search:before { content: "\ea23"; }
.aw-icon-star:before { content: "\ea24"; }
.aw-icon-system:before { content: "\ea25"; }
.aw-icon-twitter:before { content: "\ea26"; }
.aw-icon-vue:before { content: "\ea27"; }
.aw-icon-x:before { content: "\ea28"; }
.aw-icon-youtube:before { content: "\ea29"; }

Binary file not shown.

View File

@@ -48,14 +48,15 @@
.aw-icon-platform:before { content: "\ea1e"; }
.aw-icon-plus:before { content: "\ea1f"; }
.aw-icon-product-hunt:before { content: "\ea20"; }
.aw-icon-rest:before { content: "\ea21"; }
.aw-icon-search:before { content: "\ea22"; }
.aw-icon-star:before { content: "\ea23"; }
.aw-icon-system:before { content: "\ea24"; }
.aw-icon-twitter:before { content: "\ea25"; }
.aw-icon-vue:before { content: "\ea26"; }
.aw-icon-x:before { content: "\ea27"; }
.aw-icon-youtube:before { content: "\ea28"; }
.aw-icon-refine:before { content: "\ea21"; }
.aw-icon-rest:before { content: "\ea22"; }
.aw-icon-search:before { content: "\ea23"; }
.aw-icon-star:before { content: "\ea24"; }
.aw-icon-system:before { content: "\ea25"; }
.aw-icon-twitter:before { content: "\ea26"; }
.aw-icon-vue:before { content: "\ea27"; }
.aw-icon-x:before { content: "\ea28"; }
.aw-icon-youtube:before { content: "\ea29"; }
$aw-icon-apple: "\ea01";
$aw-icon-arrow-down: "\ea02";
@@ -89,11 +90,12 @@ $aw-icon-nuxt: "\ea1d";
$aw-icon-platform: "\ea1e";
$aw-icon-plus: "\ea1f";
$aw-icon-product-hunt: "\ea20";
$aw-icon-rest: "\ea21";
$aw-icon-search: "\ea22";
$aw-icon-star: "\ea23";
$aw-icon-system: "\ea24";
$aw-icon-twitter: "\ea25";
$aw-icon-vue: "\ea26";
$aw-icon-x: "\ea27";
$aw-icon-youtube: "\ea28";
$aw-icon-refine: "\ea21";
$aw-icon-rest: "\ea22";
$aw-icon-search: "\ea23";
$aw-icon-star: "\ea24";
$aw-icon-system: "\ea25";
$aw-icon-twitter: "\ea26";
$aw-icon-vue: "\ea27";
$aw-icon-x: "\ea28";
$aw-icon-youtube: "\ea29";

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 128 KiB

After

Width:  |  Height:  |  Size: 133 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 54 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -191,52 +191,58 @@
"className": "aw-icon-product-hunt",
"unicode": "&#59936;"
},
"rest": {
"refine": {
"encodedCode": "\\ea21",
"prefix": "aw-icon",
"className": "aw-icon-rest",
"className": "aw-icon-refine",
"unicode": "&#59937;"
},
"search": {
"rest": {
"encodedCode": "\\ea22",
"prefix": "aw-icon",
"className": "aw-icon-search",
"className": "aw-icon-rest",
"unicode": "&#59938;"
},
"star": {
"search": {
"encodedCode": "\\ea23",
"prefix": "aw-icon",
"className": "aw-icon-star",
"className": "aw-icon-search",
"unicode": "&#59939;"
},
"system": {
"star": {
"encodedCode": "\\ea24",
"prefix": "aw-icon",
"className": "aw-icon-system",
"className": "aw-icon-star",
"unicode": "&#59940;"
},
"twitter": {
"system": {
"encodedCode": "\\ea25",
"prefix": "aw-icon",
"className": "aw-icon-twitter",
"className": "aw-icon-system",
"unicode": "&#59941;"
},
"vue": {
"twitter": {
"encodedCode": "\\ea26",
"prefix": "aw-icon",
"className": "aw-icon-vue",
"className": "aw-icon-twitter",
"unicode": "&#59942;"
},
"x": {
"vue": {
"encodedCode": "\\ea27",
"prefix": "aw-icon",
"className": "aw-icon-x",
"className": "aw-icon-vue",
"unicode": "&#59943;"
},
"youtube": {
"x": {
"encodedCode": "\\ea28",
"prefix": "aw-icon",
"className": "aw-icon-youtube",
"className": "aw-icon-x",
"unicode": "&#59944;"
},
"youtube": {
"encodedCode": "\\ea29",
"prefix": "aw-icon",
"className": "aw-icon-youtube",
"unicode": "&#59945;"
}
}

4
src/icons/svg/refine.svg Normal file
View File

@@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M13.3416 3.31672C12.4971 2.89443 11.5029 2.89443 10.6584 3.31672L4.65836 6.31672C3.64201 6.82489 3 7.86368 3 9V15C3 16.1363 3.64201 17.1751 4.65836 17.6833L10.6584 20.6833C11.5029 21.1056 12.4971 21.1056 13.3416 20.6833L19.3416 17.6833C20.358 17.1751 21 16.1363 21 15V9C21 7.86368 20.358 6.82489 19.3416 6.31672L13.3416 3.31672ZM9 9C9 7.34315 10.3431 6 12 6C13.6569 6 15 7.34315 15 9V15C15 16.6569 13.6569 18 12 18C10.3431 18 9 16.6569 9 15V9Z" fill="#414146"/>
<path d="M13.5 9C13.5 9.82843 12.8284 10.5 12 10.5C11.1716 10.5 10.5 9.82843 10.5 9C10.5 8.17157 11.1716 7.5 12 7.5C12.8284 7.5 13.5 8.17157 13.5 9Z" fill="#414146"/>
</svg>

After

Width:  |  Height:  |  Size: 781 B

20
src/lib/UI/Media.svelte Normal file
View File

@@ -0,0 +1,20 @@
<script lang="ts">
export let src: string;
export let alt = '';
export let controls = true;
export let autoplay = false;
let className = '';
export { className as class };
const videoExtensions = ['mp4', 'webm', 'ogg'] as const;
$: isVideo = videoExtensions.some((ext) => src.endsWith(ext));
</script>
{#if isVideo}
<!-- svelte-ignore a11y-media-has-caption-->
<video {src} class={className} {controls} {autoplay}>
<slot />
</video>
{:else}
<img {src} {alt} class={className} />
{/if}

View File

@@ -1 +1,2 @@
export { default as Tabs } from './Tabs/index.svelte';
export { default as Media } from './Media.svelte';

View File

@@ -1,7 +1,7 @@
export function clickOutside(node: HTMLElement, callback: () => void) {
export function clickOutside(node: HTMLElement, callback: (e: MouseEvent) => void) {
const handleClick = (event: MouseEvent) => {
if (node && !node.contains(event.target as Node)) {
callback();
callback(event);
}
};

View File

@@ -184,7 +184,7 @@
aria-label="GitHub"
/>
</div>
<div class="aw-title u-margin-block-start-auto">32k+ GitHub Stars</div>
<div class="aw-title u-margin-block-start-auto">38k+ GitHub Stars</div>
</a>
<a
@@ -199,7 +199,7 @@
aria-label="Twitter"
/>
</div>
<div class="aw-title u-margin-block-start-auto">125k+ Twitter Followers</div>
<div class="aw-title u-margin-block-start-auto">128k+ Twitter Followers</div>
</a>
<a
@@ -214,7 +214,7 @@
aria-label="YouTube"
/>
</div>
<div class="aw-title u-margin-block-start-auto">3k+ Youtube Subscribers</div>
<div class="aw-title u-margin-block-start-auto">4k+ Youtube Subscribers</div>
</a>
<a
@@ -229,7 +229,7 @@
aria-label="GitHub"
/>
</div>
<div class="aw-title u-margin-block-start-auto">15k+ Code Commits</div>
<div class="aw-title u-margin-block-start-auto">18k+ Code Commits</div>
</a>
</div>
</div>

Binary file not shown.

After

Width:  |  Height:  |  Size: 210 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

View File

@@ -1,4 +1,10 @@
<script lang="ts" context="module">
import AuthShot from './(assets)/auth-shot.png?enhanced';
import DatabasesShot from './(assets)/db-shot.png?enhanced';
import FunctionsShot from './(assets)/fn-shot.png?enhanced';
import StorageShot from './(assets)/storage-shot.png?enhanced';
import RealtimeShot from './(assets)/realtime-shot.png?enhanced';
export const elId = writable(0);
export function getElSelector(el: string) {
@@ -36,7 +42,7 @@
'Rate-limits and advanced user protection',
'Custom SMTP and email templates'
],
shot: './images/products/auth.png'
shot: AuthShot
},
databases: {
icon: {
@@ -53,7 +59,7 @@
'Custom data validation',
'Relationships support'
],
shot: './images/products/databases.png'
shot: DatabasesShot
},
functions: {
icon: {
@@ -69,7 +75,7 @@
'Support for 30+ runtimes in 13 languages',
'Custom domain support'
],
shot: './images/products/functions.png'
shot: FunctionsShot
},
storage: {
icon: {
@@ -85,7 +91,7 @@
'Built-in image transformation capabilities',
'Advanced compression with WebP/Brotli support'
],
shot: './images/products/storage.png'
shot: StorageShot
},
realtime: {
icon: {
@@ -100,7 +106,7 @@
'Built-in permission management',
'Support for DBs, Auth, Storage & Functions'
],
shot: './images/products/realtime.png'
shot: RealtimeShot
}
};
</script>
@@ -371,7 +377,7 @@
position: sticky;
top: 0;
// min-height: 58rem;
min-height: 50rem;
overflow: hidden;
padding-inline: 1.25rem;

View File

@@ -3,7 +3,8 @@
import { infos } from './Products.svelte';
</script>
<div class="wrapper">
<div class="outside">
<div class="wrapper">
<span class="aw-badges aw-eyebrow">Products_</span>
<h2 class="aw-display aw-u-color-text-primary u-margin-block-start-16">
@@ -16,8 +17,9 @@
</p>
<div class="infos">
{#each objectKeys(infos) as prod}
{#each objectKeys(infos) as prod, i}
{@const info = infos[prod]}
{@const isLast = i === objectKeys(infos).length - 1}
{#if info}
<div class="info">
@@ -37,16 +39,19 @@
</ul>
{#if info.shot}
<img src={info.shot} alt="" />
<enhanced:img class="img" src={info.shot} alt="" />
{/if}
</div>
{#if !isLast}
<hr />
{/if}
{/if}
{/each}
</div>
<div class="post-wrapper">
<img src="/images/products/post.png" alt="" />
<div class="img-overlay" />
<h2>See your products grow</h2>
<p>
@@ -54,31 +59,49 @@
products users love and use every day.
</p>
</div>
</div>
<div class="img-overlay" />
</div>
<style lang="scss">
.wrapper {
.outside {
position: relative;
overflow: hidden;
display: none;
padding-block-start: 5rem;
padding-inline: 1.25rem;
max-width: 800px;
margin-inline: auto;
overflow: visible;
.img-overlay {
content: '';
background: linear-gradient(to bottom, transparent 0%, black 40%);
position: absolute;
bottom: 0;
width: 100vw;
height: 30rem;
z-index: 10;
}
}
@media (max-width: 1399px) {
.wrapper {
.outside {
display: block;
}
}
.wrapper {
--padding-inline: 1.25rem;
padding-block-start: 5rem;
padding-inline: var(--padding-inline);
max-width: 600px;
margin-inline: auto;
}
.infos {
margin-block-start: 3rem;
display: flex;
flex-direction: column;
gap: 6rem;
gap: 3rem;
.info {
h3 {
@@ -128,11 +151,17 @@
}
}
> img {
width: 100%;
.img {
inline-size: 100%;
block-size: auto;
margin-block-start: 2.5rem;
}
}
hr {
border: 1px solid hsl(var(--aw-color-smooth));
margin-inline: calc(var(--padding-inline) * -1);
}
}
.post-wrapper {
@@ -143,25 +172,16 @@
position: relative;
width: 100%;
/* overflow: hidden; */
padding-block-start: 35rem;
padding-block-end: 5rem;
.img-overlay {
content: '';
background: linear-gradient(to top, black, transparent);
position: absolute;
bottom: 0;
width: 100vw;
height: 30rem;
z-index: 10;
}
img {
display: block;
max-block-size: unset;
max-inline-size: 100%;
top: 0rem;
max-inline-size: unset;
top: 5rem;
left: 50%;
transform: translateX(-50%);
width: 37.5rem;

View File

@@ -13,7 +13,7 @@
onMount(() => {
let frame: number | null = null;
const ease = () => {
easedPercentage += (percentage - easedPercentage);
easedPercentage += percentage - easedPercentage;
frame = window.requestAnimationFrame(ease);
};
ease();
@@ -30,7 +30,7 @@
style:--y={`${y}px`}
style:--percentage={`${easedPercentage * 100}%`}
>
<div class="cursor" />
<div class="aw-dot" />
</div>
<style lang="scss">
@@ -47,36 +47,12 @@
hsl(var(--aw-color-greyscale-700)) 100%
);
border-radius: 100%;
}
.cursor {
border-radius: 16px;
border: 0.5px solid rgba(255, 255, 255, 0.16);
background: linear-gradient(
138deg,
rgba(255, 255, 255, 0.13) 9.61%,
rgba(255, 255, 255, 0) 105.41%
);
backdrop-filter: blur(100px);
width: 16px;
height: 16px;
.aw-dot {
position: absolute;
left: 50%;
translate: -50% var(--y, 0);
top: -8px;
&::after {
content: '';
background-color: white;
border-radius: 100%;
width: 4px;
height: 4px;
position: absolute;
left: 50%;
top: 50%;
translate: -50% -50%;
}
}
}
</style>

View File

@@ -1,4 +1,7 @@
<script lang="ts">
import Media from '$lib/UI/Media.svelte';
import { formatDate } from '$lib/utils/date';
export let title: string;
export let cover: string;
export let href: string;
@@ -11,7 +14,13 @@
<li>
<a class="aw-grid-articles-item is-transparent" {href}>
<div class="aw-grid-articles-item-image">
<img src={cover} class="aw-u-media-ratio-16-9" alt={title} />
<Media
src={cover}
class="aw-u-media-ratio-16-9"
alt={title}
autoplay
controls={false}
/>
</div>
<div class="aw-grid-articles-item-content">
<h4 class="aw-label aw-u-color-text-primary">
@@ -24,11 +33,7 @@
<h4 class="aw-sub-body-400 aw-u-color-text-primary">{author}</h4>
<ul class="aw-metadata aw-caption-400 aw-is-not-mobile">
<li>
{Intl.DateTimeFormat('en-US', {
day: '2-digit',
month: 'short',
year: 'numeric'
}).format(date)}
{formatDate(date)}
</li>
<li>{timeToRead} min</li>
</ul>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { page } from '$app/stores';
export let date: string;
export let date: string | undefined = undefined;
let showFeedback = false;
let feedbackType = '';
let email = '';

View File

@@ -35,9 +35,9 @@
],
Learn: [
{ label: 'Docs', href: '/docs' },
{ label: 'Blog', href: '/blog' },
{ label: 'Community', href: '/community' },
{ label: 'Heroes', href: '/heroes' },
{ label: 'Blog', href: '/blog' },
{ label: 'Changelog', href: '/changelog' },
{
label: 'Source code',
href: 'https://github.com/appwrite',
@@ -52,8 +52,8 @@
}
],
About: [
{ label: 'Pricing', href: '/pricing' },
{ label: 'Company', href: '/company' },
{ label: 'Pricing', href: '/pricing' },
{
label: 'Careers',
href: 'https://appwrite.careers',
@@ -68,7 +68,7 @@
rel: 'noopener noreferrer'
},
{ label: 'Contact us', href: '/contact-us' },
{ label: 'Assets', href: '/assets' }
{ label: 'Assets', href: '/assets' },
],
Policies: [
{ label: 'Terms', href: '/terms' },

View File

@@ -1,41 +1,8 @@
<script lang="ts">
import { socials } from '$lib/constants';
import ThemeSelect from './ThemeSelect.svelte';
export let variant: 'homepage' | 'docs' = 'homepage';
type Social = {
icon: string;
label?: string;
link: string;
};
const socials: Array<Social> = [
{
icon: 'aw-icon-discord',
link: 'https://appwrite.io/discord'
},
{
icon: 'aw-icon-github',
link: 'https://github.com/appwrite'
},
{
icon: 'aw-icon-x',
link: 'https://twitter.com/intent/follow?screen_name=appwrite'
},
{
icon: 'aw-icon-linkedin',
link: 'https://www.linkedin.com/company/appwrite'
},
{
icon: 'aw-icon-youtube',
label: 'YouTube',
link: 'https://www.youtube.com/c/appwrite?sub_confirmation=1'
}
];
function getSocialLabel(social: Social) {
return social.label ?? social.icon;
}
</script>
{#if variant === 'homepage'}
@@ -46,7 +13,7 @@
<a
href={social.link}
class="aw-icon-button"
aria-label="Appwrite on {getSocialLabel(social)}"
aria-label={social.label}
target="_blank"
rel="noopener noreferrer"
>
@@ -68,7 +35,7 @@
<a
href={social.link}
class="aw-icon-button"
aria-label="Appwrite on {getSocialLabel(social)}"
aria-label={social.label}
target="_blank"
rel="noopener noreferrer"
>

View File

@@ -12,7 +12,7 @@
href="https://cloud.appwrite.io"
class="aw-button is-transparent aw-u-cross-child-center"
>
<span class="text">Start building today</span>
<span class="text">Get started</span>
</a>
</section>
<section
@@ -37,7 +37,7 @@
</p>
<a
href="https://cloud.appwrite.io/register"
class="aw-button is-full-width-mobile aw-u-cross-child-end"
class="aw-button is-secondary is-full-width-mobile aw-u-cross-child-end"
>
<span class="text">Get started</span>
</a>
@@ -48,17 +48,19 @@
<div class="aw-strip-plans-plan">
<h4 class="title aw-description">Pro</h4>
<div class="aw-title aw-u-color-text-primary">$15</div>
<div class="info aw-caption-500">per user/month</div>
<div class="info aw-caption-500">per member/month</div>
</div>
<p class="aw-strip-plans-info aw-caption-500">
For pro developers and teams that need to scale their products.
</p>
<button
class="aw-button is-full-width-mobile is-secondary aw-u-cross-child-end"
disabled
<a
href="https://cloud.appwrite.io/console?type=createPro"
class="aw-button is-full-width-mobile aw-u-cross-child-end"
target="_blank"
rel="noopener noreferrer"
>
<span class="text">Coming soon</span>
</button>
<span class="text">Start trial</span>
</a>
</div>
</li>
<li class="aw-strip-plans-item aw-strip-plans-container-query">

View File

@@ -37,6 +37,11 @@
name: 'Angular',
href: '/docs/quick-starts/angular',
image: `/images/platforms/${$themeInUse}/angular.svg`
},
{
name: 'Refine',
href: '/docs/quick-starts/refine',
image: `/images/platforms/${$themeInUse}/refine.svg`
},
{
name: 'Apple',
@@ -47,7 +52,8 @@
name: 'Android',
href: '/docs/quick-starts/android',
image: `/images/platforms/${$themeInUse}/android.svg`
}
},
] as Array<{
name: string;
href: string;
@@ -59,8 +65,8 @@
{#each platforms as platform}
<Tooltip>
<li>
<a href={platform.href} class="aw-box-icon has-border-gradient">
<img src={platform.image} alt="{platform.name} Logo" width="32" height="32" />
<a href={platform.href} class="aw-icon-button aw-box-icon has-border-gradient">
<img src={platform.image} alt="{platform.name} quick start" width="32" height="32" />
</a>
</li>
<svelte:fragment slot="tooltip">{platform.name}</svelte:fragment>

View File

@@ -0,0 +1,102 @@
<script lang="ts">
import { getTocCtx } from './TocRoot.svelte';
import TocTree from './TocTree.svelte';
export let showToc = true;
const {
toc: {
elements: { item },
states: { activeHeadingIdxs, headingsTree }
}
} = getTocCtx();
$: progress = Math.max(...$activeHeadingIdxs) / ($headingsTree.length - 1);
</script>
<aside class="aw-grid-120-1fr-auto-side" class:aw-is-mobile-closed={!showToc}>
<div class="aw-page-steps">
<div class="aw-page-steps-location aw-is-not-mobile" style="--location:{progress * 100}%;">
<span class="aw-page-steps-location-button">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
>
<g clip-path="url(#clip0_1684_10747)">
<g filter="url(#filter0_b_1684_10747)">
<circle
cx="8"
cy="8"
r="8"
fill="url(#paint0_linear_1684_10747)"
fill-opacity="0.32"
/>
<circle
cx="8"
cy="8"
r="7.75"
stroke="url(#paint1_linear_1684_10747)"
stroke-width="0.5"
/>
</g>
<circle cx="8" cy="7.99219" r="3" fill="white" />
</g>
<defs>
<filter
id="filter0_b_1684_10747"
x="-200"
y="-200"
width="416"
height="416"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feGaussianBlur in="BackgroundImageFix" stdDeviation="100" />
<feComposite
in2="SourceAlpha"
operator="in"
result="effect1_backgroundBlur_1684_10747"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_backgroundBlur_1684_10747"
result="shape"
/>
</filter>
<linearGradient
id="paint0_linear_1684_10747"
x1="2.02105"
y1="1.10843"
x2="16.3872"
y2="17.2901"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" stop-opacity="0.4" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
<linearGradient
id="paint1_linear_1684_10747"
x1="7.45643"
y1="-1.10615"
x2="5.53812"
y2="17.9973"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" stop-opacity="0.16" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
<clipPath id="clip0_1684_10747">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
</span>
</div>
<TocTree tree={$headingsTree} activeHeadingIdxs={$activeHeadingIdxs} {item} />
</div>
</aside>

View File

@@ -0,0 +1,39 @@
<script context="module" lang="ts">
import {
createTableOfContents,
type CreateTableOfContentsArgs,
type TableOfContents
} from '@melt-ui/svelte';
import { getContext, setContext } from 'svelte';
const TOC_KEY = Symbol();
export type TocContext = {
toc: TableOfContents;
};
const setCtx = (ctx: TocContext) => {
setContext<TocContext>(TOC_KEY, ctx);
};
export const getTocCtx = () => {
return getContext<TocContext>(TOC_KEY);
};
</script>
<script lang="ts">
export let selector = '#main';
export let exclude: CreateTableOfContentsArgs['exclude'] = ['h1', 'h3', 'h4', 'h5', 'h6'];
export let activeType: CreateTableOfContentsArgs['activeType'] = 'lowest';
export let scrollOffset: CreateTableOfContentsArgs['scrollOffset'] = 120;
const toc = createTableOfContents({
selector,
exclude,
activeType,
scrollOffset
});
setCtx({ toc });
</script>
<slot />

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { type TableOfContentsItem, type TableOfContentsElements, melt } from '@melt-ui/svelte';
import { getPolicyCtx } from './Policy.svelte';
import { getTocCtx } from './TocRoot.svelte';
export let tree: TableOfContentsItem[] = [];
export let activeHeadingIdxs: number[];
@@ -11,7 +11,7 @@
toc: {
helpers: { isActive }
}
} = getPolicyCtx();
} = getTocCtx();
</script>
<ul class="aw-page-steps-list aw-sub-body-500">

View File

@@ -3,7 +3,7 @@
import type { FloatingConfig } from '@melt-ui/svelte/internal/actions';
import { fly, type FlyParams } from 'svelte/transition';
export let placement: FloatingConfig['placement'] = 'top';
export let placement: NonNullable<FloatingConfig>['placement'] = 'top';
export let disabled = false;
export let closeOnPointerDown = false;

View File

@@ -5,3 +5,37 @@ export const BANNER_KEY = 'pricing-banner-01'; // Change key to force banner to
* discord-banner-01
* pricing-banner-01
*/
export type Social = {
icon: string;
label: string;
link: string;
};
export const socials: Array<Social> = [
{
icon: 'aw-icon-discord',
label: 'Discord',
link: 'https://appwrite.io/discord'
},
{
icon: 'aw-icon-github',
label: 'Github',
link: 'https://github.com/appwrite'
},
{
icon: 'aw-icon-x',
label: 'Twitter',
link: 'https://twitter.com/intent/follow?screen_name=appwrite'
},
{
icon: 'aw-icon-linkedin',
label: 'LinkedIn',
link: 'https://www.linkedin.com/company/appwrite'
},
{
icon: 'aw-icon-youtube',
label: 'YouTube',
link: 'https://www.youtube.com/c/appwrite?sub_confirmation=1'
}
];

View File

@@ -15,6 +15,7 @@
showSearch: false,
currentVariant: null
});
export function toggleReferences() {
layoutState.update((state) => ({
...state,
@@ -23,19 +24,24 @@
}));
}
export function toggleSidenav() {
layoutState.update((state) => ({
layoutState.update((state) => {
return {
...state,
showReferences: false,
showSidenav: !state.showSidenav
}));
};
});
}
const CTX_KEY = Symbol('docs');
export const isInDocs = () => getContext<boolean>(CTX_KEY) ?? false;
</script>
<script lang="ts">
import Search from '$lib/components/Search.svelte';
import { setContext } from 'svelte';
import { isMac } from '$lib/utils/platform';
import { getContext, setContext } from 'svelte';
export let variant: DocsLayoutVariant = 'default';
export let isReferences = false;
@@ -56,8 +62,7 @@
showSidenav: false
}));
});
setContext('isDocs', true);
setContext(CTX_KEY, true);
const handleKeydown = (e: KeyboardEvent) => {
if (e.key === 'Escape' && ($layoutState.showReferences || $layoutState.showSidenav)) {
@@ -97,8 +102,12 @@
<a href="https://cloud.appwrite.io/console" class="aw-button aw-is-only-desktop">
<span class="aw-sub-body-500">Go to console</span>
</a>
<button on:click={toggleSidenav} class="aw-button is-text" aria-label="open navigation">
<span class="aw-icon-hamburger-menu" />
<button class="aw-button is-text" aria-label="open navigation" on:click={toggleSidenav}>
{#if $layoutState.showSidenav}
<span aria-hidden="true" class="aw-icon-close" />
{:else}
<span aria-hidden="true" class="aw-icon-hamburger-menu" />
{/if}
</button>
</div>
</section>
@@ -124,7 +133,7 @@
width="130"
/>
</a>
<nav class="aw-main-header-nav" aria-label="Top" >
<nav class="aw-main-header-nav" aria-label="Top">
<ul class="aw-main-header-nav-list">
<li class="aw-main-header-nav-item">
<a class="aw-link" href="/docs">Docs</a>
@@ -165,13 +174,6 @@
<a href="https://cloud.appwrite.io/console" class="aw-button">
<span class="aw-sub-body-500">Go to console</span>
</a>
<button
on:click={toggleSidenav}
class="aw-button is-text aw-is-not-desktop"
aria-label="open navigation"
>
<span class="aw-icon-hamburger-menu" />
</button>
</div>
</div>
</div>

View File

@@ -20,10 +20,10 @@
export let title: string;
export let toc: Array<TocItem>;
export let back: string | undefined = undefined;
export let date: string;
export let date: string | undefined = undefined;
</script>
<main class="u-contents">
<main class="u-contents" id="main">
<article class="aw-article u-contents">
<header class="aw-article-header">
<div class="aw-article-header-start u-flex-vertical aw-u-cross-start">

View File

@@ -16,7 +16,7 @@
$: prevStep = tutorials.find((tutorial) => tutorial.step === currentStep - 1);
</script>
<main class="u-contents">
<main class="u-contents" id="main">
<article class="aw-article u-contents">
<header class="aw-article-header">
<div class="aw-article-header-start u-flex-vertical aw-u-cross-start">

View File

@@ -4,9 +4,12 @@
export type NavLink = {
label: string;
href: string;
showBadge?: boolean;
};
export const isHeaderHidden = writable(false);
export const isMobileNavOpen = writable(false);
const initialized = writable(false);
</script>
<script lang="ts">
@@ -15,9 +18,12 @@
import { BANNER_KEY } from '$lib/constants';
import { isVisible } from '$lib/utils/isVisible';
import { createScrollInfo } from '$lib/utils/scroll';
import { hasNewChangelog } from '$routes/changelog/utils';
import { addEventListener } from '@melt-ui/svelte/internal/helpers';
import { onMount } from 'svelte';
import { page } from '$app/stores';
export let omitMainId = false;
let theme: 'light' | 'dark' | null = 'dark';
function setupThemeObserver() {
@@ -74,6 +80,9 @@
}
onMount(() => {
setTimeout(() => {
$initialized = true;
}, 1000);
return setupThemeObserver();
});
@@ -90,6 +99,11 @@
label: 'Blog',
href: '/blog'
},
{
label: 'Changelog',
href: '/changelog',
showBadge: hasNewChangelog() && !$page.url.pathname.includes('/changelog')
},
{
label: 'Pricing',
href: '/pricing'
@@ -167,7 +181,9 @@
<div class="aw-top-banner">
<div class="aw-top-banner-content aw-u-color-text-primary">
<a href="/blog/post/announcing-appwrite-pro">
<span class="aw-caption-500">Appwrite Pro is now available. Get started with $15 credit!</span>
<span class="aw-caption-500"
>Appwrite Pro is now available! Get started with $15 credit.</span
>
</a>
{#if browser}
<button
@@ -201,9 +217,15 @@
</a>
<nav class="aw-main-header-nav" aria-label="Main">
<ul class="aw-main-header-nav-list">
{#each navLinks as { label, href }}
{#each navLinks as navLink}
<li class="aw-main-header-nav-item">
<a class="aw-link" {href}>{label}</a>
<a
class="aw-link"
href={navLink.href}
data-initialized={$initialized ? '' : undefined}
data-badge={navLink.showBadge ? '' : undefined}
>{navLink.label}
</a>
</li>
{/each}
</ul>
@@ -220,9 +242,9 @@
<span class="text">Star on GitHub</span>
<span class="aw-inline-tag aw-sub-body-400">38.4K</span>
</a>
<!-- <a href="https://cloud.appwrite.io/register" class="aw-button is-secondary"-->
<!-- >Sign up</a-->
<!-- >-->
<!-- <a href="https://cloud.appwrite.io/register" class="aw-button is-secondary"-->
<!-- >Sign up</a-->
<!-- >-->
<a href="https://cloud.appwrite.io" class="aw-button">
<span class="text">Get started</span>
</a>
@@ -231,7 +253,48 @@
</header>
<MobileNav bind:open={$isMobileNavOpen} links={navLinks} />
<main class="aw-main-section" class:aw-u-hide-mobile={$isMobileNavOpen}>
<main
class="aw-main-section"
class:aw-u-hide-mobile={$isMobileNavOpen}
id={omitMainId ? undefined : 'main'}
>
<slot />
</main>
</div>
<style lang="scss">
.nav-badge {
margin-inline-start: 0.5rem;
padding-inline: 0.375rem;
}
@keyframes scale-in {
0% {
transform: scale(0);
}
100% {
transform: scale(1);
}
}
[data-badge] {
position: relative;
&::after {
content: '';
position: absolute;
background-color: hsl(var(--aw-color-accent));
border-radius: 100%;
width: 0.375rem;
height: 0.375rem;
inset-block-start: -2px;
inset-inline-end: -4px;
translate: 100%;
}
&:not([data-initialized])::after {
animation: scale-in 0.2s ease-out;
}
}
</style>

View File

@@ -37,7 +37,12 @@
<nav
class="aw-side-nav"
use:clickOutside={() => ($layoutState.showSidenav = false)}
use:clickOutside={(e) => {
const el = e.target;
if (!(el instanceof HTMLElement)) return;
if (el.closest('.aw-main-header') || el.closest('.aw-mobile-header')) return;
$layoutState.showSidenav = false;
}}
aria-label="Side"
>
<div class="aw-side-nav-wrapper">

View File

@@ -1,5 +0,0 @@
import type { ImageFormat } from '$routes/api/image/+server';
export const getImageUrl = (img: string, format: ImageFormat) => {
return `/api/image?img=${encodeURIComponent(img)}&format=${format}`;
};

9
src/lib/utils/is.ts Normal file
View File

@@ -0,0 +1,9 @@
export function isNumeric(value: unknown): boolean {
if (typeof value === 'number' && !isNaN(value)) {
return true;
}
if (typeof value === 'string' && value.trim() !== '') {
return !isNaN(Number(value));
}
return false;
}

View File

@@ -1,5 +1,4 @@
import type { AuthorData } from '$markdoc/layouts/Author.svelte';
import type { PostsData } from '$markdoc/layouts/Post.svelte';
import type { AuthorData, PostsData } from "$routes/blog/content";
export const DEFAULT_HOST = 'https://website-appwrite.vercel.app';
export const DEFAULT_DESCRIPTION = 'Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools.';

View File

@@ -18,6 +18,8 @@ type SDKMethod = {
contentType?: string;
models?: SDKMethodModel[];
}>;
method: OpenAPIV3.HttpMethods;
url: string;
};
type SDKMethodModel = {
@@ -78,36 +80,35 @@ function getExamples(version: string) {
function* iterateAllMethods(
api: OpenAPIV3.Document,
service: string
): Generator<[OpenAPIV3.HttpMethods, OpenAPIV3.OperationObject]> {
): Generator<{ method: OpenAPIV3.HttpMethods; value: OpenAPIV3.OperationObject; url: string }> {
for (const url in api.paths) {
const methods = api.paths[url];
if (methods?.get?.tags?.includes(service)) {
yield [OpenAPIV3.HttpMethods.GET, methods.get];
yield { method: OpenAPIV3.HttpMethods.GET, value: methods.get, url };
}
if (methods?.post?.tags?.includes(service)) {
yield [OpenAPIV3.HttpMethods.POST, methods.post];
yield { method: OpenAPIV3.HttpMethods.POST, value: methods.post, url };
}
if (methods?.put?.tags?.includes(service)) {
yield [OpenAPIV3.HttpMethods.PUT, methods.put];
yield { method: OpenAPIV3.HttpMethods.PUT, value: methods.put, url };
}
if (methods?.patch?.tags?.includes(service)) {
yield [OpenAPIV3.HttpMethods.PATCH, methods.patch];
yield { method: OpenAPIV3.HttpMethods.PATCH, value: methods.patch, url };
}
if (methods?.delete?.tags?.includes(service)) {
yield [OpenAPIV3.HttpMethods.DELETE, methods.delete];
yield { method: OpenAPIV3.HttpMethods.DELETE, value: methods.delete, url };
}
}
}
function getParameters(
operation: AppwriteOperationObject
): SDKMethod['parameters'] {
function getParameters(operation: AppwriteOperationObject): SDKMethod['parameters'] {
const parameters: ReturnType<typeof getParameters> = [];
const requestBody = operation?.requestBody as OpenAPIV3.RequestBodyObject;
const schemaJson = requestBody?.content['application/json']?.schema as OpenAPIV3.SchemaObject;
const schemaMultipart = requestBody?.content['multipart/form-data']?.schema as OpenAPIV3.SchemaObject;
const schemaMultipart = requestBody?.content['multipart/form-data']
?.schema as OpenAPIV3.SchemaObject;
if (operation?.parameters) {
for (const parameter of (operation?.parameters as OpenAPIV3.ParameterObject[])) {
for (const parameter of operation?.parameters as OpenAPIV3.ParameterObject[]) {
const schema = parameter.schema as OpenAPIV3.SchemaObject;
parameters.push({
@@ -145,7 +146,7 @@ function getParameters(
}
return parameters.sort((a, b) => {
return (a.required === b.required) ? 0 : a.required ? -1 : 1;
return a.required === b.required ? 0 : a.required ? -1 : 1;
});
}
@@ -208,8 +209,10 @@ export async function getService(
/**
* Exceptions for Android SDK.
*/
const isAndroidJava = platform === Platform.ClientAndroidJava || platform === Platform.ServerJava;
const isAndroidKotlin = platform === Platform.ClientAndroidKotlin || platform === Platform.ServerKotlin;
const isAndroidJava =
platform === Platform.ClientAndroidJava || platform === Platform.ServerJava;
const isAndroidKotlin =
platform === Platform.ClientAndroidKotlin || platform === Platform.ServerKotlin;
const isAndroid = isAndroidJava || isAndroidKotlin;
const isAndroidServer = platform === Platform.ServerJava || platform === Platform.ServerKotlin;
const api = await getApi(version, platform);
@@ -229,14 +232,15 @@ export async function getService(
return data;
}
for (const [method, value] of iterateAllMethods(api, service)) {
for (const { method, value, url } of iterateAllMethods(api, service)) {
const operation = value as AppwriteOperationObject;
const parameters = getParameters(operation);
const responses: SDKMethod['responses'] = Object.entries(operation.responses ?? {}).map(
(tuple) => {
const [code, response] = tuple as [string, OpenAPIV3.ResponseObject];
const models: SDKMethodModel[] = [];
const schemas = response?.content?.['application/json']?.schema as OpenAPIV3.SchemaObject;
const schemas = response?.content?.['application/json']
?.schema as OpenAPIV3.SchemaObject;
if (code !== '204') {
if (schemas?.oneOf) {
schemas.oneOf.forEach((ref) => {
@@ -249,7 +253,10 @@ export async function getService(
} else {
if (schemas) {
const id = getIdFromReference(schemas as OpenAPIV3.ReferenceObject);
const schema = resolveReference(schemas as OpenAPIV3.ReferenceObject, api);
const schema = resolveReference(
schemas as OpenAPIV3.ReferenceObject,
api
);
models.push({
id,
name: schema?.description ?? ''
@@ -267,9 +274,9 @@ export async function getService(
);
const path = isAndroid
? `/node_modules/@appwrite.io/repo/docs/examples/${version}/${isAndroidServer ? 'server-kotlin' : 'client-android'}/${
isAndroidJava ? 'java' : 'kotlin'
}/${operation['x-appwrite'].demo}`
? `/node_modules/@appwrite.io/repo/docs/examples/${version}/${
isAndroidServer ? 'server-kotlin' : 'client-android'
}/${isAndroidJava ? 'java' : 'kotlin'}/${operation['x-appwrite'].demo}`
: `/node_modules/@appwrite.io/repo/docs/examples/${version}/${platform}/examples/${operation['x-appwrite'].demo}`;
if (!(path in examples)) {
continue;
@@ -280,7 +287,9 @@ export async function getService(
title: operation.summary ?? '',
description: operation.description ?? '',
parameters: parameters ?? [],
responses: responses ?? []
responses: responses ?? [],
method,
url
});
}

View File

@@ -26,7 +26,7 @@
export let back: string | undefined = undefined;
export let difficulty: string | undefined = undefined;
export let readtime: string | undefined = undefined;
export let date: string;
export let date: string | undefined = undefined;
setContext<LayoutContext>('headings', writable({}));

View File

@@ -40,7 +40,7 @@
<div class="aw-big-padding-section-level-1">
<div class="aw-big-padding-section-level-2">
<div class="aw-container">
<a class="aw-link aw-u-color-text-secondary" href="/blog">
<a class="aw-link aw-u-color-text-secondary u-cross-baseline" href="/blog">
<span class="aw-icon-chevron-left" aria-hidden="true" />
<span>Back to blog</span>
</a>

View File

@@ -0,0 +1,27 @@
<script lang="ts" context="module">
import { hasContext, setContext } from 'svelte';
export type ChangelogData = {
title: string;
description?: string;
date: string;
cover?: string;
};
const CONTEXT_KEY = Symbol('changelog');
function initContext() {
setContext(CONTEXT_KEY, null);
}
export function isInChangelog() {
return hasContext(CONTEXT_KEY);
}
</script>
<script lang="ts">
initContext();
</script>
<!-- Empty so it can be displayed inline -->
<slot />

View File

@@ -1,7 +1,6 @@
<script context="module" lang="ts">
const POLICY_KEY = Symbol();
export type PolicyContext = {
toc: TableOfContents;
transparentTableCells?: boolean;
};
@@ -13,7 +12,7 @@
return getContext<PolicyContext>(POLICY_KEY);
};
export const isInsidePolicy = () => {
export const isInPolicy = () => {
return hasContext(POLICY_KEY);
};
</script>
@@ -26,11 +25,11 @@
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
import { TITLE_SUFFIX } from '$routes/titles';
import { createTableOfContents, type TableOfContents } from '@melt-ui/svelte';
import { getContext, hasContext, setContext } from 'svelte';
import PolicyTree from './PolicyTree.svelte';
import TocNav from '$lib/components/TocNav.svelte';
import TocRoot from '$lib/components/TocRoot.svelte';
import { isHeaderHidden } from '$lib/layouts/Main.svelte';
import { getContext, hasContext, setContext } from 'svelte';
export let title: string;
export let transparentTableCells = false;
@@ -45,21 +44,7 @@
let showToc = false;
const toc = createTableOfContents({
exclude: ['h1', 'h3', 'h4', 'h5', 'h6'],
selector: '#policy-content',
activeType: 'lowest',
scrollOffset: 120
});
setCtx({ toc, transparentTableCells });
const {
elements: { item },
states: { activeHeadingIdxs, headingsTree }
} = toc;
$: progress = Math.max(...$activeHeadingIdxs) / ($headingsTree.length - 1);
setCtx({ transparentTableCells });
</script>
<svelte:head>
@@ -79,7 +64,8 @@
<meta name="twitter:card" content="summary_large_image" />
</svelte:head>
<Main>
<Main omitMainId>
<TocRoot>
<div class="aw-container">
<div class="aw-grid-120-1fr-auto">
<header class="aw-grid-120-1fr-auto-header">
@@ -98,105 +84,10 @@
<span class="aw-description">Table of contents</span>
<span class="icon-menu-alt-4" aria-hidden="true" />
</button>
<aside class="aw-grid-120-1fr-auto-side" class:aw-is-mobile-closed={!showToc}>
<div class="aw-page-steps">
<div
class="aw-page-steps-location aw-is-not-mobile"
style="--location:{progress * 100}%;"
>
<span class="aw-page-steps-location-button">
<svg
xmlns="http://www.w3.org/2000/svg"
width="16"
height="16"
viewBox="0 0 16 16"
fill="none"
>
<g clip-path="url(#clip0_1684_10747)">
<g filter="url(#filter0_b_1684_10747)">
<circle
cx="8"
cy="8"
r="8"
fill="url(#paint0_linear_1684_10747)"
fill-opacity="0.32"
/>
<circle
cx="8"
cy="8"
r="7.75"
stroke="url(#paint1_linear_1684_10747)"
stroke-width="0.5"
/>
</g>
<circle cx="8" cy="7.99219" r="3" fill="white" />
</g>
<defs>
<filter
id="filter0_b_1684_10747"
x="-200"
y="-200"
width="416"
height="416"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feGaussianBlur
in="BackgroundImageFix"
stdDeviation="100"
/>
<feComposite
in2="SourceAlpha"
operator="in"
result="effect1_backgroundBlur_1684_10747"
/>
<feBlend
mode="normal"
in="SourceGraphic"
in2="effect1_backgroundBlur_1684_10747"
result="shape"
/>
</filter>
<linearGradient
id="paint0_linear_1684_10747"
x1="2.02105"
y1="1.10843"
x2="16.3872"
y2="17.2901"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" stop-opacity="0.4" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
<linearGradient
id="paint1_linear_1684_10747"
x1="7.45643"
y1="-1.10615"
x2="5.53812"
y2="17.9973"
gradientUnits="userSpaceOnUse"
>
<stop stop-color="white" stop-opacity="0.16" />
<stop offset="1" stop-color="white" stop-opacity="0" />
</linearGradient>
<clipPath id="clip0_1684_10747">
<rect width="16" height="16" fill="white" />
</clipPath>
</defs>
</svg>
</span>
</div>
<PolicyTree
tree={$headingsTree}
activeHeadingIdxs={$activeHeadingIdxs}
{item}
/>
</div>
</aside>
<main class="aw-grid-120-1fr-auto-main /aw-is-mobile-closed" id="policy-content">
<TocNav />
<main class="aw-grid-120-1fr-auto-main /aw-is-mobile-closed" id="main">
<div class="aw-content is-count-headers" class:aw-is-mobile-closed={showToc}>
<!-- eslint-disable-next-line svelte/valid-compile -->
<!-- svelte-ignore a11y-hidden -->
<h2 aria-hidden="true">Introduction</h2>
<slot />
</div>
@@ -205,6 +96,7 @@
<FooterNav />
<MainFooter />
</div>
</TocRoot>
</Main>
<style lang="scss">
@@ -216,10 +108,4 @@
.toc-btn {
transition: translate 0.3s ease;
}
@media screen and (max-width: 1023px) {
.aw-page-steps {
margin-block-start: 2rem;
}
}
</style>

View File

@@ -1,12 +1,13 @@
<script lang="ts">
import { Media } from '$lib/UI';
import { scroll } from '$lib/animations';
import { Article, FooterNav, MainFooter, Newsletter } from '$lib/components';
import { Main } from '$lib/layouts';
import { formatDate } from '$lib/utils/date';
import { getContext } from 'svelte';
import { scroll } from '$lib/animations';
import { DEFAULT_HOST } from '$lib/utils/metadata';
import type { AuthorData, CategoryData, PostsData } from '$routes/blog/content';
import { BLOG_TITLE_SUFFIX } from '$routes/titles';
import { getContext } from 'svelte';
export let title: string;
export let description: string;
@@ -64,7 +65,10 @@
<div class="aw-container" style="--container-size:42.5rem">
<article class="aw-main-article">
<header class="aw-main-article-header">
<a class="aw-link aw-u-color-text-secondary" href="/blog">
<a
class="aw-link aw-u-color-text-secondary u-cross-baseline"
href="/blog"
>
<span class="aw-icon-chevron-left" aria-hidden="true" />
<span>Back to blog</span>
</a>
@@ -150,7 +154,7 @@
</header>
{#if cover}
<div class="aw-media-container">
<img class="u-block" src={cover} alt="" />
<Media class="u-block" src={cover} />
</div>
{/if}
@@ -195,9 +199,7 @@
</section>
</div>
</div>
<div
class="aw-big-padding-section-level-2 u-position-relative u-overflow-hidden"
>
<div class="aw-big-padding-section-level-2 u-position-relative u-overflow-hidden">
<div class="aw-container">
<Newsletter />
<FooterNav />

View File

@@ -9,9 +9,11 @@
import { melt } from '@melt-ui/svelte';
export let content: string;
export let toCopy: string | undefined = undefined;
export let language: Language;
export let process: boolean;
export let withLineNumbers = true;
export let badge: string | null = null;
const insideMultiCode = hasContext('multi-code');
const selected = insideMultiCode ? getContext<CodeContext>('multi-code').selected : null;
@@ -22,7 +24,7 @@
}
let copyText = CopyStatus.Copy;
async function handleCopy() {
await copy(content);
await copy(toCopy ?? content);
copyText = CopyStatus.Copied;
setTimeout(() => {
@@ -49,6 +51,8 @@
$: result = process
? getCodeHtml({ content, language: language ?? 'sh', withLineNumbers })
: content;
$: badgeValue = badge ?? platformMap[language];
</script>
{#if insideMultiCode}
@@ -60,9 +64,9 @@
<section class="theme-dark aw-code-snippet" aria-label="code-snippet panel">
<header class="aw-code-snippet-header">
<div class="aw-code-snippet-header-start">
{#if platformMap[language]}
{#if badgeValue}
<div class="u-flex u-gap-16">
<div class="aw-tag"><span class="text">{platformMap[language]}</span></div>
<div class="aw-tag"><span class="text">{badgeValue}</span></div>
</div>
{/if}
</div>

View File

@@ -1,7 +1,7 @@
<script lang="ts">
import { getContext, hasContext, onMount } from 'svelte';
import type { LayoutContext } from '../layouts/Article.svelte';
import { isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
import { isInPolicy } from '$markdoc/layouts/Policy.svelte';
export let level: number;
export let id: string | undefined = undefined;
@@ -48,12 +48,11 @@
observer.observe(element);
});
const inPolicy = isInsidePolicy();
const inPolicy = isInPolicy();
$: headingClass = inPolicy && level === 1 ? 'aw-title' : classList[level];
</script>
{#if id}
<svelte:element
this={tag}
{id}
@@ -64,7 +63,6 @@
>
<a href={`#${id}`} class=""><slot /></a>
</svelte:element>
{:else}
<svelte:element
this={tag}

View File

@@ -1,7 +1,18 @@
<script lang="ts">
import { getContext } from 'svelte';
import { isInDocs } from '$lib/layouts/Docs.svelte';
import { isInChangelog } from '$markdoc/layouts/Changelog.svelte';
import { isInPolicy } from '$markdoc/layouts/Policy.svelte';
const isDocs = getContext('isDocs') ?? false;
const inDocs = isInDocs();
const inChangelog = isInChangelog();
const inPolicy = isInPolicy();
$: classes = (() => {
if (inDocs) return '';
if (inChangelog) return '';
if (inPolicy) return '';
return 'aw-paragraph-lg';
})();
</script>
<li><p class:aw-paragraph-lg={!isDocs}><slot /></p></li>
<li><p class={classes}><slot /></p></li>

View File

@@ -1,6 +1,6 @@
<script lang="ts">
import { getContext } from 'svelte';
import { TABLE_CTX_KEY } from './Table.svelte';
import { isInChangelog } from '$markdoc/layouts/Changelog.svelte';
import { hasContext } from 'svelte';
export let href: string;
export let title: string;
@@ -9,14 +9,20 @@
const target = isExternal ? '_blank' : undefined;
const rel = isExternal ? 'noopener nofollow' : undefined;
const isDocs = getContext('isDocs') ?? false;
const inTable = getContext(TABLE_CTX_KEY) ?? false;
const isDocs = hasContext('isDocs');
const inChangelog = isInChangelog();
$: classes = (() => {
if (isDocs) return 'aw-paragraph-md';
if (inChangelog) return 'aw-paragraph-lg in-changelog';
return '';
})();
</script>
<a
class="aw-link is-inline"
{href}
{title}
{target}
{rel}><slot /></a
>
<a class="aw-link is-inline {classes}" {href} {title} {target} {rel}><slot /></a>
<style lang="scss">
.in-changelog:last-child {
margin-block-start: 1rem;
}
</style>

View File

@@ -1,19 +1,22 @@
<script lang="ts">
import { isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
import { isInChangelog } from '$markdoc/layouts/Changelog.svelte';
import { isInPolicy } from '$markdoc/layouts/Policy.svelte';
import { setContext } from 'svelte';
export let ordered: boolean;
setContext('no-paragraph', true);
const inPolicy = isInsidePolicy();
const inPolicy = isInPolicy();
const inChangelog = isInChangelog();
</script>
<svelte:element
this={ordered ? 'ol' : 'ul'}
class:aw-numeric-list={ordered}
class:aw-pink-dots={!ordered}
class:in-policy={inPolicy}><slot /></svelte:element
class:in-policy={inPolicy}
class:in-changelog={inChangelog}><slot /></svelte:element
>
<style lang="scss">
@@ -42,4 +45,10 @@
padding-inline-end: 0;
}
}
.in-changelog {
padding-inline-start: 0.9rem;
margin-block-end: 1rem;
gap: 0.75rem;
}
</style>

View File

@@ -1,23 +1,31 @@
<script lang="ts">
import { isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
import { isInChangelog } from '$markdoc/layouts/Changelog.svelte';
import { isInPolicy } from '$markdoc/layouts/Policy.svelte';
import { getContext, hasContext } from 'svelte';
import { isInTable } from './Table.svelte';
const noParagraph = hasContext('no-paragraph') ? getContext('no-paragraph') : false;
const isDocs = getContext('isDocs') ?? false;
const inPolicy = isInsidePolicy();
const isDocs = getContext<boolean>('isDocs') ?? false;
const inPolicy = isInPolicy();
const inChangelog = isInChangelog();
const inTable = isInTable();
let className = '';
export { className as class };
$: classes = (() => {
if (isDocs) return 'aw-paragraph-md';
if (inPolicy) return 'aw-paragraph-md in-policy';
if (inTable) return 'aw-paragraph-md';
if (inChangelog) return 'aw-paragraph-lg in-changelog';
return 'aw-paragraph-lg';
})();
</script>
{#if noParagraph}
<slot />
{:else if isDocs}
<p class="aw-paragraph-md {className}"><slot /></p>
{:else if inPolicy}
<p class="aw-paragraph-md in-policy {className}"><slot /></p>
{:else}
<p class="aw-paragraph-lg {className}"><slot /></p>
<p class="{classes} {className}"><slot /></p>
{/if}
<style lang="scss">
@@ -28,4 +36,9 @@
.in-policy {
margin-block-end: 1rem;
}
.in-changelog {
margin-block-end: 1rem !important;
font-weight: 400;
}
</style>

View File

@@ -1,17 +1,16 @@
<script lang="ts" context="module">
export const TABLE_CTX_KEY = 'in-table';
export const getTableCtx = () => getContext<boolean>(TABLE_CTX_KEY);
export const isInTable = () => !!getTableCtx();
export const TABLE_CTX_KEY = Symbol('table');
export const isInTable = () => getContext<boolean>(TABLE_CTX_KEY) ?? false;
</script>
<script lang="ts">
import { getPolicyCtx, isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
import { getPolicyCtx, isInPolicy } from '$markdoc/layouts/Policy.svelte';
import { getContext, setContext } from 'svelte';
setContext(TABLE_CTX_KEY, true);
const inPolicy = isInsidePolicy();
const inPolicy = isInPolicy();
const transparentCells = inPolicy ? getPolicyCtx().transparentTableCells : false;
</script>

View File

@@ -1,5 +1,6 @@
<script lang="ts">
import { getContext } from 'svelte';
import { isInDocs } from '$lib/layouts/Docs.svelte';
import { isInChangelog } from '$markdoc/layouts/Changelog.svelte';
export let href: string;
@@ -7,9 +8,26 @@
const target = isExternal ? '_blank' : undefined;
const rel = isExternal ? 'noopener nofollow' : undefined;
const isDocs = getContext('isDocs') ?? false;
const inChangelog = isInChangelog();
const inDocs = isInDocs();
$: classes = (() => {
if (inDocs) return 'aw-link aw-paragraph-md';
if (inChangelog) return 'aw-link aw-paragraph-lg';
return '';
})();
</script>
<a class="aw-link {isDocs ? 'aw-paragraph-md' : 'aw-paragraph-lg'}" {href} {target} {rel}
<a class={classes} data-in-changelog={inChangelog ? '' : undefined} {href} {target} {rel}
><slot /><span class="icon-cheveron-right" style:font-size="16px" /></a
>
<style>
a {
display: flex;
}
[data-in-changelog]:last-child {
padding-block-start: 1rem;
}
</style>

View File

@@ -0,0 +1,61 @@
## Check resource usage {% #check-resource-usage %}
You can check your organization's resource usage for the current billing cycle by navigating to your organization, under the **Usage** tab.
## Reaching resource limits {% #reaching-resource-limits %}
Reaching your organization's resource limits will have the following effects until the current billing period ends.
{% table %}
* Component
* Consequence
---
* **Bandwidth** {% rowspan=3 %}
* More bandwidth will be purchased automatically until your organization reaches a budget cap. If the organization uses a Starter plan or a budget cap is reached, API access will be denied until your organization's plan is upgraded or your budget cap is increased.
---
* Importing projects via migrations disabled, but you can still export your projects.
---
* Platform creation disabled.
---
* **Users**
* Creating new accounts and team invitations disabled.
---
* **Compute**
* Function executions are disabled.
---
* **Realtime**
* Realtime subscriptions disabled.
---
* **Storage**
* File uploads are disabled. Persists across billing periods until the amount of storage used is below the plan limit.
{% /table %}
## Switching to Starter plan and reaching limits {% #switching-to-starter-plan-reaching-resource-limits %}
When an orgnization switches from Pro or Scale plan to Starter plan, the organization's projects will be able to take advantage of the existing limits until the end of the current billing period.
After the billing period ends, the Starter plan limits and consequences will apply.
If an organisation has multiple members after the billing period ends, all admins besides the original creator of the organization will be removed.
The following consequences should also apply at the project level if the Starter plan per-project resource limits have been exceeded.
{% table %}
* Component
* Action
---
* **Platforms**
* If more than 3 platforms have been created, disable them in order of date created (oldest ones first).
---
* **Webhooks**
* If more than 2 webhooks have been created, disable them in order of date created (oldest ones first).
---
* **Teams**
* If more than 100 teams have been created, disable them in order of date created (oldest ones first).
---
* **Databases**
* If more than 1 database has been created, disable them in order of date created (oldest ones first).
---
* **Buckets**
* If more than 3 buckets have been created, disable them in order of date created (oldest ones first).
---
* **Functions**
* If more than 5 functions have been created, disable them in order of date created (oldest ones first).
{% /table %}

View File

@@ -1 +1,11 @@
import { getAllChangelogEntries } from "./changelog/utils";
export const prerender = true;
export const load = async () => {
return {
changelogEntries: (await getAllChangelogEntries()).length
}
};

View File

@@ -87,4 +87,29 @@
{/if}
</svelte:head>
<a class="skip" href="#main">Skip to content</a>
<slot />
<style lang="scss">
.skip {
position: absolute;
inset-block-start: 0;
z-index: 9999;
display: block;
background-color: hsl(var(--aw-color-mint-500));
color: hsl(var(--aw-color-black));
text-decoration: underline;
opacity: 0;
padding: 0.75rem 1.25rem;
pointer-events: none;
}
.skip:focus {
opacity: 1;
position: relative;
pointer-events: all;
}
</style>

View File

@@ -130,13 +130,13 @@
<div class="aw-big-padding-section-level-2">
<section class="aw-container aw-u-padding-block-end-0">
<a
href="/blog/post/meet-the-new-appwrite"
href="/blog/post/announcing-appwrite-pro"
class="aw-hero-banner-button aw-u-margin-block-end-24"
>
<span class="aw-icon-star" aria-hidden="true" />
<span class="aw-caption-500">New</span>
<div class="aw-hero-banner-button-sep" />
<span class="aw-caption-400">Meet the new Appwrite</span>
<span class="aw-caption-400">Announcing Appwrite Pro</span>
<span class="aw-icon-arrow-right" aria-hidden="true" />
</a>
<div class="aw-hero is-horizontal">

View File

@@ -1,12 +1,14 @@
<script lang="ts">
import { visible } from '$lib/actions';
import TocNav from '$lib/components/TocNav.svelte';
import TocRoot from '$lib/components/TocRoot.svelte';
import { Main } from '$lib/layouts';
import { copy } from '$lib/utils/copy';
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
import { TITLE_SUFFIX } from '$routes/titles';
import FooterNav from '../../lib/components/FooterNav.svelte';
import MainFooter from '../../lib/components/MainFooter.svelte';
import Copy from './Copy.svelte';
const title = 'Assets' + TITLE_SUFFIX;
const description = DEFAULT_DESCRIPTION;
@@ -27,21 +29,6 @@
Object.values(Section).map((section) => [section, false])
);
$: selected = (function getSelected() {
for (const [key, value] of selectedMap) {
if (value) {
return key;
}
}
return Section.NAMING;
})();
$: progress = (function getProgress() {
const sections = Object.values(Section);
const index = sections.indexOf(selected);
return (index / (sections.length - 1)) * 100;
})();
const handleVisibility = (section: Section) => {
return (e: CustomEvent<boolean>) => {
selectedMap.set(section, e.detail);
@@ -72,6 +59,7 @@
</div>
<Main>
<TocRoot activeType="highest">
<div class="aw-container">
<div class="aw-grid-120-1fr-auto">
<header class="aw-grid-120-1fr-auto-header">
@@ -90,35 +78,14 @@
</span>
</button>
</header>
<aside class="aw-grid-120-1fr-auto-side aw-is-mobile-closed">
<div class="aw-page-steps">
<div
class="aw-page-steps-location aw-is-not-mobile"
style="--location:{progress}%;"
>
<button class="aw-page-steps-location-button">
<img src="/images/animations/indicator.svg" alt="" />
</button>
</div>
<ul class="aw-page-steps-list aw-sub-body-500">
{#each Object.values(Section) as section}
<li>
<a
href="#{getSectionId(section)}"
class:is-selected={selected === section}>{section}</a
>
</li>
{/each}
</ul>
</div>
</aside>
<main class="aw-grid-120-1fr-auto-main /aw-is-mobile-closed">
<TocNav />
<main class="aw-grid-120-1fr-auto-main /aw-is-mobile-closed" id="main">
<div class="aw-content">
<section>
<p>
Resources for presenting the Appwrite brand to maintain consistency
while using our logos, colors, and other brand elements across various
platforms and materials.
while using our logos, colors, and other brand elements across
various platforms and materials.
</p>
<a href="/assets.zip" download class="aw-button u-cross-child-start">
@@ -137,9 +104,9 @@
Naming
</h2>
<p>
Write 'Appwrite,' with a lowercase 'w' and no space between the two
words. Please refrain from using variations like 'AppWrite' or 'App
Write'.
Write 'Appwrite,' with a lowercase 'w' and no space between the
two words. Please refrain from using variations like 'AppWrite'
or 'App Write'.
</p>
</div>
</section>
@@ -155,8 +122,8 @@
</h2>
<p>
The Appwrite logo stands as a prominent symbol of our brand's
identity. Refrain from altering our logo and preferably use our logo
on a neutral background.
identity. Refrain from altering our logo and preferably use our
logo on a neutral background.
</p>
<div class="u-flex u-flex-wrap u-gap-32 u-margin-block-start-12">
<div
@@ -174,7 +141,10 @@
href="/assets/logotype/white.svg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>SVG</span>
</a>
<a
@@ -182,7 +152,10 @@
href="/assets/logotype/white.png"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -202,7 +175,10 @@
href="/assets/logotype/black.svg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>SVG</span>
</a>
<a
@@ -210,7 +186,10 @@
href="/assets/logotype/black.png"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -250,10 +229,10 @@
Logomark
</h3>
<p>
While prioritizing recognizability, the logotype is the recommended
choice. Using the Appwrite logomark is suitable for situations where
space constraints make it challenging to showcase the complete
logotype.
While prioritizing recognizability, the logotype is the
recommended choice. Using the Appwrite logomark is suitable for
situations where space constraints make it challenging to
showcase the complete logotype.
</p>
<div class="u-flex u-flex-wrap u-gap-32 u-margin-block-start-12">
<div
@@ -271,7 +250,10 @@
href="/assets/logomark/logo.svg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>SVG</span>
</a>
<a
@@ -279,7 +261,10 @@
href="/assets/logomark/logo.png"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -299,7 +284,10 @@
href="/assets/logomark/logo.svg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>SVG</span>
</a>
<a
@@ -307,7 +295,10 @@
href="/assets/logomark/logo.png"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -355,13 +346,7 @@
<h3 class="aw-label">Light Grey</h3>
<p class="aw-caption-400">#EDEDF0</p>
<div class="buttons | theme-light">
<button
class="aw-button is-secondary"
on:click={() => copy('#EDEDF0')}
>
<span class="aw-icon-copy" aria-label="copy" />
<span>Copy</span>
</button>
<Copy toCopy="#EDEDF0" />
</div>
</div>
<div
@@ -373,13 +358,7 @@
<h3 class="aw-label">Dark Grey</h3>
<p class="aw-caption-400">#19191D</p>
<div class="buttons">
<button
class="aw-button is-secondary"
on:click={() => copy('#19191D')}
>
<span class="aw-icon-copy" aria-label="copy" />
<span>Copy</span>
</button>
<Copy toCopy="#19191D" />
</div>
</div>
<div
@@ -391,17 +370,7 @@
<h3 class="aw-label">Appwrite Pink</h3>
<p class="aw-caption-400">#FD366E</p>
<div class="buttons">
<button
class="aw-button is-secondary"
on:click={() => copy('#FD366E')}
>
<span
class="aw-icon-copy"
aria-label="copy"
style="color: white;"
/>
<span>Copy</span>
</button>
<Copy toCopy="#FD366E" />
</div>
</div>
</div>
@@ -417,8 +386,8 @@
Product visuals
</h2>
<p>
Use these product visuals to enhance your articles, presentations,
and content related to Appwrite.
Use these product visuals to enhance your articles,
presentations, and content related to Appwrite.
</p>
<div class="aw-grid-1-1-opt-2 u-gap-32 u-margin-block-start-12">
<div class="media-wrapper">
@@ -433,7 +402,10 @@
href="/assets/visuals/dashboard.jpg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>JPG</span>
</a>
<a
@@ -441,7 +413,10 @@
href={'/assets/visuals/dashboard.png'}
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -458,7 +433,10 @@
href="/assets/visuals/auth.jpg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>JPG</span>
</a>
<a
@@ -466,7 +444,10 @@
href={'/assets/visuals/auth.png'}
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -483,7 +464,10 @@
href="/assets/visuals/databases.jpg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>JPG</span>
</a>
<a
@@ -491,7 +475,10 @@
href={'/assets/visuals/databases.png'}
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -509,7 +496,10 @@
href="/assets/visuals/storage.jpg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>JPG</span>
</a>
<a
@@ -517,7 +507,10 @@
href={'/assets/visuals/storage.png'}
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -535,7 +528,10 @@
href="/assets/visuals/functions.jpg"
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>JPG</span>
</a>
<a
@@ -543,7 +539,10 @@
href={'/assets/visuals/functions.png'}
download
>
<span class="aw-icon-download" aria-label="download" />
<span
class="aw-icon-download"
aria-label="download"
/>
<span>PNG</span>
</a>
</div>
@@ -562,11 +561,11 @@
Contact us
</h2>
<p>
Should you require further assistance or have specific needs beyond
what's presented on this page, please don't hesitate to
Should you require further assistance or have specific needs
beyond what's presented on this page, please don't hesitate to
<a
class="aw-link"
href="/"
href="/contact-us"
target="_blank"
rel="noopener noreferrer">contact us</a
>.
@@ -580,6 +579,7 @@
<FooterNav />
<MainFooter />
</div>
</TocRoot>
</Main>
<style lang="scss">

View File

@@ -0,0 +1,17 @@
<script lang="ts">
import { copy } from '$lib/utils/copy';
export let toCopy: string;
let copied = false;
const copyToClipboard = () => {
copy(toCopy);
copied = true;
setTimeout(() => (copied = false), 2000);
};
</script>
<button class="aw-button is-secondary" on:click={copyToClipboard}>
<span class="aw-icon-{copied ? 'check' : 'copy'}" aria-label={copied ? 'Copied' : 'Copy'} />
<span>Copy</span>
</button>

View File

@@ -1,8 +1,6 @@
<script lang="ts">
import type { AuthorData } from '$markdoc/layouts/Author.svelte';
import type { CategoryData } from '$markdoc/layouts/Category.svelte';
import type { PostsData } from '$markdoc/layouts/Post.svelte';
import { setContext } from 'svelte';
import type { AuthorData, CategoryData, PostsData } from './content.js';
export let data;
setContext<PostsData[]>('posts', data.posts);

View File

@@ -31,6 +31,7 @@
</svelte:head>
<Main>
<div class="aw-big-padding-section">
<div class="aw-big-padding-section-level-1 u-position-relative">
<div
class="u-position-absolute"
@@ -202,4 +203,5 @@
</div>
</div>
</div>
</div>
</Main>

View File

@@ -1,6 +1,6 @@
import { base } from '$app/paths';
import type { AuthorData } from '$markdoc/layouts/Author.svelte';
import type { PostsData } from '$markdoc/layouts/Post.svelte';
import type { AuthorData, PostsData } from './content';
export function load() {
const postsGlob = import.meta.glob('./post/**/*.markdoc', {

View File

@@ -1,33 +1,29 @@
---
layout: post
title: "Announcing: Appwrite Pro"
description: Introducing the next chapter in our journey with Appwrite Cloud.
date: 2023-12-13
cover: /images/blog/announcing-appwrite-pro.png
description: Were happy to introduce Appwrite Pro. After announcing our pricing plans for Appwrite Cloud in August, we have reached another milestone as we roll out billing and welcome our newest addition to Appwrite Cloud. A new product offering that allows you to build with more confidence.
date: 2023-12-20
cover: /images/blog/announcing-appwrite-pro/header.png
timeToRead: 5
author: laura-du-ry
author: christy-jacob
category: cloud
featured: true
---
After [announcing our pricing plans](https://appwrite.io/blog/post/announcing-pricing) for Appwrite Cloud in August, we have reached yet another milestone as we welcome our newest addition to Appwrite Cloud, [Appwrite Pro](https://appwrite.io/pricing).
After [announcing our pricing plans](https://appwrite.io/blog/post/announcing-pricing) for Appwrite Cloud in August, we have reached another milestone as we roll out billing and welcome our newest addition to Appwrite Cloud, Appwrite Pro. A new product offering that allows you to build with more confidence.
While Appwrite Cloud is still in beta, Appwrite Pro is a new and exciting chapter we write together, allowing us to add more capabilities to Appwrite Cloud faster. It is yet another step towards our mission of making software development more accessible and fun for all.
While Appwrite Cloud is still in beta, Appwrite Pro is a new chapter we write together. Pro will help us add more features and capabilities to Appwrite, making app development more accessible and enjoyable for developers like you.
# The Appwrite Pro developer
# Appwrite Pro resources & support
Appwrite Pro helps engineers with big plans build successful applications. Appwrite Pro offers additional resources like more bandwidth, function executions, active users, and storage, allowing you to build with confidence. In addition, you will receive premium support and early access to features and products. With this, we bridge the gap between the Appwrite Starter and the Appwrite Pro developer, both equally as important but with significantly different needs. We focus on helping you get your projects to production faster and smoother and provide the tools, resources, and support you need to scale.
Previously, all Appwrite Cloud developers used the Starter plan with unrestricted resources. This changes with Pro. Depending on your needs, you can choose between two different plans for your organization(s):
# Pro resources & support
- Starter: a free plan that provides an easy starting point for budding projects
- Pro: a paid plan that offers more resources and premium support
Our pricing plans are based on a set of guiding principles we created, which we call the “Value Framework.” It guided us through the process of determining the resources in a Pro plan and a fair price. It focuses on delivering the maximum value for you without compromising on the affordability and accessibility of our products and services.
As we explained in depth in our [Pricing announcement](https://appwrite.io/blog/post/announcing-pricing), our pricing plans are based on a set of guiding principles we created, which we call the “Value Framework.” It guided us through determining the resources in a Pro plan and a fair price. It focuses on delivering the maximum value for you without compromising on the affordability and accessibility of our products and services.
Appwrite Pro is priced to help your team and Appwrite grow together. Instead of pricing per project, we price Appwrite Pro per member in your organization. By allowing you to have unlimited projects, we will enable you to experiment and build freely as an individual. When your app grows and your team grows, we bill per additional team member so our pricing is fair for development teams of all sizes.
Appwrite Pro also starts with a generous resource limit but allows you to buy additional resources like storage or bandwidth. This helps us avoid overcharging developers for resources they dont need while remaining flexible enough as you establish a growing user base.
Another important benefit is premium support. This is an email support service on top of our Discord community, where we will have a dedicated team to provide technical support.
Here is a snapshot of the differences in resources and support for Appwrite Starter and Appwrite Pro. You can find the complete overview on our [pricing page](https://appwrite.io/pricing#:~:text=Contact%20us-,Compare%20plans,-Discover%20our%20plans).
Pro gives you much more room and flexibility to build, scale, and maintain your apps compared to the Starter plan. Here is a snapshot of the differences in resources and support for Appwrite Starter and Appwrite Pro. You can find the complete overview on our [pricing page](https://appwrite.io/pricing#:~:text=Contact%20us-,Compare%20plans,-Discover%20our%20plans).
{% table %}
* Appwrite Starter
@@ -66,18 +62,68 @@ Here is a snapshot of the differences in resources and support for Appwrite Star
{% /table %}
## Authentication, Bandwidth & Monthly active users
With 300GB bandwidth and 200K monthly active users in the Pro plan, we ensure you can scale your user base and provide great performance for your users without facing large bills. Allowing this room for your growth at a low cost fits our philosophy that we grow together.
## Databases, Buckets & Storage
Another essential addition to your projects is having an unlimited number of databases, allowing you to tailor and optimize your data management. You can manage different data types and patterns, separate concerns, have stricter data governance and security and optimize performance. All are very necessary for more complex and demanding software applications.
Again, youre allowed unlimited buckets. This provides several benefits, including improved organization, scalability, modularity, data redundancy, storage cost management, and data governance. With a starting point of 150GB storage, you can grow your projects instantly without reaching high costs, allowing you to grow in production at a low cost.
## Functions & Executions
Having unlimited functions is a must-have for many, as it is the holy grail for building customized business logic to extend Appwrites capabilities. With unlimited functions and 3.5M executions, most projects wont exceed these limits anytime soon. You can run many executions like scheduled tasks, batch processing jobs, webhooks, and some IoT use cases with this resource.
## Customization
For a production product, you need more control of your brand, as well as the flexibility to add to your plan as it evolves. Within the Pro plan, it is possible to remove Appwrite branding from emails, have custom SMTP, and get resource add-ons to fit your needs.
## Premium support
For the last few years, we have had help from the community and dedicated moderators to help us support you using Appwrite. Now, with the launch of Appwrite Pro, we add premium support. This is an email support service on top of our Discord community, where we will have a dedicated team to provide technical support. We understand that this is an important addition for you to have when building with Appwrite. We want to ensure you can build with confidence.
![Appwrite Pro](/images/blog/announcing-appwrite-pro/pro.png)
# Growth in beta with early adopters
Appwrite Cloud has seen immense growth since we entered public beta. It has been truly encouraging to see what the strength of a valuable product and a fantastic community can do. Weve seen unique projects hosted on Appwrite Cloud. Cloud has helped developers scale their apps, while their feedback has helped us improve Cloud.
Beta developers are fundamental to the success of Appwrite Cloud today. We will provide each early adopter of Appwrite Cloud with credits for Appwrite Pro for the first two months as a sign of appreciation.
## Thanking early adopters
Beta developers are fundamental to the success of Appwrite Cloud today, and we are grateful for the support we have received. To give back and say thank you, we will provide each early adopter of Appwrite Cloud with credits for Appwrite Pro for the first two months.
![Appwrite Pro early adopters $30 Credits](/images/blog/announcing-appwrite-pro/credits.png)
## Existing organizations and projects
Early adopters of Appwrite Cloud may already have a large number of organizations and projects. With the release of Appwrite Pro, each Appwrite Cloud account will have access to only one free Starter organization. Additional projects will be paused. You can find an overview of your projects resources on the Appwrite Console. To help you make a smooth transition, you can take one of the following options.
- Upgrade additional organizations to the Pro plan using your early adopters credit.
- Move your projects using the **Transfer project** feature in your projects settings.
- [Migrate](https://appwrite.io/docs/advanced/migrations/cloud) your Appwrite Cloud projects to a self-hosted instance.
We appreciate the support and feedback from early Cloud adopters. These options should help you transition smoothly as we roll out Appwrite Pro.
# Appwrite Pro for open-source maintainers
Open-source software forms the foundation of modern software technologies. As lovers and builders of open-source software, we understand and appreciate the difficulty of building in the open. Were fortunate to have the opportunity to build open-source software sustainably, and we cant wait to give back.
Like our previous initiatives, such as the OSS Fund finalization, Appwrite Cloud will also support open-source developers. OSS maintainers can use Appwrite to build and maintain their projects on the Appwrite Pro plan for free. As always, Appwrite also remains open-sourced and free to self-host.
Like our previous initiatives, such as the OSS Fund, Appwrite Cloud will also support open-source developers. OSS maintainers can use Appwrite to build and maintain their projects on the Appwrite Pro plan for free. Please reach out on our [contact page](https://apwr.dev/OSSplan) to enquire about this plan. As always, Appwrite also remains open-sourced and free to [self-host](https://appwrite.io/docs/advanced/self-hosting).
We are excited to see your projects grow and succeed with Appwrite Cloud. While you benefit from everything Appwrite Pro has to offer, you also become advocates in our mission to make software development accessible and enjoyable for all.
Appwrite Pro is just the start. We will continue developing our product offering to match your growing needs and capabilities and are excited to see your projects succeed with Appwrite Cloud. While you benefit from everything Appwrite Pro has to offer, you also become advocates in our mission to make software development accessible and enjoyable for all.
Upgrade to Appwrite Pro and claim your early adopters credits.
Together, we build like a team of hundreds_
---
## Need help?
In case you need help upgrading, transferring your projects, or have billing and budget cap queries, please find the following resources:
- [FAQ on pricing page](https://apwr.dev/pricingFAQ)
- [Project migration](https://appwrite.io/docs/advanced/migrations/cloud)
- [Discord support](http://appwrite.io/discord)

View File

@@ -0,0 +1,38 @@
---
layout: post
title: Introducing the new Changelog feed
description: Unveiling the latest development highlights at Appwrite with our new Changelog feed.
date: 2023-12-22
cover: /images/blog/changelog.png
timeToRead: 5
author: eldad-fux
category: product
---
At Appwrite, we're constantly evolving our products, features, and experience, and we understand it is challenging to keep up with these modifications. Therefore, we introduce the Changelog feed to our website. An easier way to be informed about releases, updates, and fixes. The new Changelog feed is our commitment to transparency and collaboration and a great tool to be easily in the loop with all the small or big changes in our products.
## What to expect from the new Changelog
The Changelog is how we keep you informed and show our continuous improvements. Here's what you can find in it:
**Concise updates**: Each entry is a quick-to-digest snippet, giving you the gist of the update without requiring extensive reading.
**Highlighted features**: Discover the most impactful changes with highlights that draw your attention to what matters most.
**Resources**: Whether it's an in-depth blog post or an informative YouTube video, we provide links to resources that delve deeper into the update.
## Why should you check the Changelog
**Stay ahead**: We thrive on innovation. With the new Changelog, you're always informed about the latest features and enhancements in Appwrite.
**Plan better**: Understanding what's new and what's changed can help you plan your projects and teamwork more effectively.
**Community**: Your feedback is vital. By staying updated, you can provide more timely and relevant feedback to help shape the future of Appwrite and encourage collaboration in the Appwrite community.
## Easy access
Accessing the Changelog is easy, all you need to do is look for the small badge near the Changelog link on the Appwrite website. This badge is your clue that something new and exciting awaits. A single click and you're there, exploring all the latest developments.
![The new Changelog feed alert](/images/blog/changelog-alert.png "The new Changelog feed alert")
The Changelog page is more than a feature; it's a tool for Appwrite developers and a commitment to the Appwrite community of developers and the values we share in the community. We're excited to hear your feedback about the new Changelog feed, so don't hesitate to join us on [Discord](https://appwrite.io/discord) and share your thoughts.

View File

@@ -2,22 +2,22 @@
layout: post
title: Announcing Cloud pricing plans
description: Appwrite Cloud pricing are now available.
date: 2023-05-02
date: 2023-08-08
cover: /images/blog/cloud-pricing.png
timeToRead: 5
author: laura-du-ry
category: cloud
---
An important day has come for Appwrite, where we finally announce the pricing for Appwrite Cloud. Many of you mentioned that in order to continue building with Appwrite Cloud it is important to know what the potential costs are. So due to the high number of requests and to provide clarity we decided to share our pricing models before they're live. Please note that Appwrite Cloud continues to be free while in beta and pricing will be effective once we are generally available.
An important day has come for Appwrite, where we finally announce the pricing for Appwrite Cloud. Many of you mentioned that in order to continue building with Appwrite Cloud it is important to know what the potential costs are. So due to the high number of requests and to provide clarity we decided to share our pricing models before they're live. Please note that Appwrite Cloud continues to be free untill billing is enabled towards the end of 2024.
Now let's dive in.
# Building for the future
As part of our mission to make software more accessible, we wanted to make sure our pricing complements that mission but also enables us to have the resources to continue building a strong company that can reach new levels. We have put a lot of effort into creating Appwrite's pricing that aligns with our values, transparency, ambition, and simplicity.
As part of our mission to make software development more accessible and enjoyable, we wanted to make sure our pricing complements that mission but also enables us to have the resources to continue building a strong foundation that can reach new levels. We have put a lot of effort into creating Appwrite's pricing that aligns with our values, transparency, ambition, and simplicity.
# Value framework
# Value Framework
We created a set of principles to guide us through the pricing process. We like to call this the “Value Framework” as it focuses on delivering the maximum value to our users without compromising on the affordability and accessibility of our products and services.
@@ -36,14 +36,17 @@ The above framework supports our goal to make Appwrite Cloud affordable and acce
![Appwrite pricing plans](/images/blog/pricing-plans.png)
**Starter Plan** a free tier for every developer working on a side project or SaaS product that can thrive on our starter plan limits.
Free
**Starter Plan**
- A free tier for every developer working on a side project or SaaS product that can thrive on our starter plan limits.
- Free.
**Pro Plan** a plan that supports your ambitions to scale your production project and allows you to grow. With flexible budget control tools to ensure you never get a surprise bill.
$15, per month, per member
**Pro Plan**
- A plan that supports your ambitions to scale your production project and allows you to grow. With flexible budget control tools to ensure you never get a surprise bill.
- $15, per month, per member.
**Scale plan** a plan that offers more support as you continue to scale your commercial product and team. With higher limits and more support from our team to ensure we support the demand of scaling teams.
$685, per month, per organization
**Scale plan**
- A plan that offers more support as you continue to scale your commercial product and team. With higher limits and more support from our team to ensure we support the demand of scaling teams.
- $685, per month, per organization.
# Business model: Pay per organization member

View File

@@ -0,0 +1,38 @@
---
layout: post
title: Announcing the Appwrite OSS Program
description: Appwrite's newest initiative to support open-source projects and their maintainers.
date: 2023-12-28
cover: /images/blog/OSS-program.png
timeToRead: 3
author: laura-du-ry
category: OSS
---
### Continued support for OSS maintainers
After successfully completing the OSS Fund, were excited to announce our newest initiative to support the open-source community and its maintainers, the Appwrite OSS program.
The OSS program is a dedicated and exclusive program that invites open-source maintainers to become Appwrite Pro, free of charge.
### Become Appwrite Pro with the OSS Program
The OSS Program, similar to the OSS Fund, aims to support OSS projects and their maintainers by alleviating financial burdens and promoting growth. By joining, you will receive a free Appwrite Pro subscription and benefit from all its resources and support. This enables you to use Appwrite Pro while pursuing your dream projects without worrying about potential costs and get premimum support. The program has no fixed end date but will be reviewed annually to ensure optimal mutual support.
### OSS and Appwrite Criteria
To apply for this program, you must adhere to the official criteria for having an open-source project, which is integral to being an acknowledged open-source maintainer. In addition, we have Appwrite criteria that will apply. You will need to provide the following information:
- Your project needs to meet the criteria of the [Open Source Initiative definition](https://opensource.org/osd/)
- Have obtained an approved [OSS license](https://opensource.org/licenses/)
- Have an open-source GitHub repository
- Have an active project that has at least 15 contributions and 100 stars
- Have a nonprofit or pre-revenue project
### Your application
To apply, you can simply contact us on our [contact page](https://appwrite.io/contact-us). Please add `OSS Program` to the subject line and tell us why you would like to apply in the comment section. Please also share a link to your project's repository. We will review your request as soon as possible and get back to you to go over your application. Please note that Appwrite holds the sole discretion on deciding whether to accept projects.
### Just the beginning
At Appwrite, we strongly believe in helping OSS maintainers, and we are humbled to have the opportunity and privilege to contribute to the growth of developers. Were excited to see many OSS projects join the program. This is only the beginning. As we grow, so shall our support with more funds, more knowledge, and more time. The future is bright. The future is open source.

View File

@@ -43,7 +43,7 @@ Eventually, to increase K-Collect users' access to desktop-based clients and ena
One of the most important aspects of any BaaS platform is the features and offerings it provides a developer. Appwrite was a boon for K-Collect, as it offered several features for their product. Currently, K-Collect is leveraging Appwrite Authentication to manage identity (and relevant functionalities) for the entire web platform. All information related to K-pop albums, stars, and photo card metadata that K-Collect uses is stored in Appwrite Databases. The photo cards themselves are stored and consumed through Appwrite Storage. K-Collect also implements Appwrite Functions to integrate external 3rd-party services (such as sending emails) and other event-driven tasks related to the other Appwrite offerings being used.
At the moment, Ryan is developing an internal trading platform that will allow users to communicate with each other and buy/sell photo cards. For these features, Ryan will be returning Appwrite Functions for transaction management with a payments gateway and using a combination of Appwrite Databases and Appwrite Realtime for an internal chat platform.
At the moment, Ryan is developing an internal trading platform that will allow users to communicate with each other and buy/sell photo cards. For these features, Ryan will be returning to Appwrite Functions for transaction management with a payments gateway and using a combination of Appwrite Databases and Appwrite Realtime for an internal chat platform.
## Improved developer productivity due to easy SDK access

View File

@@ -46,7 +46,7 @@ Using an Appwrite-based stack enabled several benefits for the Smartbee team:
## Solving performance and security problems of a self-developed stack
In the initial MVP that Smartbee built, they faced several performance issues due to their custom Flask-based solution. The solution could barely manage the load from sensors in 2 coal mines. Since transitioning to an Appwrite-based stack, Smartbee is currently sustaining a substantially higher throughput:
In the initial MVP that Smartbee built, they faced several performance issues due to their custom Flask-based solution. The solution could barely manage the load from sensors in 2 coal mines. Since transitioning to an Appwrite-based stack, Smartbee is able to process a substantially higher amount of data:
1. **Database:** 35 million documents are added from a total of 70 sensors each month
2. **Bandwidth:** 500GB of bandwidth is transmitted between the IoT sensors, local coal mine servers, and the remote server each month
@@ -54,7 +54,7 @@ In the initial MVP that Smartbee built, they faced several performance issues du
## Improved developer productivity due to SDKs, Functions runtimes, and CLI
A significant factor in accelerating Smartbees developer productivity was Appwrites developer experience. The Appwrite CLI helped Smartbee quickly set up and manage all the local coal mine Appwrite instances and the remote server. Appwrites pre-built API endpoints and SDKs meant that Smartbee did not have to re-create any services to support the monitoring platform. They only had to swap in the function calls from Appwrites SDK for Python and replace the synchronization and CSV report management scripts with Python-based Appwrite functions. One of the most significant benefits that Smartbee received was our Storage offering. In the past, Smartbee used an FTP server to store and manage reports, which had severe performance deficits, considering the high data stream from Smartbees IoT sensors. Appwrite pre-built storage buckets and SDK allowed a simpler-to-implement yet performant alternative. In fact, Sergio learned Flutter and chose it for their client application because of the well-supported Appwrite SDK and learning resources available around the same.
A significant factor in accelerating Smartbees developer productivity was Appwrites developer experience. The Appwrite CLI helped Smartbee quickly set up and manage all the local coal mine Appwrite instances and the remote server. Appwrites pre-built API endpoints and SDKs meant that Smartbee did not have to re-create any services to support the monitoring platform. They only had to swap in the function calls from Appwrites SDK for Python and replace the synchronization and CSV report management scripts with Python-based Appwrite functions. One of the most significant benefits that Smartbee received was our Storage offering. In the past, Smartbee used an FTP server to store and manage reports, which had severe performance deficits, considering the high data stream from Smartbees IoT sensors. Appwrite's pre-built storage buckets and SDK allowed a simpler-to-implement yet performant alternative. In fact, Sergio learned Flutter and chose it for their client application because of the well-supported Appwrite SDK and learning resources available around the same.
## Support from a highly active and helpful virtual community

View File

@@ -7,7 +7,7 @@ cover: /images/blog/logo.png
timeToRead: 5
author: sara-kaandorp
category: design, brand
featured: true
featured: false
---
At Appwrite, we are constantly collaborating with the Appwrite community to improve Appwrite's products, services, and content. All for the sake of improving your developer experience, as well as staying true to the open-source values.

View File

@@ -0,0 +1,68 @@
---
layout: post
title: Supporting the future of open source
description: A short walk down memory lane to learn about Appwrites open-source journey so far and why we owe the community our support.
date: 2023-12-29
cover: /images/blog/Blog-cover-oss-journey.png
timeToRead: 10
author: laura-du-ry
category: OSS
---
With the release of [Appwrite Pro](https://appwrite.io/blog/post/announcing-appwrite-pro), we reached another milestone. A new development in our product offering that allows you to build with more confidence.
But for those of you thinking: This is a paid plan, so what about us open-source maintainers?
Well, we havent forgotten you. Appwrite is open-source, and we have come so far due to the love the open-source community has shown us. Therefore, we want to continue to give back to the community by allowing OSS maintainers to build with Appwrite Pro free of charge. With this OSS plan, we hope to ensure the future for many open-source maintainers, just like many of you have ensured ours.
Lets take a short walk down memory lane to learn about Appwrites open-source journey so far and why we owe the community our support.
### **Falling in love with open-source**
The [first ever pull request](https://github.com/appwrite/appwrite/pulls?page=105&q=is%3Apr+is%3Aclosed) for Appwrite was submitted in August 2019 by Eldad, Founder and CEO of Appwrite, and not much later, the [first community PR](https://github.com/appwrite/appwrite/pull/2) followed. Contributors kept on coming and helped with code, copy, accessibility, fixing bugs, and more. For Eldad at the time, it was remarkable to witness how devs would help out and expect nothing in return. They wanted to use Appwrite as their backend, so they helped improve Appwrite for themselves and also ensured others would not have to face the challenges of building a complex backend. During this period, Eldad fell in love with open-source and the community around it.
![Eldad's first ever PR for Appwrite](/images/blog/first-pr1.png)
Not much later, Appwrite's [open-source version](https://betterprogramming.pub/introducing-appwrite-an-open-source-backend-server-for-mobile-web-developers-4be70731575d) was launched. The launch went viral, our Hacker News post was trending, and our GitHub Stars charts went up with over 1,500 new stargazers in the first month after release. The love was evident from both sides and together with many contributors, Eldad worked tirelessly as a solo maintainer to build out the product envisioned, ensuring many other developers would not face the same struggles when building complex software.
![First ever community contribution for Appwrite](/images/blog/community-pr1.png)
Today, Appwrite has over [38K stars](https://github.com/appwrite/appwrite), 800 contributors, 3.5K forks, and thousands of contributions made, all with the help, support, and love of the open-source community.
![The number of contributions over the years](/images/blog/contributions-2023.png)
### **Pre-monetization as a solo maintainer**
After three months of getting serious traction, Eldad decided to become a full-time open-source maintainer, leaving a full-time job. After 12 months, with no monetization of Appwrite yet possible, the real struggle started. The financial burden as a solo maintainer was immense. Not only do you have to cover the costs of building your project, such as infrastructure, development tools, and hosting, but you also feel the financial burden of day-to-day life.
After many considerations to fight the financial pressure, Appwrite got an amazing investment offer from the outstanding individuals at Ibex and Seedcamp, who offered to support the vision. Six months later, they were followed by an investment from Bessemer (Auth0, Cloudinary) and Flybridge (Firebase, MongoDB) that led our 8.5m seed round. The timing could not have been better, and Appwrite was able to thrive with new financial stability.
### **Giving back to the community**
This financial power meant many things, such as Appwrite could build a team, invest in a better product offering, community swag, and start an OSS fund. And all of it with one thing in mind, giving back to the community. Helping those who have helped us.
### **Hiring from the OSS community**
The first thing Eldad did with the funding was build out the team. And what better place to find the right people than in the open-source community? We hired some of the first and top Appwrite contributors. [Christy](https://github.com/christyjacob4), [Torsten](https://github.com/TorstenDittmann/TorstenDittmann), [Damodar](https://github.com/lohanidamodar), and Brandon. They formed Appwrites founding engineering team. Damodar even had a [YouTube channel](https://www.youtube.com/@appwriters) that focussed on Appwrite and Flutter, just to show off the support we got from an early stage.
![The evolution of Torsten Dittman's contributions to Appwrite](/images/blog/Torsten-GitHub-profile.png)
As we grew, so did our team, and we added many more engineers to the team. Many of them came [from the community.](https://appwrite.io/community) Some contributed to code, some started helping others in the community, and some created content. To this day, we still hire from the community, as we stand by open source and its contributors. It is embedded in our DNA.
### **Appwrite Swag Store**
Another way we ensure funds from the community go back to the community is by giving back the money we earn on swags. For every shirt, hoodie, bag, or sock we sell from the [Appwrite store](https://appwrite.store/), we give back the profit made. In general, we believe swag should be there for the community, not to line our pockets.
### *OSS Fund*
We always knew that if we're ever fortunate enough to be in a position to give back funds to the community, we'd never think twice about it. And with our Series A, we were in that position. In 2022 we announced the Appwrite OSS Fund, a $50,000 fund to support open-source projects.
At the time, Eldad put the purpose of the fund into words, and we couldnt phrase it any better:
*In the journey of turning your dream project into a reality, all that you need to have is the vision to build an awesome open-source project. The idea behind the fund is to support developers like you and the open-source project you are building.*
This fund has now come to an end, but we successfully supported twenty OSS maintainers, such as [LinkFree](https://github.com/EddieHubCommunity/BioDrop) (now BioDrop), [Vale](https://github.com/errata-ai/vale), [Rally](https://github.com/lukevella/rallly), and [Strawberry](https://github.com/strawberry-graphql/strawberry/).
### **Pro Plan for free**
Now that we have released Appwrite Pro, we can also release our new initiative to give back to the OSS community, the Appwrite OSS Program. OSS maintainers can apply for the OSS Program and use Appwrite Pro resources free of charge. This plan is important to us as we strongly believe in helping OSS maintainers, and we are humbled to have the opportunity and privilege to contribute to the growth of developers. We know very well where we came from, and Appwrite has been built with and by the open-source community. Were excited to continue our support and to see OSS maintainers reach out to build with Appwrite Pro. In case you are such a maintainer, read our [announcement blog](https://appwrite.io/blog/post/announcing-the-appwrite-oss-program) to learn more.

View File

@@ -1,30 +1,63 @@
<script lang="ts">
import { FooterNav, MainFooter } from '$lib/components';
import { Main } from '$lib/layouts';
</script>
<div class="u-position-absolute aw-is-only-mobile u-inset-inline-0">
<svg class="u-width-full-line"
xmlns="http://www.w3.org/2000/svg" width="360" height="661" viewBox="0 0 360 661" fill="none">
<svg
class="u-width-full-line"
xmlns="http://www.w3.org/2000/svg"
width="360"
height="661"
viewBox="0 0 360 661"
fill="none"
>
<g opacity="0.24" filter="url(#filter0_f_3260_129410)">
<ellipse cx="180" cy="80" rx="999" ry="325" fill="#FD366E"/>
<ellipse cx="180" cy="80" rx="999" ry="325" fill="#FD366E" />
</g>
<defs>
<filter id="filter0_f_3260_129410" x="-1074.02" y="-500.022" width="2508.04" height="1160.04" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape"/>
<feGaussianBlur stdDeviation="127.511" result="effect1_foregroundBlur_3260_129410"/>
<filter
id="filter0_f_3260_129410"
x="-1074.02"
y="-500.022"
width="2508.04"
height="1160.04"
filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur
stdDeviation="127.511"
result="effect1_foregroundBlur_3260_129410"
/>
</filter>
</defs>
</svg>
</div>
<div class="u-position-absolute" style="inline-size:100rem; inset-block-start:calc(50% - 250px); inset-inline-start:50%; transform:translateX(-50%);">
<div class="u-position-absolute u-inset-inline-start-0 aw-u-inset-block-start-100 aw-is-not-mobile">
<img src="/images/pages/brand-lunch/left-side-top-brand.png" alt width="800" style="transform:translateX(-35%)">
<div
class="u-position-absolute"
style="inline-size:100rem; inset-block-start:calc(50% - 250px); inset-inline-start:50%; transform:translateX(-50%);"
>
<div
class="u-position-absolute u-inset-inline-start-0 aw-u-inset-block-start-100 aw-is-not-mobile"
>
<img
src="/images/pages/brand-lunch/left-side-top-brand.png"
alt=""
width="800"
style="transform:translateX(-35%)"
/>
</div>
<div class="u-position-absolute u-inset-inline-end-0 aw-u-inset-block-start-100 aw-is-not-mobile">
<img src="/images/pages/brand-lunch/right-side-top-brand.png" alt width="800" style="transform:translateX(35%)">
<div
class="u-position-absolute u-inset-inline-end-0 aw-u-inset-block-start-100 aw-is-not-mobile"
>
<img
src="/images/pages/brand-lunch/right-side-top-brand.png"
alt=""
width="800"
style="transform:translateX(35%)"
/>
</div>
</div>
@@ -52,7 +85,10 @@
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="127.511" result="effect1_foregroundBlur_3260_127073" />
<feGaussianBlur
stdDeviation="127.511"
result="effect1_foregroundBlur_3260_127073"
/>
</filter>
</defs>
</svg>
@@ -62,59 +98,78 @@
<div class="aw-big-padding-section">
<div class="aw-big-padding-section-level-1">
<div class="aw-container u-position-relative">
<div class="aw-big-padding-section-level-2 aw-u-max-width-800 u-flex u-main-center u-cross-center u-margin-inline-auto aw-u-margin-block-0 "
style="block-size:calc(100vh - 6.375rem)">
<div
class="aw-big-padding-section-level-2 aw-u-max-width-800 u-flex u-main-center u-cross-center u-margin-inline-auto aw-u-margin-block-0"
style="block-size:calc(100vh - 6.375rem)"
>
<div class="aw-hero aw-u-gap-20">
<h1 class="aw-headline aw-u-color-text-primary">Brand new Appwrite</h1>
<p class="aw-description aw-u-color-text-primary aw-u-opacity-64">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae diam augue.
Praesent sagittis eget ipsum vitae eleifend.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae
diam augue. Praesent sagittis eget ipsum vitae eleifend.
</p>
</div>
</div>
<div class="aw-big-padding-section-level-2 aw-u-sep-block-start">
<div class="u-stretch u-flex aw-u-flex-vertical-mobile">
<div class="aw-hero is-align-start aw-u-max-width-480 aw-u-padding-block-start-48">
<h2 class="aw-title aw-u-color-text-primary">All the tools that developers need</h2>
<div
class="aw-hero is-align-start aw-u-max-width-480 aw-u-padding-block-start-48"
>
<h2 class="aw-title aw-u-color-text-primary">
All the tools that developers need
</h2>
<p class="aw-main-body-500">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae diam augue.
Praesent sagittis eget ipsum vitae eleifend.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae
diam augue. Praesent sagittis eget ipsum vitae eleifend.
</p>
</div>
<div class="u-stretch u-flex aw-u-margin-inline-auto-mobile aw-u-margin-block-start-40-mobile">
<img style="margin-block-start:-0.5rem"
class="u-margin-inline-start-auto u-width-full-line u-max-width-500" src="/images/pages/brand-lunch/box.png" alt>
<div
class="u-stretch u-flex aw-u-margin-inline-auto-mobile aw-u-margin-block-start-40-mobile"
>
<img
style="margin-block-start:-0.5rem"
class="u-margin-inline-start-auto u-width-full-line u-max-width-500"
src="/images/pages/brand-lunch/box.png"
alt=""
/>
</div>
</div>
</div>
<div class="aw-big-padding-section-level-2 aw-u-sep-block-start">
<div class="aw-grid-1-1-opt-2">
<div class="aw-hero is-align-start aw-u-padding-block-start-40">
<h2 class="aw-title aw-u-color-text-primary">Eliminating complexity</h2>
<p class="aw-main-body-500">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae diam augue.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae
diam augue.
</p>
<div class="u-margin-block-start-48">
<img class="u-max-width-100-percent" src="/images/pages/brand-lunch/box-1.png" alt>
<img
class="u-max-width-100-percent"
src="/images/pages/brand-lunch/box-1.png"
alt=""
/>
</div>
</div>
<div class="aw-hero is-align-start
aw-u-padding-block-start-40 aw-u-sep-inline-start-not-break1 aw-u-padding-inline-start-48-not-break1">
<div
class="aw-hero is-align-start
aw-u-padding-block-start-40 aw-u-sep-inline-start-not-break1 aw-u-padding-inline-start-48-not-break1"
>
<h2 class="aw-title aw-u-color-text-primary">
Allowing you to innovate without limits
</h2>
<p class="aw-main-body-500">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae diam augue.
Praesent sagittis eget ipsum vitae eleifend.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Etiam vitae
diam augue. Praesent sagittis eget ipsum vitae eleifend.
</p>
<div class="u-margin-block-start-48">
<img class="u-max-width-100-percent" src="/images/pages/brand-lunch/box-2.png" alt>
<img
class="u-max-width-100-percent"
src="/images/pages/brand-lunch/box-2.png"
alt=""
/>
</div>
</div>
</div>
@@ -177,8 +232,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<filter
id="filter1_f_3260_125165"
@@ -190,8 +253,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<filter
id="filter2_f_3260_125165"
@@ -203,8 +274,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<radialGradient
id="paint0_radial_3260_125165"
@@ -291,8 +370,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<filter
id="filter1_f_3260_125160"
@@ -304,8 +391,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="80"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<filter
id="filter2_f_3260_125160"
@@ -317,8 +412,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<radialGradient
id="paint0_radial_3260_125160"
@@ -359,7 +462,9 @@
<div class="aw-big-padding-section-level-2 u-position-relative">
<div class="aw-container">
<div class="aw-hero">
<p class="aw-headline aw-u-color-text-primary aw-u-max-width-610 u-margin-inline-auto">
<p
class="aw-headline aw-u-color-text-primary aw-u-max-width-610 u-margin-inline-auto"
>
Build like a team of hundreds
</p>
</div>
@@ -422,8 +527,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<filter
id="filter1_f_3260_125165"
@@ -435,8 +548,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<filter
id="filter2_f_3260_125165"
@@ -448,8 +569,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125165" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125165"
/>
</filter>
<radialGradient
id="paint0_radial_3260_125165"
@@ -536,8 +665,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<filter
id="filter1_f_3260_125160"
@@ -549,8 +686,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="80"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<filter
id="filter2_f_3260_125160"
@@ -562,8 +707,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125160" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125160"
/>
</filter>
<radialGradient
id="paint0_radial_3260_125160"
@@ -750,8 +903,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<filter
id="filter1_f_3260_125183"
@@ -763,8 +924,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="80" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="80"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<filter
id="filter2_f_3260_125183"
@@ -776,8 +945,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<filter
id="filter3_f_3260_125183"
@@ -789,8 +966,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="150" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="150"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<filter
id="filter4_f_3260_125183"
@@ -802,8 +987,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<filter
id="filter5_f_3260_125183"
@@ -815,8 +1008,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="70" result="effect1_foregroundBlur_3260_125183" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="70"
result="effect1_foregroundBlur_3260_125183"
/>
</filter>
<radialGradient
id="paint0_radial_3260_125183"
@@ -974,9 +1175,27 @@
viewBox="0 0 65 64"
fill="none"
>
<rect x="24.6846" y="25.3359" width="28.27" height="13.9945" fill="#C3C3C6" />
<rect x="11.9727" y="44.207" width="41.0827" height="14.0127" fill="#C3C3C6" />
<rect x="11.9453" y="7" width="28.27" height="13.9945" fill="#C3C3C6" />
<rect
x="24.6846"
y="25.3359"
width="28.27"
height="13.9945"
fill="#C3C3C6"
/>
<rect
x="11.9727"
y="44.207"
width="41.0827"
height="14.0127"
fill="#C3C3C6"
/>
<rect
x="11.9453"
y="7"
width="28.27"
height="13.9945"
fill="#C3C3C6"
/>
</svg>
<div class="aw-caption-400">With a passion for coding</div>
</div>
@@ -1066,7 +1285,11 @@
fill="#EDEDF0"
/>
</g>
<g style="mix-blend-mode:overlay" opacity="0.35" filter="url(#filter7_f_3260_127730)">
<g
style="mix-blend-mode:overlay"
opacity="0.35"
filter="url(#filter7_f_3260_127730)"
>
<ellipse
cx="376.561"
cy="45.906"
@@ -1123,8 +1346,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="180.206" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="180.206"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter1_f_3260_127730"
@@ -1136,8 +1367,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="132.713" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="132.713"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter2_f_3260_127730"
@@ -1149,8 +1388,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="132.713" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="132.713"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter3_f_3260_127730"
@@ -1162,8 +1409,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="108.124" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="108.124"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter4_f_3260_127730"
@@ -1175,8 +1430,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="250.098" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="250.098"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter5_f_3260_127730"
@@ -1188,8 +1451,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="250.098" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="250.098"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter6_f_3260_127730"
@@ -1201,8 +1472,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="63.0722" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="63.0722"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter7_f_3260_127730"
@@ -1214,8 +1493,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="50.0197" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="50.0197"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter8_f_3260_127730"
@@ -1227,8 +1514,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="63.0722" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="63.0722"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter9_f_3260_127730"
@@ -1240,8 +1535,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="63.0722" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="63.0722"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<filter
id="filter10_f_3260_127730"
@@ -1253,8 +1556,16 @@
color-interpolation-filters="sRGB"
>
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feBlend mode="normal" in="SourceGraphic" in2="BackgroundImageFix" result="shape" />
<feGaussianBlur stdDeviation="108.124" result="effect1_foregroundBlur_3260_127730" />
<feBlend
mode="normal"
in="SourceGraphic"
in2="BackgroundImageFix"
result="shape"
/>
<feGaussianBlur
stdDeviation="108.124"
result="effect1_foregroundBlur_3260_127730"
/>
</filter>
<linearGradient
id="paint0_linear_3260_127730"
@@ -1274,12 +1585,18 @@
<div class="aw-container">
<div class="aw-grid-1-1-opt-2 u-gap-32 u-main-space-between">
<div class="aw-hero is-align-start aw-u-max-width-480">
<h4 class="aw-title aw-u-color-text-primary">Be a part of #Teamof100</h4>
<p class="aw-main-body-500 u-margin-block-start-4 aw-u-color-text-primary aw-u-opacity-64">
Join our rebrand celebration by sharing #teamof100 in your socials for a chance to win
brand-new Appwrite swag.
<h4 class="aw-title aw-u-color-text-primary">
Be a part of #Teamof100
</h4>
<p
class="aw-main-body-500 u-margin-block-start-4 aw-u-color-text-primary aw-u-opacity-64"
>
Join our rebrand celebration by sharing #teamof100 in your socials
for a chance to win brand-new Appwrite swag.
</p>
<button class="aw-button is-transparent u-margin-block-start-16 is-full-width-mobile">
<button
class="aw-button is-transparent u-margin-block-start-16 is-full-width-mobile"
>
<span class="aw-icon-star" aria-hidden="true" />
<span>Share</span>
</button>

View File

@@ -0,0 +1,18 @@
---
layout: changelog
title: Appwrite 1.4
date: 2023-08-30
cover: /images/changelog/2023-08-30.png
---
Weve just released Appwrite 1.4 with the goals to eliminate barriers to get started, maximize flexibility to grow, and deliver improved security for peace of mind.
Notable new features:
- Next generation of Appwrite Functions brings Git deployments, http executions, intuitive syntax, and reliable logging.
- Command center for quicker console navigation and keyboard shortcuts
- AI assistant to help you search docs and answer questions.
- One-Click migrations lets you migrate from Firebase, Supabase, and NHost. You can also migrate between Appwrite Cloud and self-hosted.
{% arrow_link href="https://dev.to/appwrite/appwrite-14-brings-command-center-ai-one-click-migrations-and-upgraded-functions-1ch2" %}
Read the full announcement
{% /arrow_link %}

View File

@@ -0,0 +1,16 @@
---
layout: changelog
title: Meet the new Appwrite
date: 2023-09-21
cover: /images/changelog/2023-09-21.png
---
Unveiling Appwrite's new fresh look and feel with a new brand, website and experience.
- New brand, new logo, and new design that aligns with our growth
- Design elements inspired by the community and reflects our values
- New documentation with improved navigation, search, and more beginner friendly tutorials
{% arrow_link href="/blog/post/meet-the-new-appwrite" %}
Read the full announcement
{% /arrow_link %}

View File

@@ -0,0 +1,19 @@
---
layout: changelog
title: Announcing Appwrite Pro
date: 2023-12-20
cover: /images/changelog/2023-12-20.png
---
Appwrite Pro is now available for Appwrite Cloud developers.
Previously, all Appwrite Cloud developers used the Starter plan with unrestricted resources.
With Pro, you can choose between two different plans for your organization(s):
- Starter: a free plan that provides an easy starting point for budding projects
- Pro: for developers who need more resources to scale their apps,
customized branding in emails and messages, more team members per organization, and premium support.
{% arrow_link href="/blog/post/announcing-appwrite-pro" %}
Read the full announcement
{% /arrow_link %}

View File

@@ -0,0 +1,15 @@
---
layout: changelog
title: Changelog
date: 2023-12-23
cover: /images/changelog/2023-12-23.png
---
Appwrite's capabilities grow everyday and it's difficult to keep up.
To help everyone stay updated, we added a dedicated changelog page to the Appwrite website.
You can now find all of Appwrite's latest features, changes, and announcements all in one place.
{% arrow_link href="/blog/post/announcing-new-changelog" %}
Read the full announcement
{% /arrow_link %}

View File

@@ -0,0 +1,14 @@
---
layout: changelog
title: Announcing the Appwrite OSS Program
date: 2023-12-28
cover: /images/changelog/oss-progam.png
---
Appwrite's newest initiative to support open-source projects and their maintainers.
After successfully completing the OSS Fund, were excited to announce our newest initiative to support the open-source community and its maintainers, the Appwrite OSS program.
{% arrow_link href="/blog/post/announcing-the-appwrite-oss-program" %}
Read the full announcement
{% /arrow_link %}

View File

@@ -0,0 +1,46 @@
<script lang="ts">
import { formatDate } from '$lib/utils/date';
import type { PageData } from './[[page]]/$types';
export let entry: PageData['entries'][0];
</script>
<div class="changelog-entry">
<time class="aw-caption-400 padded" datetime={entry.date}>{formatDate(entry.date)}</time>
{#if entry.cover}
<a href={entry.href} class="aw-media">
<img src={entry.cover} alt="" class="aw-u-media-ratio-16-9 u-width-full-line" />
</a>
{/if}
<div class="padded">
<h2 class="aw-title aw-u-color-text-primary">
<a href={entry.href}>
{entry.title}
</a>
</h2>
<slot />
</div>
</div>
<style lang="scss">
.changelog-entry {
display: grid;
gap: 20px;
}
h2 {
display: grid;
padding-block: 0.5rem 1rem;
&:hover {
text-decoration: underline;
}
}
@media screen and (min-width: 513px) {
.padded {
padding-inline: 1rem;
}
}
</style>

View File

@@ -0,0 +1,143 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { FooterNav, MainFooter, PreFooter } from '$lib/components';
import { Main } from '$lib/layouts';
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
import { onMount } from 'svelte';
import ChangelogEntry from '../ChangelogEntry.svelte';
import { page } from '$app/stores';
import { CHANGELOG_KEY } from '../utils';
export let data;
const seo = {
title: 'Changelog',
description: DEFAULT_DESCRIPTION,
ogImage: `${DEFAULT_HOST}/images/open-graph/website.png`
};
function loadMore() {
goto(`/changelog/${data.nextPage}`, { replaceState: true, noScroll: true });
}
onMount(() => {
localStorage.setItem(CHANGELOG_KEY, $page.data.changelogEntries.toString());
});
</script>
<svelte:head>
<!-- Titles -->
<title>{seo.title}</title>
<meta property="og:title" content={seo.title} />
<meta name="twitter:title" content={seo.title} />
<!-- Desscription -->
<meta name="description" content={seo.description} />
<meta property="og:description" content={seo.description} />
<meta name="twitter:description" content={seo.description} />
<!-- Image -->
<meta property="og:image" content={seo.ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:image" content={seo.ogImage} />
<meta name="twitter:card" content="summary_large_image" />
</svelte:head>
<Main>
<div class="aw-big-padding-section">
<div class="aw-big-padding-section-level-1">
<div class="aw-big-padding-section-level-2">
<div class="aw-container wrapper">
<h1 class="aw-display aw-u-color-text-primary">Changelog</h1>
<ul>
{#each data.entries as entry}
<li>
<div class="aw-dot" />
<ChangelogEntry {entry}>
<svelte:component this={entry.component} />
</ChangelogEntry>
</li>
{/each}
</ul>
{#if data.nextPage}
<button class="aw-button is-secondary" on:click={loadMore}>Load more</button>
{/if}
</div>
</div>
</div>
<div class="aw-big-padding-section-level-1 u-position-relative u-overflow-hidden">
<div class="aw-big-padding-section-level-2">
<div class="aw-container">
<PreFooter />
<FooterNav />
<MainFooter />
</div>
</div>
</div>
</div>
</Main>
<style lang="scss">
.wrapper {
--padding-inline: 1rem;
box-sizing: border-box;
max-width: calc(49.375rem + var(--padding-inline) * 2);
padding-inline: var(--padding-inline);
}
ul {
--padding-is: 2rem;
--dot-offset: 0.25rem;
display: grid;
gap: 5rem;
padding-inline-start: var(--padding-is);
border-inline-start: 2px solid transparent;
margin-block-start: 5rem;
position: relative;
&::before {
content: '';
background: linear-gradient(
to bottom,
hsl(var(--aw-color-greyscale-700)) 0%,
hsl(var(--aw-color-greyscale-700)) 95%,
transparent 100%
);
position: absolute;
inset-inline-start: -0.5px;
inset-block-start: var(--dot-offset);
width: 1px;
height: 100%;
}
li {
position: relative;
.aw-dot {
position: absolute;
inset-inline-start: calc(var(--padding-is) * -1);
translate: -50% var(--dot-offset);
}
}
}
button {
margin-block-start: 5rem;
margin-inline: auto;
min-inline-size: 10.9375rem;
}
@media screen and (max-width: 512px) {
ul {
padding-inline-start: 0;
margin-block-start: 2.5rem;
&::before {
display: none;
}
}
}
</style>

View File

@@ -0,0 +1,25 @@
import { CHANGELOG_DEPENDENCY, getAllChangelogEntries } from '../utils';
const PER_PAGE = 5;
export const entries = async () => {
const entries = await getAllChangelogEntries();
const totalPages = Math.ceil(entries.length / PER_PAGE);
return Array.from({ length: totalPages }, (_, i) => {
return {
page: (i + 1).toString()
};
});
};
export const load = async ({ depends, params }) => {
depends(CHANGELOG_DEPENDENCY);
const page = parseInt(params.page || '1', 10);
const entries = await getAllChangelogEntries();
return {
entries: entries.slice(0, page * PER_PAGE),
nextPage: page < Math.ceil(entries.length / PER_PAGE) ? page + 1 : null
};
};

View File

@@ -0,0 +1,79 @@
<script lang="ts">
import { FooterNav, MainFooter } from '$lib/components';
import PreFooter from '$lib/components/PreFooter.svelte';
import { Main } from '$lib/layouts';
import { formatDate } from '$lib/utils/date';
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
import { CHANGELOG_TITLE_SUFFIX } from '$routes/titles';
export let data;
const seo = {
title: data.title,
description: data.description ?? DEFAULT_DESCRIPTION,
ogImage: data.cover
? DEFAULT_HOST + data.cover
: `${DEFAULT_HOST}/images/open-graph/website.png`
};
</script>
<svelte:head>
<!-- Titles -->
<title>{seo.title + CHANGELOG_TITLE_SUFFIX}</title>
<meta property="og:title" content={seo.title} />
<meta name="twitter:title" content={seo.title} />
<!-- Desscription -->
<meta name="description" content={seo.description} />
<meta property="og:description" content={seo.description} />
<meta name="twitter:description" content={seo.description} />
<!-- Image -->
<meta property="og:image" content={seo.ogImage} />
<meta property="og:image:width" content="1200" />
<meta property="og:image:height" content="630" />
<meta name="twitter:image" content={seo.ogImage} />
<meta name="twitter:card" content="summary_large_image" />
</svelte:head>
<Main>
<div class="aw-big-padding-section">
<div class="aw-big-padding-section-level-1">
<div class="aw-big-padding-section-level-2">
<div class="aw-container wrapper" style="--container-size:42.5rem">
<article class="aw-main-article">
<header class="aw-main-article-header">
<a class="aw-link aw-u-color-text-secondary u-cross-baseline" href="/changelog">
<span class="aw-icon-chevron-left" aria-hidden="true" />
<span>Back to Changelog</span>
</a>
<ul class="aw-metadata aw-caption-400">
<li>
<time datetime={data.date}>{formatDate(data.date)}</time>
</li>
</ul>
<h1 class="aw-title aw-u-color-text-primary">{data.title}</h1>
</header>
{#if data.cover}
<div class="aw-media-container">
<img class="u-block" src={data.cover} alt="" />
</div>
{/if}
<div class="aw-article-content u-margin-block-start-32">
<svelte:component this={data.component} />
</div>
</article>
</div>
</div>
<div class="aw-big-padding-section-level-1 u-position-relative u-overflow-hidden">
<div class="aw-big-padding-section-level-2">
<div class="aw-container">
<PreFooter />
<FooterNav />
<MainFooter />
</div>
</div>
</div>
</div>
</div>
</Main>

View File

@@ -0,0 +1,28 @@
import { error } from '@sveltejs/kit';
import { getAllChangelogEntries } from '../../utils.js';
import type { EntryGenerator } from './$types.js';
export const entries: EntryGenerator = async () => {
const entries = await getAllChangelogEntries();
return entries.map((entry) => {
return {
entry: entry.slug
}
});
}
export const load = async ({ params, }) => {
const entries = await getAllChangelogEntries();
const entry = entries.find((entry) => {
return entry.filepath.includes(params.entry);
});
if (!entry) {
throw error(404, 'Not found')
}
return {
...entry
}
};

View File

@@ -0,0 +1,44 @@
import { browser } from '$app/environment';
import { page } from '$app/stores';
import { isNumeric } from '$lib/utils/is';
import type { ChangelogData } from '$markdoc/layouts/Changelog.svelte';
import { get } from 'svelte/store';
type ChangelogComponent = {
frontmatter: ChangelogData;
default: ConstructorOfATypedSvelteComponent;
};
export const getAllChangelogEntries = async () => {
const entriesGlob = await import.meta.glob('./**/*.markdoc', { eager: true });
let entries = Object.entries(entriesGlob).map(([filepath, component]) => {
const typedComponent = component as ChangelogComponent;
const { frontmatter } = typedComponent;
const slug = filepath.replace(/\.markdoc$/, '').replace('(entries)/', '');
const href = `/changelog/entry/${slug}`;
return { ...frontmatter, component: typedComponent.default, filepath, href, slug };
});
entries = entries.filter(({ filepath }) => {
return filepath.includes('(entries)');
});
return entries.sort((a, b) => {
const aDate = new Date(a.date);
const bDate = new Date(b.date);
return bDate.getTime() - aDate.getTime();
});
};
export const CHANGELOG_DEPENDENCY = 'changelog';
export const CHANGELOG_KEY = 'changelog';
export function hasNewChangelog() {
if (!browser) return false;
const prev = localStorage.getItem(CHANGELOG_KEY);
if (!prev) return true;
return isNumeric(prev) && JSON.parse(prev) < get(page).data.changelogEntries;
}

View File

@@ -47,6 +47,9 @@ export const load = async () => {
// fetch issues from github, appwrite/appwrite repo
const response = await fetch('https://api.github.com/repos/appwrite/appwrite/issues');
const issues = await response.json();
if (issues?.message?.includes('API rate limit exceeded')) {
return { issues: mockIssues };
}
// map issues to our format
return {

View File

@@ -23,7 +23,7 @@
src: '/images/community/events/december-1.png',
alt: ''
},
date: 'Decemeber 14th',
date: 'December 14th',
location: 'Discord',
title: 'Office hours',
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
@@ -87,14 +87,14 @@
];
const metrics = [
{ metric: '33K+', description: 'GitHub Stars' },
{ metric: '8K+', description: 'Pull Requests' },
{ metric: '15K+', description: 'Commits' },
{ metric: '2.5K+', description: 'Issues' },
{ metric: '400+', description: 'Open Issues' },
{ metric: '1.9K+', description: 'Closed Issues' },
{ metric: '4.9K+', description: 'Forks' },
{ metric: '20K+', description: 'Contributors' }
{ metric: '38K+', description: 'GitHub Stars' },
{ metric: '3K+', description: 'Pull Requests' },
{ metric: '18K+', description: 'Commits' },
{ metric: '3K+', description: 'Issues' },
{ metric: '600+', description: 'Open Issues' },
{ metric: '2.5K+', description: 'Closed Issues' },
{ metric: '3.5K+', description: 'Forks' },
{ metric: '800+', description: 'Contributors' }
];
let name = '';
@@ -154,7 +154,7 @@
<section class="aw-container aw-u-padding-block-end-0 u-position-relative">
<div class="aw-hero is-align-start aw-u-max-width-580">
<h1 class="aw-display aw-u-color-text-primary">
Built by a community of 700+ contributors
Built by a community of 800+ contributors
</h1>
<div>
<p class="aw-description">
@@ -499,7 +499,7 @@
<span
class="icon-discord aw-u-font-size-40"
aria-hidden="true"
aria-label="Discord logo"
aria-label="Discord"
/>
</div>
<div class="aw-title u-margin-block-start-auto">
@@ -519,11 +519,11 @@
<span
class="aw-icon-x aw-u-font-size-40"
aria-hidden="true"
aria-label="X logo"
aria-label="X"
/>
</div>
<div class="aw-title u-margin-block-start-auto">
125K+ followers
128K+ followers
</div>
</a>
</li>
@@ -539,11 +539,11 @@
<span
class="icon-github aw-u-font-size-40"
aria-hidden="true"
aria-label="GitHub logo"
aria-label="GitHub"
/>
</div>
<div class="aw-title u-margin-block-start-auto">
33K+ stargazers
38K+ stargazers
</div>
</a>
</li>
@@ -559,11 +559,11 @@
<span
class="icon-youtube aw-u-font-size-40"
aria-hidden="true"
aria-label="Youtube logo"
aria-label="Youtube"
/>
</div>
<div class="aw-title u-margin-block-start-auto">
3K+ subscribers
4K+ subscribers
</div>
</a>
</li>

View File

@@ -4,6 +4,7 @@
import { TITLE_SUFFIX } from '$routes/titles';
import FooterNav from '../../lib/components/FooterNav.svelte';
import MainFooter from '../../lib/components/MainFooter.svelte';
import { socials } from '$lib/constants';
let email = '';
let firstName = '';
@@ -104,61 +105,19 @@
>
<h2 class="aw-label aw-u-color-text-primary">Follow us</h2>
<ul class="u-flex u-gap-8">
{#each socials as social}
<li>
<a
href="/discord"
href={social.link}
class="aw-icon-button"
aria-label="Appwrite on aw-icon-discord"
aria-label={social.label}
target="_blank"
rel="noopener noreferrer"
>
<span class="aw-icon-discord" aria-hidden="true" />
</a>
</li>
<li>
<a
href="https://github.com/appwrite"
class="aw-icon-button"
aria-label="Appwrite on aw-icon-github"
target="_blank"
rel="noopener noreferrer"
>
<span class="aw-icon-github" aria-hidden="true" />
</a>
</li>
<li>
<a
href="https://twitter.com/appwrite"
class="aw-icon-button"
aria-label="Appwrite on aw-icon-x"
target="_blank"
rel="noopener noreferrer"
>
<span class="aw-icon-x" aria-hidden="true" />
</a>
</li>
<li>
<a
href="https://linkedin.com/company/appwrite"
class="aw-icon-button"
aria-label="Appwrite on aw-icon-linkedin"
target="_blank"
rel="noopener noreferrer"
>
<span class="aw-icon-linkedin" aria-hidden="true" />
</a>
</li>
<li>
<a
href="https://youtube.com/c/appwrite?sub_confirmation=1"
class="aw-icon-button"
aria-label="Appwrite on YouTube"
target="_blank"
rel="noopener noreferrer"
>
<span class="aw-icon-youtube" aria-hidden="true" />
<span class={social.icon} aria-hidden="true" />
</a>
</li>
{/each}
</ul>
</section>
<div

View File

@@ -26,10 +26,6 @@ Cookies are not the only way to recognize or track visitors to a website. We may
You may have the right to decide whether to accept or reject cookies. When you access our Site, you are presented with a cookie consent mechanism that allows you to accept or reject cookies that are not essential cookies. You may also control cookies by setting your browser to turn off cookies as described further below. If you turn off cookies, web beacon and other technologies will still detect your visits to our Site; however, they will not be associated with information otherwise stored in cookies.
## Targeted advertising
Third parties may drop cookies on your computer or mobile device to serve advertising through our website. These companies may use information about your visits to this and other websites to provide relevant advertisements about goods and services that you may be interested in. They may also employ technology that is used to measure the effectiveness of advertisements. The information collected through this process does not enable us or them to identify your name, contact details, or other personally identifying details unless you choose to provide these to us.
## How do we use cookies?
We use cookies to:
@@ -56,96 +52,6 @@ These cookies are essential to provide you with services available through this
* Appwrite Console
* 365
* The user will be logged in for 1 year
---
* _orig_referrer
* Shopify
* 13
* This cookie is connection with swag shopping
---
* _cmp_a
* appwrite.io
* Session
* CMPs are tools that help website owners comply with privacy regulations by obtaining user consent
---
* cookietest
* Shopify
* Session
* Checking to see if the browser is set to block or allow cookies
---
* keep_alive
* Shopify
* Session
* Ensure that a user remains authenticated and logged in while navigating different pages or performing various actions on the website / cart
---
* secure_customer_sig
* Shopify
* 365
* Customer login
---
* XSRF-TOKEN
* Homerun
* Session
* Used as a token to mitigate Cross-Site Request Forgery
---
* laravel_session
* Homerun
* Session
* Used to store session-specific data and maintain user state across different requests and pages, enabling features like user authentication and personalizing users
---
* enforce_policy
* paypal.com
* 364
* E-commerce Provision
---
* ts_c
* paypal.com
* 1094
* E-commerce Provision
---
* l7_az
* paypal.com
* Session
* E-commerce Provision
---
* tsrce
* paypal.com
* Session
* E-commerce Provision
---
* x-csrf-jwt
* paypal.com
* 6
* E-commerce Provision
---
* LANG
* paypal.com
* Session
* E-commerce Provision
---
* __cfruid
* CloudFlare
* Session
* Improvement purposes, product analysis and deployment for our status pages
---
* cf_use_ob
* CloudFlare Website Security
* Session
* Provides technical functions
---
* x-pp-s
* paypal.com
* Session
* E-commerce Provision
---
* nsid
* paypal.com
* Session
* E-commerce Provision
---
* ts
* paypal.com
* 1094
* E-commerce Provision
{% /table %}
# Performance / Analytics cookies
@@ -165,136 +71,6 @@ We use performance/analytics cookies to analyze how the website is accessed, use
* Expiry (In days)
* Purpose
---
* _ga_xxxxxxxxxx
* JavaScript / Google Analytics
* 729
* Gather information about how users interact with their websites
---
* cb_group_id
* Clearbit: B2B Marketing Intelligence
* 364
* Tracking marketing performance
---
* initial_referrer
* Homerun
* Session
* Tracking the initial referral source of a visitor to a website that is using the Homerun platform
---
* __hssrc
* HubSpot
* Session
* Website analytics
---
* _shopify_d
* Shopify
* Session
* Shopify analytics
---
* _s
* Shopify
* Session
* Shopify's analytics suite
---
* _gat
* Google Universal
* Session
* Google Universal Analytics - Throttle the request rate - limiting the collection of data on high traffic sites
---
* _shopify_s
* Shopify
* Session
* Shopify's analytics suite
---
* mp_xxxxxxx_mixpanel
* (empty)
* 364
* Helps Mixpanel Improve usability
---
* ahoy_visit
* BetterStack
* Session
* Analytics purposes
---
* __mp_opt_in_out_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
* Mixpanel
* Session
* Helps Mixpanel Improve usability
---
* _y
* Shopify
* 364
* Shopify's analytics suite
---
* cb%3Atest
* Clearbit: B2B Marketing Intelligence
* 364
* Tracks page views and traits for Clearbit
---
* __hstc
* HubSpot
* 179
* HubSpot - website analytics
---
* ph_phc_Tbfg4EiRsr5iefFoth2Y1Hi3sttTeLQ5RV5TLg4hL1W_posthog
* Betterstack
* 364
* Track user behavior across different pages and interactions, enabling us to analyze user engagement, usage patterns, and other relevant data
---
* _shopify_sa_p
* Shopify
* Session
* Shopify's analytics suite concerning marketing and referrals
---
* _ga
* Google Universal
* 729
* Distinguish unique users by assigning a randomly generated number as a client identifier, calculate visitor, session, and campaign data for the site's analytics reports
---
* _gid
* Google Universal
* Session
* Google Universal Analytics - Store and update a unique value for each page visited
---
* _shopify_sa_t
* Shopify
* Session
* Shopify's analytics suite
---
* _s
* Shopify
* Session
* Shopify's analytics suite
---
* _shopify_y
* Shopify
* 364
* Shopify's analytics suite
---
* _landing_page
* appwrite.io
* 13
* Track, report, and analyze landing pages
---
* _gclxxxx
* Google
* 89
* Google conversion tracking cookie
---
* _ga
* Google Universal
* 729
* Google Universal Analytics - distinguish unique users by assigning a randomly generated number as a client identifier, calculate visitor, session, and campaign data for the site's analytics reports
---
* __hssc
* appwrite.io/ HubSpot
* Session
* Website analytics
---
* _ph
* BetterStack
* Session
* Analytics
---
* _lr_uf_-rgthvf
* Logrocket
* Session
@@ -305,11 +81,6 @@ We use performance/analytics cookies to analyze how the website is accessed, use
* Session
* Bug tracking and debugging
---
* _lr5_
* Logrocket
* Session
* Bug tracking and debugging
---
* _lr_hb_-rgthvf%2Fappwrite
* Logrocket
* Session
@@ -326,12 +97,9 @@ We use performance/analytics cookies to analyze how the website is accessed, use
* For debugging actions on our the console
{% /table %}
# Marketing
# Functionality cookies
We use marketing cookies to deliver many types of targeted digital marketing. We do this to provide you with a better user experience and to continually improve the website. These cookies store user data and behavior information, which allows advertising services to target the audience based on various variables. For example, these cookies allow us to:
- Observe the site performance and generate retargeting (Site retargeting, search retargeting, etc).
- Maintain and improve the website and our products.
These cookies record information about choices you've made and allow us to tailor the website to you. These cookies allow us to provide you with our services in the way in which you have required, as you continue to use or come back to our Site.
{% table %}
* Cookie name
@@ -339,85 +107,15 @@ We use marketing cookies to deliver many types of targeted digital marketing. We
* Expiry (In days)
* Purpose
---
* cb_user_id
* Clearbit: B2B Marketing Intelligence
* 364
* Tracks page views and traits for Clearbit
---
* pt_
* appwrite.io
* __stripe_sid
* Stripe
* Session
* NA
* Address collection for receipts
---
* _gat_UA-XXXXXX-X
* careers.appwrite.io
* Session
* Google Analytics Cookies
---
* cb_anonymous_id
* Clearbit: B2B Marketing Intelligence
* 364
* Tracks page views and traits for Clearbit
---
* test_cookie
* doubleclick.net
* Session
* Doubleclick - Google's real-time bidding advertising exchange
---
* IDE
* doubleclick.net
* 389
* Doubleclick - Google's real-time bidding advertising exchange
---
* __cf_bm
* CloudFlare
* Session
* CloudFlare authentication
---
* _fbp
* Facebook
* 89
* Advertisement products from third-party advertisers
---
* bcookie
* LinkedIn
* 364
* Content sharing buttons
---
* UserMatchHistory
* LinkedIn
* 29
* Content sharing buttons
---
* personalization_id
* Twitter
* 729
* Collects data through a range of plug-ins and integrations, that is primarily used for tracking and targeting
---
* lidc
* LinkedIn
* Session
* Content sharing buttons
---
* li_sugr
* LinkedIn
* 89
* Content sharing buttons
---
* AnalyticsSyncHistory
* LinkedIn
* 29
* Content sharing buttons
---
* muc_ads
* Twitter
* 729
* Optimizes ads on the website
---
* ln_or
* Linkedin
* Session
* Used to determine if Oribi analytics can be carried out on a specific domain
* __stripe_mid
* Stripe
* 365
* Enable the website to process payments without storing any credit card information
{% /table %}
# How to control or delete cookies
@@ -446,7 +144,7 @@ To opt out of being tracked by Google Analytics across all websites, visit [here
When using our website, you may be directed to other websites for activities such as surveys, making payments in currencies other than U.S. dollars, or job applications. These websites may use their own cookies. We do not have control over the placement of cookies by other websites you visit, even if you are directed to them from our website.
If you use the buttons that allow you to share products and content with your friends via social networks like Facebook, Twitter, Google, YouTube, GitHub, Betterstack, Hubspot, Shopify, Homerun, Paypal, Logrocket, LinkedIn, Mixpanel, and Clearbit, these companies may set a cookie on your computer memory. Find out more about these [here](https://www.facebook.com/privacy/policy/?entry_point=data_policy_redirect&entry=0), [here](https://twitter.com/en/privacy), [here](https://policies.google.com/privacy?hl=en-GB), [here](https://docs.github.com/en/site-policy/privacy-policies/github-privacy-statement), [here](https://betterstack.com/privacy), [here](https://legal.hubspot.com/privacy-policy), [here](https://www.shopify.com/legal/privacy), [here](https://homerun.app/privacy-policy/), [here](https://www.paypal.com/us/legalhub/privacy-full), [here](https://logrocket.com/privacy/), [here](https://www.linkedin.com/legal/privacy-policy), and [here](https://mixpanel.com/legal/privacy-policy), and [here](https://dashboard.clearbit.com/privacy).
If you use the buttons that allow you to share products and content with your friends via social networks like [Logrocket](https://logrocket.com/privacy), these companies may set a cookie on your computer memory.
# Need more information?

View File

@@ -69,23 +69,7 @@
<enhanced:img src="./blur-1.png" alt="" />
</div>
<div
class="u-position-absolute aw-is-not-mobile u-only-light"
style:inline-size="768px"
style:block-size="768px"
style:inset-block-start="26rem"
style:left="calc(50% - 384px + 600px)"
>
<img
src="/images/animations/tech-light.png"
width="768"
height="768"
alt=""
style="position: absolute; display: block;"
/>
</div>
<main class="aw-main-section u-position-relative">
<main class="aw-main-section u-position-relative aw-u-overflow-hidden-break1-to-break3" id="main">
<div class="u-position-absolute aw-u-opacity-40-mobile bg-blur">
<img src="/images/bgs/docs-blur-1.svg" alt="" />
</div>
@@ -97,7 +81,7 @@
<enhanced:img src="./blur-2.png" alt="" />
</div>
<section class="aw-hero is-align-start u-position-relative">
<section class="aw-hero is-align-start u-position-relative e-hero-docs">
<h1 class="aw-display aw-u-color-text-primary u-max-width-600">
Learn how to build like a team of hundreds<span class="aw-u-color-text-accent"
>_
@@ -120,11 +104,19 @@
<a href="/docs/sdks" class="aw-button is-secondary" style:align-self="start">
<span class="aw-sub-body-500">Explore all technologies</span>
</a>
<div class="u-position-absolute aw-is-not-mobile u-only-dark spline-wrapper">
<div class="u-position-absolute aw-is-not-mobile spline-wrapper">
<Spline url="/images/animations/dark-scene.splinecode" width={660} height={660}>
<img
src="/images/animations/tech-dark.png"
class="u-only-dark"
src="/images/animations/tech-dark-transparent.png"
width="660"
height="660"
alt=""
style="position: absolute;"
/>
<img
class="u-only-light"
src="/images/animations/tech-light-transparent.png"
width="660"
height="660"
alt=""
@@ -132,7 +124,7 @@
/>
</Spline>
</div>
<div class="bg-boi" />
<div class="bg-overlay" />
</section>
<section>
<h2 class="aw-title aw-u-color-text-primary u-max-width-600">Show me some code</h2>
@@ -415,6 +407,12 @@
</Docs>
<style lang="scss">
.e-hero-docs {
@media (min-width:1280px) {
margin-block-start:5rem;
}
}
.aw-main-section {
max-inline-size: unset;
margin-inline: unset;
@@ -429,11 +427,6 @@
.tech-hero {
@include border-block-gradient;
background: linear-gradient(to right, #19191c80, #19191c00);
// background-color: #19191c80;
backdrop-filter: blur(1rem);
-webkit-backdrop-filter: blur(1rem);
--m-border-size: 1px;
--m-border-gradient-before: linear-gradient(
to right,
@@ -460,9 +453,10 @@
left: calc(50% - 384px + 600px);
}
.bg-boi {
.bg-overlay {
position: absolute;
background: linear-gradient(to right, #19191c00 0%, #19191c00 10%, #19191c);
background: linear-gradient(to right, #ffffff00 0%, #ffffff00 10%, #ffffff);
top: 0;
right: 10rem;
@@ -473,4 +467,14 @@
z-index: 9999;
}
}
:global(.theme-dark) .tech-hero {
background: linear-gradient(to right, #19191c80, #19191c00);
backdrop-filter: blur(1rem);
-webkit-backdrop-filter: blur(1rem);
}
:global(.theme-dark) .bg-overlay {
background: linear-gradient(to right, #19191c00 0%, #19191c00 10%, #19191c);
}
</style>

View File

@@ -1,13 +1,12 @@
---
layout: article
title: Migrate from Cloud
description: Migrate to Appwrite from cloud platforms seamlessly. Learn how to move your applications and data to Appwrite for enhanced flexibility and control.
description: Self-hosted application migration made easy with Appwrite. Discover the steps and strategies for migrating your self-hosted apps to Appwrite's managed platform.
difficulty: beginner
readtime: 10
---
Migrations makes it as easy as a couple clicks to move all of your self-hosted project data to a Cloud instance.
Migrations make it as easy as a couple of clicks to move all your Appwrite Cloud data into a self-hosted instance.
{% section #notices step=1 title="Things to keep in mind" %}
@@ -21,23 +20,25 @@ Migrations makes it as easy as a couple clicks to move all of your self-hosted p
{% section #create-migration step=2 title="Create migration" %}
To begin migrating to self-hosted, make sure to read the [migration overview](/docs/advanced/migrations)
and [things to keep in mind](#notices) sections above.
To begin migrating to self-hosted, make sure to read the [migration overview](/docs/advanced/migrations) and [things to keep in mind](#notices) sections above.
1. Navigate to your self-hosted project's Console and click on the **Migrations** tab.
1. Navigate to your Cloud project's console, navigate to **Settings** and click on the **Migrations** tab.
2. Click **Deploy to Cloud**, you will be redirected to Appwrite Cloud.
1. Under **Export to self-hosted instance**, click **Export data**.
3. You will complete the migration on Appwrite Cloud.
1. You will complete the migration on your self-hosted instance.
{% /section %}
{% section #continue-on-cloud step=3 title="Continue on Appwrite Cloud" %}
{% section #continue-on-self-hosted step=3 title="Continue on self-hosted" %}
1. Once redirected to Appwrite Cloud, you'll be prompted to select an organization and a project. You can migrate to an existing project or create a new one.
1. Once redirected to your self-hosted project, you'll be prompted to select an organization and a project. You can migrate to an existing project or create a new one.
2. Select the data you wish to migrate. You can choose among accounts, databases, documents, files, and functions.
1. Select the data you wish to migrate. You can choose among accounts, databases, documents, files, and functions.
3. Click **Start migration** to start the migration process. You do not need to keep the Appwrite Console open through the process.
1. Click **Start Migration** to start the migration process. You do not need to keep the Appwrite Console open through the process.
{% info title="Keep in mind" %}
Your self-hosted instance will generate an API Key in the background to pass to Appwrite Cloud. You can revoke this key after the migration process is complete.
{% /info %}
{% /section %}

View File

@@ -1,12 +1,11 @@
---
layout: article
title: Migrate from self-hosted
description: Self-hosted application migration made easy with Appwrite. Discover the steps and strategies for migrating your self-hosted apps to Appwrite's managed platform.
description: Migrate to Appwrite from self-hosted platforms seamlessly. Learn how to move your applications and data to Appwrite for enhanced flexibility and control.
difficulty: beginner
readtime: 20
---
Migrations make it as easy as a couple of clicks to move all your Appwrite Cloud data into a self-hosted instance.
Migrations makes it as easy as a couple clicks to move all of your self-hosted project data to a Cloud instance.
{% section #notices step=1 title="Things to keep in mind" %}
@@ -21,25 +20,23 @@ Migrations make it as easy as a couple of clicks to move all your Appwrite Cloud
{% section #create-migration step=2 title="Create migration" %}
To begin migrating to self-hosted, make sure to read the [migration overview](/docs/advanced/migrations) and [things to keep in mind](#notices) sections above.
To begin migrating to Cloud, make sure to read the [migration overview](/docs/advanced/migrations)
and [things to keep in mind](#notices) sections above.
1. Navigate to your self-hosted project's console and click on the **Migrations** tab.
1. Navigate to your self-hosted project's Console and click on the **Migrations** tab.
1. Click **Deploy to Cloud**, you will be redirected to Appwrite Cloud.
2. Click **Deploy to Cloud**, you will be redirected to Appwrite Cloud.
1. You will complete the migration on Appwrite Cloud.
3. You will complete the migration on Appwrite Cloud.
{% /section %}
{% section #continue-on-self-hosted step=3 title="Continue on self-hosted" %}
{% section #continue-on-cloud step=3 title="Continue on Appwrite Cloud" %}
1. Once redirected to your self-hosted project, you'll be prompted to select an organization and a project. You can migrate to an existing project or create a new one.
1. Once redirected to Appwrite Cloud, you'll be prompted to select an organization and a project. You can migrate to an existing project or create a new one.
1. Select the data you wish to migrate. You can choose among accounts, databases, documents, files, and functions.
2. Select the data you wish to migrate. You can choose among accounts, databases, documents, files, and functions.
1. Click **Start Migration** to start the migration process. You do not need to keep the Appwrite Console open through the process.
3. Click **Start migration** to start the migration process. You do not need to keep the Appwrite Console open through the process.
{% info title="Keep in mind" %}
Your self-hosted instance will generate an API Key in the background to pass to Appwrite Cloud. You can revoke this key after the migration process is complete.
{% /info %}
{% /section %}

View File

@@ -51,6 +51,35 @@
},
]
},
{
label: 'Plans',
items: [
{
label: 'Billing',
href: '/docs/advanced/platform/billing'
},
{
label: 'Starter',
href: '/docs/advanced/platform/starter'
},
{
label: 'Pro',
href: '/docs/advanced/platform/pro'
},
{
label: 'Scale',
href: '/docs/advanced/platform/scale'
},
{
label: 'Enterprise',
href: '/docs/advanced/platform/enterprise'
},
{
label: 'Open source',
href: '/docs/advanced/platform/oss'
},
]
},
{
label: 'Configuration',
items: [

View File

@@ -0,0 +1,46 @@
---
layout: article
title: Billing
description: Understand Appwrite's billing features, like budget caps, billing periods, taxes, and more.
---
Appwrite allows you to configure billing per organization.
You can access your organizations billing information under the **Billing** tab of your organization.
# Plans {% #plans %}
You can view or change your organization's plan under the **Billing** section.
You'll also find the expected cost, as well as the start and end date of the current billing period.
## Billing period {% #billing-period %}
Billing periods begin the day you change your plan, and lasts 30 days.
You resource limits are reset at the beginning of each billing period and you will be billed for your plan and usage on the last day of each billing period.
# Payment history {% #payment-history %}
You can view and download you past invoices under **Payment history**.
You can click the three-dots menu to view and download your invoices.
# Payment methods {% #payment-methods %}
Appwrite Cloud accepts credit and debit cards as payment methods and will bill the card at the end of each billing cycle.
Appwrite accepts Visa, Mastercard, American Express, Discover & Diners Club, China UnionPay, Japan Credit Bureau (JCB), Cartes Bancairies, and eftpos Australia.
# Billing addresss {% #billing-addresss %}
Your billing address will be displayed on your invoices and used when Appwrite Cloud bills your payment method at the end of a billing cycle.
# Tax ID {% #tax-id %}
If you'd like you or your company's tax ID displayed on your generated invoice, you can provide it under **Tax ID**.
# Budget cap {% #budget-cap %}
Appwrite allows you to set budget caps when on your organizations.
Appwrite will automatically scale your projects by purchasing add-ons as they require more resources.
Budget caps limit the amount of automatic scaling and prevent unexpected bills.
You can enable budget caps under **Budget cap**, toggle the option **Enable budget cap**.
You will be able to set a budget cap in USD.
## Budget alerts {% #budget-alerts %}
When you enable budget cap, you'll be able to configure alerts under **Budget alerts** to warn you when you organization is near the budget cap.
By default, an email alert will be sent at 75% of the budget cap. You can add up to three check points that send budget alerts when reached.
# Redeem credit {% #redeem-credit %}
If you received a redeemable code for Appwrite Cloud credits, you can redeem them in the **Available credit** section.
When you redeem credit, the credit balance will be automatically applied to the next billing cycle.

View File

@@ -0,0 +1,10 @@
---
layout: article
title: Enterprise
description: How Appwrite can accelerate enterprise development teams and provide custom support and hosting options.
---
Enterprise development teams face unique challenges and have unique needs.
Appwrite can provide tailored solutions for enterprise customers with custom hosting, training, and support needs.
If you're interested to learn about what Appwrite can do for your enterprise development teams, [contact us](https://appwrite.io/contact-us) for more details.

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