Merge branch 'main' into feat-refine-tutorial
32
package.json
@@ -19,35 +19,35 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@melt-ui/pp": "^0.1.2",
|
||||
"@melt-ui/svelte": "^0.55.0",
|
||||
"@playwright/test": "^1.37.1",
|
||||
"@melt-ui/svelte": "^0.60.0",
|
||||
"@playwright/test": "^1.39.0",
|
||||
"@sveltejs/adapter-node": "^1.3.1",
|
||||
"@sveltejs/adapter-static": "^2.0.3",
|
||||
"@sveltejs/kit": "^1.24.1",
|
||||
"@types/compression": "^1.7.3",
|
||||
"@sveltejs/kit": "^1.27.1",
|
||||
"@types/compression": "^1.7.4",
|
||||
"@types/glob": "^8.1.0",
|
||||
"@types/markdown-it": "^13.0.1",
|
||||
"@types/morgan": "^1.9.6",
|
||||
"@types/markdown-it": "^13.0.5",
|
||||
"@types/morgan": "^1.9.7",
|
||||
"@typescript-eslint/eslint-plugin": "^5.62.0",
|
||||
"@typescript-eslint/parser": "^5.62.0",
|
||||
"eslint": "^8.49.0",
|
||||
"eslint": "^8.52.0",
|
||||
"eslint-config-prettier": "^8.10.0",
|
||||
"eslint-plugin-svelte": "^2.33.1",
|
||||
"eslint-plugin-svelte": "^2.34.0",
|
||||
"glob": "^10.3.10",
|
||||
"openapi-types": "^12.1.3",
|
||||
"oslllo-svg-fixer": "^3.0.0",
|
||||
"prettier": "^2.8.8",
|
||||
"prettier-plugin-svelte": "^2.10.1",
|
||||
"sass": "^1.66.1",
|
||||
"svelte": "^4.2.0",
|
||||
"svelte-check": "^3.5.1",
|
||||
"svelte-markdoc-preprocess": "^1.1.0",
|
||||
"sass": "^1.69.5",
|
||||
"svelte": "^4.2.2",
|
||||
"svelte-check": "^3.5.2",
|
||||
"svelte-markdoc-preprocess": "^1.1.2",
|
||||
"svelte-sequential-preprocessor": "^2.0.1",
|
||||
"svgo": "^3.0.2",
|
||||
"svgtofont": "^4.0.0",
|
||||
"tslib": "^2.6.2",
|
||||
"typescript": "^5.2.2",
|
||||
"vite": "^4.4.9",
|
||||
"vite": "^4.5.0",
|
||||
"vite-plugin-dynamic-import": "^1.5.0",
|
||||
"vite-plugin-image-optimizer": "^1.1.7",
|
||||
"vitest": "^0.32.4"
|
||||
@@ -60,10 +60,10 @@
|
||||
"@splinetool/viewer": "0.9.455",
|
||||
"compression": "^1.7.4",
|
||||
"express": "^4.18.2",
|
||||
"highlight.js": "^11.8.0",
|
||||
"markdown-it": "^13.0.1",
|
||||
"highlight.js": "^11.9.0",
|
||||
"markdown-it": "^13.0.2",
|
||||
"meilisearch": "^0.35.0",
|
||||
"motion": "^10.16.2",
|
||||
"motion": "^10.16.4",
|
||||
"sharp": "^0.32.6"
|
||||
}
|
||||
}
|
||||
|
||||
486
pnpm-lock.yaml
generated
11
src/app.html
@@ -19,6 +19,7 @@
|
||||
|
||||
<body class="theme-dark" data-sveltekit-preload-data="hover">
|
||||
<script>
|
||||
// Theme
|
||||
const isDocs = window.location.pathname.startsWith('/docs');
|
||||
if (isDocs) {
|
||||
const theme = localStorage.getItem('theme');
|
||||
@@ -35,6 +36,16 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Progressive enhancement
|
||||
document.body.dataset.jsEnabled = '';
|
||||
|
||||
// Banner
|
||||
const BANNER_KEY = '%aw_banner_key%';
|
||||
const hideBanner = localStorage.getItem(BANNER_KEY);
|
||||
if (hideBanner === 'true') {
|
||||
document.body.dataset.bannerHidden = '';
|
||||
}
|
||||
</script>
|
||||
<div style="display: contents">%sveltekit.body%</div>
|
||||
</body>
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import type { Handle } from '@sveltejs/kit';
|
||||
import redirects from './redirects.json';
|
||||
import { sequence } from '@sveltejs/kit/hooks';
|
||||
import { BANNER_KEY } from '$lib/constants';
|
||||
|
||||
const redirectMap = new Map(redirects.map(({ link, redirect }) => [link, redirect]));
|
||||
|
||||
export const handle: Handle = async ({ event, resolve }) => {
|
||||
const redirecter: Handle = async ({ event, resolve }) => {
|
||||
const currentPath = event.url.pathname;
|
||||
if (redirectMap.has(currentPath)) {
|
||||
return new Response(null, {
|
||||
@@ -16,3 +18,12 @@ export const handle: Handle = async ({ event, resolve }) => {
|
||||
|
||||
return await resolve(event);
|
||||
};
|
||||
|
||||
const bannerRewriter: Handle = async ({ event, resolve }) => {
|
||||
const response = await resolve(event, {
|
||||
transformPageChunk: ({ html }) => html.replace('%aw_banner_key%', BANNER_KEY)
|
||||
});
|
||||
return response;
|
||||
};
|
||||
|
||||
export const handle = sequence(redirecter, bannerRewriter);
|
||||
|
||||
|
Before Width: | Height: | Size: 509 B After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 128 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 54 KiB |
@@ -1,4 +1,4 @@
|
||||
<svg width="25" height="22" viewBox="0 0 25 22" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M0 0.333008L12.4444 21.6663L24.8889 0.333008H20L12.4444 13.2219L4.88889 0.333008H0Z" fill="#ADADB0"/>
|
||||
<path d="M4.88867 0.333008L12.4442 13.2219L19.9998 0.333008H15.5553L12.5022 5.67756L9.33312 0.333008H4.88867Z" fill="#6C6C71"/>
|
||||
<svg width="25" height="24" viewBox="0 0 25 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M21.4628 4.5L13 19.0077L4.53714 4.5H7.04682L12.5686 13.9195L13 14.6554L13.4313 13.9195L18.9531 4.5H21.4628Z" stroke="#6C6C71"/>
|
||||
<path d="M7.33325 4L12.9999 13.6667L18.6666 4H15.3333L13.0434 8.00841L10.6666 4H7.33325Z" fill="#6C6C71"/>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 342 B After Width: | Height: | Size: 347 B |
@@ -27,7 +27,7 @@
|
||||
desktop: {
|
||||
main: animation(
|
||||
'#oss-discord',
|
||||
{ x: 20, y: '-80vh', rotate: 15 },
|
||||
{ x: 20, y: '-75vh', rotate: 15 },
|
||||
animationOptions
|
||||
),
|
||||
reversed: animation(
|
||||
@@ -45,7 +45,7 @@
|
||||
desktop: {
|
||||
main: animation(
|
||||
'#oss-github',
|
||||
{ x: -100, y: '-55vh', rotate: 6.26 },
|
||||
{ x: -100, y: '-50vh', rotate: 6.26 },
|
||||
animationOptions
|
||||
),
|
||||
reversed: animation(
|
||||
@@ -67,7 +67,7 @@
|
||||
desktop: {
|
||||
main: animation(
|
||||
'#oss-twitter',
|
||||
{ x: 100, y: '-70vh', rotate: -15 },
|
||||
{ x: 100, y: '-65vh', rotate: -15 },
|
||||
animationOptions
|
||||
),
|
||||
reversed: animation(
|
||||
@@ -85,7 +85,7 @@
|
||||
desktop: {
|
||||
main: animation(
|
||||
'#oss-youtube',
|
||||
{ x: -100, y: '-55vh', rotate: -3.77 },
|
||||
{ x: -100, y: '-50vh', rotate: -3.77 },
|
||||
animationOptions
|
||||
),
|
||||
reversed: animation(
|
||||
@@ -103,7 +103,7 @@
|
||||
desktop: {
|
||||
main: animation(
|
||||
'#oss-commits',
|
||||
{ x: 100, y: '-80vh', rotate: -10.2 },
|
||||
{ x: 100, y: '-70vh', rotate: -10.2 },
|
||||
animationOptions
|
||||
),
|
||||
reversed: animation(
|
||||
@@ -157,6 +157,8 @@
|
||||
<div class="cards-wrapper">
|
||||
<a
|
||||
href="/discord"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-card is-white aw-u-min-block-size-320 u-flex-vertical oss-card"
|
||||
id="oss-discord"
|
||||
>
|
||||
@@ -245,7 +247,7 @@
|
||||
|
||||
.sticky-wrapper {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
top: -15vh;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
@@ -256,7 +258,7 @@
|
||||
padding-inline: 1.25rem;
|
||||
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
height: 130vh;
|
||||
|
||||
text-align: center;
|
||||
|
||||
|
||||
15
src/lib/components/MetricCard.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
export let metric: string;
|
||||
export let description: string;
|
||||
</script>
|
||||
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">{metric}</div>
|
||||
<div class="aw-description">{description}</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.aw-card {
|
||||
padding: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
@@ -39,11 +39,11 @@
|
||||
href="https://github.com/appwrite/appwrite/stargazers"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-button is-text u-width-full-line"
|
||||
class="aw-button is-text aw-u-inline-width-100-percent-mobile"
|
||||
>
|
||||
<span class="aw-icon-star" aria-hidden="true" />
|
||||
<span class="text">Star on GitHub</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">36.8K</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">37.9K</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -91,7 +91,7 @@
|
||||
{/if}
|
||||
<span>{$selectedLabel}</span>
|
||||
</div>
|
||||
<span class="icon-cheveron-down" aria-hidden="true" />
|
||||
<span class="icon-cheveron-{$open ? 'up' : 'down'}" aria-hidden="true" />
|
||||
</button>
|
||||
|
||||
{#if $open}
|
||||
@@ -136,7 +136,7 @@
|
||||
{/if}
|
||||
|
||||
<div
|
||||
class="aw-select is-colored aw-is-only-mobile"
|
||||
class="aw-select is-colored aw-is-only-mobile aw-u-inline-width-100-percent-mobile-break1"
|
||||
style:display={nativeMobile ? undefined : 'none'}
|
||||
>
|
||||
{#if selectedOption?.icon}
|
||||
@@ -162,7 +162,7 @@
|
||||
{/if}
|
||||
{/each}
|
||||
</select>
|
||||
<span class="icon-cheveron-down" aria-hidden="true" />
|
||||
<span class="icon-cheveron-{$open ? 'up' : 'down'}" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import type { SplineViewer } from '@splinetool/viewer';
|
||||
import { onMount } from 'svelte';
|
||||
import { writable } from 'svelte/store';
|
||||
import type { SplineViewer } from '@splinetool/viewer';
|
||||
import { fade } from 'svelte/transition';
|
||||
import { dev } from '$app/environment';
|
||||
|
||||
export let url: SplineViewer['url'];
|
||||
export let width: SplineViewer['width'] = undefined;
|
||||
@@ -16,7 +15,15 @@
|
||||
onMount(async () => {
|
||||
await import('@splinetool/viewer');
|
||||
const onLoad = () => {
|
||||
spline.shadowRoot?.querySelector('#logo')?.remove(); // Remove Spline logo
|
||||
const shadowRoot = spline.shadowRoot;
|
||||
if (shadowRoot) {
|
||||
shadowRoot.querySelector('#logo')?.remove(); // Remove Spline logo
|
||||
const canvas = shadowRoot.getElementById('spline');
|
||||
if (canvas) {
|
||||
canvas.style.width = '100%';
|
||||
canvas.style.height = '100%';
|
||||
}
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
loaded.set(true);
|
||||
@@ -25,9 +32,11 @@
|
||||
|
||||
spline.addEventListener('load-complete', onLoad);
|
||||
});
|
||||
|
||||
const ENABLED = false;
|
||||
</script>
|
||||
|
||||
{#if !dev}
|
||||
{#if ENABLED}
|
||||
<spline-viewer
|
||||
style="position: absolute;"
|
||||
{url}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import { currentTheme, themeInUse } from '$routes/+layout.svelte';
|
||||
import { themeInUse } from '$routes/+layout.svelte';
|
||||
|
||||
$: platforms = [
|
||||
{
|
||||
|
||||
@@ -12,3 +12,4 @@ export { default as FloatingHeads } from './FloatingHeads.svelte';
|
||||
export { default as FloatingHead } from './FloatingHead.svelte';
|
||||
export { default as Feedback } from './Feedback.svelte';
|
||||
export { default as Select } from './Select.svelte';
|
||||
export { default as MetricCard } from './MetricCard.svelte';
|
||||
|
||||
1
src/lib/constants.ts
Normal file
@@ -0,0 +1 @@
|
||||
export const BANNER_KEY = 'discord-banner-01'; // Change key to force banner to show again
|
||||
@@ -81,7 +81,7 @@
|
||||
</a>
|
||||
</div>
|
||||
<div class="aw-mobile-header-end">
|
||||
<a href="https://cloud.appwrite.io/console" class="aw-button">
|
||||
<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">
|
||||
@@ -147,7 +147,7 @@
|
||||
>
|
||||
<span class="aw-icon-star" aria-hidden="true" />
|
||||
<span class="text">Star on GitHub</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">36.8K</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">37.9K</span>
|
||||
</a>
|
||||
<a href="https://cloud.appwrite.io/console" class="aw-button">
|
||||
<span class="aw-sub-body-500">Go to console</span>
|
||||
|
||||
@@ -113,7 +113,7 @@
|
||||
use:scrollToTop
|
||||
>
|
||||
<span class="aw-icon-arrow-up" aria-hidden="true" />
|
||||
<span class="aw-sub-body-500">Back to top</span>
|
||||
<span class="aw-caption-400">Back to top</span>
|
||||
</button>
|
||||
</div>
|
||||
{/if}
|
||||
@@ -129,7 +129,7 @@
|
||||
position: absolute;
|
||||
content: '';
|
||||
top: 0;
|
||||
left: -1.5rem;
|
||||
left: -1.8rem;
|
||||
height: 100%;
|
||||
width: 2px;
|
||||
background-color: hsl(var(--p-references-menu-link-color-text));
|
||||
|
||||
@@ -147,7 +147,7 @@
|
||||
<div class="u-sep-block-start u-padding-block-start-20">
|
||||
<button class="aw-link u-inline-flex u-cross-center u-gap-8" use:scrollToTop>
|
||||
<span class="aw-icon-arrow-up" aria-hidden="true" />
|
||||
<span class="aw-sub-body-500">Back to top</span>
|
||||
<span class="aw-caption-400">Back to top</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -12,12 +12,11 @@
|
||||
<script lang="ts">
|
||||
import { browser } from '$app/environment';
|
||||
import { MobileNav } from '$lib/components';
|
||||
import { BANNER_KEY } from '$lib/constants';
|
||||
import { isVisible } from '$lib/utils/isVisible';
|
||||
import { createScrollInfo } from '$lib/utils/scroll';
|
||||
import { addEventListener } from '@melt-ui/svelte/internal/helpers';
|
||||
import { onMount } from 'svelte';
|
||||
import { slide } from 'svelte/transition';
|
||||
import { persisted } from '$lib/utils/persisted';
|
||||
|
||||
let theme: 'light' | 'dark' | null = 'dark';
|
||||
|
||||
@@ -112,11 +111,10 @@
|
||||
return $scrollInfo.deltaDirChange < 200;
|
||||
})();
|
||||
|
||||
const BANNER_KEY = 'discord-banner-00'; // Change this key whenever you want to show the banner again for all users
|
||||
let showTopBanner = persisted(BANNER_KEY, {
|
||||
defaultValue: true,
|
||||
validate: (value): value is boolean => typeof value === 'boolean'
|
||||
});
|
||||
const hideTopBanner = () => {
|
||||
document.body.dataset.bannerHidden = '';
|
||||
localStorage.setItem(BANNER_KEY, 'true');
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="u-position-relative">
|
||||
@@ -163,14 +161,12 @@
|
||||
</div>
|
||||
</section>
|
||||
<header
|
||||
class="aw-main-header is-special-padding theme-{resolvedTheme}"
|
||||
class:is-transparent={browser}
|
||||
class="aw-main-header is-special-padding theme-{resolvedTheme} is-transparent"
|
||||
class:is-hidden={$isHeaderHidden}
|
||||
>
|
||||
{#if $showTopBanner}
|
||||
<div class="aw-top-banner" transition:slide={{ duration: 250 }}>
|
||||
<div class="aw-top-banner">
|
||||
<div class="aw-top-banner-content aw-u-color-text-primary">
|
||||
<a href="https://appwrite.io/discord" target="_blank" rel="noopener noreferrer">
|
||||
<a href="/discord" target="_blank" rel="noopener noreferrer">
|
||||
<span class="aw-caption-500">We are having lots of fun on</span>
|
||||
<span class="aw-icon-discord" aria-hidden="true" />
|
||||
<span class="aw-caption-500">Discord. Come and join us!</span>
|
||||
@@ -179,14 +175,14 @@
|
||||
<button
|
||||
class="aw-top-banner-button"
|
||||
aria-label="close discord message"
|
||||
on:click={() => ($showTopBanner = false)}
|
||||
on:click={hideTopBanner}
|
||||
>
|
||||
<span class="aw-icon-close" aria-hidden="true" />
|
||||
</button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<div class="aw-main-header-wrapper">
|
||||
<div class="aw-main-header-start">
|
||||
<a href="/">
|
||||
@@ -224,7 +220,7 @@
|
||||
>
|
||||
<span aria-hidden="true" class="aw-icon-star" />
|
||||
<span class="text">Star on GitHub</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">36.8K</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">37.9K</span>
|
||||
</a>
|
||||
<a href="https://cloud.appwrite.io/register" class="aw-button is-secondary"
|
||||
>Sign up</a
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
{/if}
|
||||
{:else}
|
||||
{#if navGroup.label}
|
||||
<h4 class="aw-side-nav-header aw-eyebrow u-un-break-text">{navGroup.label}</h4>
|
||||
<h2 class="aw-side-nav-header aw-eyebrow u-un-break-text">{navGroup.label}</h2>
|
||||
{/if}
|
||||
<ul>
|
||||
{#each navGroup.items as groupItem}
|
||||
@@ -95,7 +95,7 @@
|
||||
</button>
|
||||
{/if}
|
||||
<div class="aw-side-nav-mobile-footer-buttons">
|
||||
<button class="aw-button u-width-full-line">
|
||||
<button class="aw-button aw-u-inline-width-100-percent-mobile">
|
||||
<span class="text">Go to console</span>
|
||||
</button>
|
||||
|
||||
@@ -103,11 +103,11 @@
|
||||
href="https://github.com/appwrite/appwrite/stargazers"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-button is-text u-width-full-line"
|
||||
class="aw-button is-text aw-u-inline-width-100-percent-mobile"
|
||||
>
|
||||
<span class="aw-icon-star" aria-hidden="true" />
|
||||
<span class="text">Star on GitHub</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">36.8K</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">37.9K</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -28,6 +28,7 @@ import { Platform } from './references';
|
||||
|
||||
const languages = {
|
||||
js: javascript,
|
||||
javascript: javascript,
|
||||
dart: dart,
|
||||
ts: typescript,
|
||||
typescript: typescript,
|
||||
@@ -56,7 +57,8 @@ const languages = {
|
||||
py: python,
|
||||
rb: ruby,
|
||||
cs: csharp,
|
||||
css: css
|
||||
css: css,
|
||||
svelte: xml
|
||||
} as const satisfies Record<string, LanguageFn>;
|
||||
|
||||
const platformAliases: Record<string, keyof typeof languages> = {
|
||||
|
||||
7
src/lib/utils/date.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
export const formatDate = (date: string): string => {
|
||||
const dt = new Date(date);
|
||||
const month = dt.toLocaleString('default', { month: 'short' });
|
||||
const day = dt.getDate();
|
||||
const year = dt.getFullYear();
|
||||
return `${month} ${day}, ${year}`;
|
||||
};
|
||||
@@ -1,24 +1,10 @@
|
||||
<script context="module" lang="ts">
|
||||
export type AuthorData = {
|
||||
name: string;
|
||||
slug: string;
|
||||
role: string;
|
||||
avatar: string;
|
||||
bio: string;
|
||||
twitter: string;
|
||||
linkedin: string;
|
||||
github: string;
|
||||
href: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Article, FooterNav, MainFooter } from '$lib/components';
|
||||
import { page } from '$app/stores';
|
||||
import { Main } from '$lib/layouts';
|
||||
import { getContext } from 'svelte';
|
||||
import type { PostsData } from './Post.svelte';
|
||||
import { BLOG_TITLE_SUFFIX } from '$routes/titles';
|
||||
import type { PostsData, AuthorData } from '$routes/blog/content';
|
||||
import { DEFAULT_HOST } from '$lib/utils/metadata';
|
||||
import FloatingHead from '$lib/components/FloatingHead.svelte';
|
||||
|
||||
|
||||
@@ -1,17 +1,8 @@
|
||||
<script context="module" lang="ts">
|
||||
export type CategoryData = {
|
||||
name: string;
|
||||
description: string;
|
||||
href: string;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Article, FooterNav, MainFooter } from '$lib/components';
|
||||
import { Main } from '$lib/layouts';
|
||||
import { getContext } from 'svelte';
|
||||
import type { PostsData } from './Post.svelte';
|
||||
import type { AuthorData } from './Author.svelte';
|
||||
import type { PostsData, AuthorData } from '$routes/blog/content';
|
||||
import { BLOG_TITLE_SUFFIX } from '$routes/titles';
|
||||
import { DEFAULT_HOST } from '$lib/utils/metadata';
|
||||
|
||||
|
||||
225
src/markdoc/layouts/Policy.svelte
Normal file
@@ -0,0 +1,225 @@
|
||||
<script context="module" lang="ts">
|
||||
const POLICY_KEY = Symbol();
|
||||
export type PolicyContext = {
|
||||
toc: TableOfContents;
|
||||
transparentTableCells?: boolean;
|
||||
};
|
||||
|
||||
const setCtx = (ctx: PolicyContext) => {
|
||||
setContext<PolicyContext>(POLICY_KEY, ctx);
|
||||
};
|
||||
|
||||
export const getPolicyCtx = () => {
|
||||
return getContext<PolicyContext>(POLICY_KEY);
|
||||
};
|
||||
|
||||
export const isInsidePolicy = () => {
|
||||
return hasContext(POLICY_KEY);
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { FooterNav, MainFooter } from '$lib/components';
|
||||
|
||||
import { Main } from '$lib/layouts';
|
||||
|
||||
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 { isHeaderHidden } from '$lib/layouts/Main.svelte';
|
||||
|
||||
export let title: string;
|
||||
export let transparentTableCells = false;
|
||||
|
||||
const seo = {
|
||||
title: title + TITLE_SUFFIX,
|
||||
description: DEFAULT_DESCRIPTION,
|
||||
ogImage: DEFAULT_HOST + '/images/open-graph/website.png',
|
||||
|
||||
APP_NAME: 'Appwrite'
|
||||
};
|
||||
|
||||
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);
|
||||
</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-container">
|
||||
<div class="aw-grid-120-1fr-auto">
|
||||
<header class="aw-grid-120-1fr-auto-header">
|
||||
<h1 class="aw-title aw-u-color-text-primary">{title}</h1>
|
||||
</header>
|
||||
<button
|
||||
class="toc-btn u-position-sticky u-flex u-width-full-line u-main-space-between u-cross-center
|
||||
aw-u-padding-20 aw-u-margin-inline-20-negative aw-u-color-text-primary aw-is-only-mobile
|
||||
u-margin-block-start-24 aw-u-sep-block aw-u-filter-blur-8"
|
||||
style:--inset-block-start="4.5rem"
|
||||
style:inline-size="100vw"
|
||||
style:background-color="hsl(var(--p-body-bg-color) / 0.1)"
|
||||
style:translate="0 {$isHeaderHidden ? '-4.5rem' : '0'}"
|
||||
on:click={() => (showToc = !showToc)}
|
||||
>
|
||||
<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">
|
||||
<div class="aw-content is-count-headers" class:aw-is-mobile-closed={showToc}>
|
||||
<!-- eslint-disable-next-line svelte/valid-compile -->
|
||||
<h2 aria-hidden="true">Introduction</h2>
|
||||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
<FooterNav />
|
||||
<MainFooter />
|
||||
</div>
|
||||
</Main>
|
||||
|
||||
<style lang="scss">
|
||||
h2[aria-hidden='true'] {
|
||||
height: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.toc-btn {
|
||||
transition: translate 0.3s ease;
|
||||
}
|
||||
|
||||
@media screen and (max-width: 1023px) {
|
||||
.aw-page-steps {
|
||||
margin-block-start: 2rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
40
src/markdoc/layouts/PolicyTree.svelte
Normal file
@@ -0,0 +1,40 @@
|
||||
<script lang="ts">
|
||||
import { type TableOfContentsItem, type TableOfContentsElements, melt } from '@melt-ui/svelte';
|
||||
import { getPolicyCtx } from './Policy.svelte';
|
||||
|
||||
export let tree: TableOfContentsItem[] = [];
|
||||
export let activeHeadingIdxs: number[];
|
||||
export let item: TableOfContentsElements['item'];
|
||||
export let level = 1;
|
||||
|
||||
const {
|
||||
toc: {
|
||||
helpers: { isActive }
|
||||
}
|
||||
} = getPolicyCtx();
|
||||
</script>
|
||||
|
||||
<ul class="aw-page-steps-list aw-sub-body-500">
|
||||
{#if tree && tree.length}
|
||||
{#each tree as heading, i (i)}
|
||||
<li>
|
||||
<a
|
||||
class:is-selected={$isActive(heading.id)}
|
||||
href="#{heading.id}"
|
||||
use:melt={$item(heading.id)}
|
||||
>
|
||||
<!-- eslint-disable-next-line svelte/no-at-html-tags -->
|
||||
{@html heading.node.innerHTML}
|
||||
</a>
|
||||
{#if heading.children && heading.children.length}
|
||||
<svelte:self
|
||||
tree={heading.children}
|
||||
level={level + 1}
|
||||
{activeHeadingIdxs}
|
||||
{item}
|
||||
/>
|
||||
{/if}
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
@@ -1,25 +1,12 @@
|
||||
<script context="module" lang="ts">
|
||||
export type PostsData = {
|
||||
title: string;
|
||||
description: string;
|
||||
date: Date;
|
||||
cover: string;
|
||||
timeToRead: number;
|
||||
author: string;
|
||||
category: string;
|
||||
href: string;
|
||||
featured?: boolean;
|
||||
};
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { Article, FooterNav, MainFooter, Newsletter } from '$lib/components';
|
||||
import { Main } from '$lib/layouts';
|
||||
import { formatDate } from '$lib/utils/date';
|
||||
import { getContext } from 'svelte';
|
||||
import type { AuthorData } from './Author.svelte';
|
||||
import type { CategoryData } from './Category.svelte';
|
||||
import { BLOG_TITLE_SUFFIX } from '$routes/titles';
|
||||
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';
|
||||
|
||||
export let title: string;
|
||||
export let description: string;
|
||||
@@ -42,6 +29,8 @@
|
||||
cats.some((cat) => cat.toLocaleLowerCase() === c.name.toLocaleLowerCase())
|
||||
);
|
||||
}
|
||||
|
||||
let readPercentage = 0;
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -62,7 +51,13 @@
|
||||
</svelte:head>
|
||||
|
||||
<Main>
|
||||
<div class="aw-big-padding-section">
|
||||
<div
|
||||
class="aw-big-padding-section"
|
||||
use:scroll
|
||||
on:aw-scroll={(e) => {
|
||||
readPercentage = e.detail.percentage;
|
||||
}}
|
||||
>
|
||||
<div class="aw-big-padding-section">
|
||||
<div class="aw-big-padding-section-level-1">
|
||||
<div class="aw-big-padding-section-level-2">
|
||||
@@ -75,9 +70,7 @@
|
||||
</a>
|
||||
<ul class="aw-metadata aw-caption-400">
|
||||
<li>
|
||||
<time datetime={date}
|
||||
>{new Date(date).toLocaleDateString()}</time
|
||||
>
|
||||
<time datetime={date}>{formatDate(date)}</time>
|
||||
</li>
|
||||
{#if timeToRead}
|
||||
<li>{timeToRead} min</li>
|
||||
@@ -214,3 +207,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</Main>
|
||||
|
||||
<div class="progress-bar" style:--percentage="{readPercentage * 100}%" />
|
||||
|
||||
<style lang="scss">
|
||||
.progress-bar {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
height: 2px;
|
||||
width: var(--percentage);
|
||||
background: hsl(var(--aw-color-accent));
|
||||
z-index: 10000;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script context="module" lang="ts">
|
||||
export type Tutorial = {
|
||||
title: string;
|
||||
framework?: string;
|
||||
step: number;
|
||||
href: string;
|
||||
draft?: boolean;
|
||||
};
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,6 +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';
|
||||
|
||||
export let level: number;
|
||||
export let id: string | undefined = undefined;
|
||||
@@ -46,6 +47,9 @@
|
||||
|
||||
observer.observe(element);
|
||||
});
|
||||
|
||||
const inPolicy = isInsidePolicy();
|
||||
$: headingClass = inPolicy && level === 1 ? 'aw-title' : classList[level];
|
||||
</script>
|
||||
|
||||
{#if id}
|
||||
@@ -56,7 +60,7 @@
|
||||
bind:this={element}
|
||||
class:aw-snap-location={id && !inReferences}
|
||||
class:aw-snap-location-references={id && inReferences}
|
||||
class="{classList[level]} aw-u-color-text-primary"
|
||||
class="{headingClass} aw-u-color-text-primary"
|
||||
>
|
||||
<slot />
|
||||
</svelte:element>
|
||||
@@ -65,8 +69,20 @@
|
||||
<svelte:element
|
||||
this={tag}
|
||||
bind:this={element}
|
||||
class="{classList[level]} aw-u-color-text-primary"
|
||||
class="{headingClass} aw-u-color-text-primary"
|
||||
class:in-policy={inPolicy}
|
||||
>
|
||||
<slot />
|
||||
</svelte:element>
|
||||
{/if}
|
||||
|
||||
<style>
|
||||
.aw-title {
|
||||
margin-block-end: 1rem;
|
||||
margin-block-start: 2rem;
|
||||
}
|
||||
|
||||
.aw-sub-body-500.in-policy {
|
||||
margin-block-end: 1.25rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1 +1,7 @@
|
||||
<li><p><slot /></p></li>
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
const isDocs = getContext('isDocs') ?? false;
|
||||
</script>
|
||||
|
||||
<li><p class:aw-paragraph-lg={!isDocs}><slot /></p></li>
|
||||
|
||||
@@ -1,10 +1,20 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
export let href: string;
|
||||
export let title: string;
|
||||
|
||||
const isExternal = ['http://', 'https://'].some((prefix) => href.startsWith(prefix));
|
||||
const target = isExternal ? '_blank' : undefined;
|
||||
const rel = isExternal ? 'noopener nofollow' : undefined;
|
||||
|
||||
const isDocs = getContext('isDocs') ?? false;
|
||||
</script>
|
||||
|
||||
<a class="aw-link" {href} {title} {target} {rel}><slot /></a>
|
||||
<a
|
||||
class="aw-link is-inline {isDocs ? 'aw-paragraph-md' : 'aw-paragraph-lg'}"
|
||||
{href}
|
||||
{title}
|
||||
{target}
|
||||
{rel}><slot /></a
|
||||
>
|
||||
|
||||
@@ -1,13 +1,45 @@
|
||||
<script lang="ts">
|
||||
import { isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
export let ordered: boolean;
|
||||
|
||||
setContext('no-paragraph', true);
|
||||
|
||||
const inPolicy = isInsidePolicy();
|
||||
</script>
|
||||
|
||||
<svelte:element
|
||||
this={ordered ? 'ol' : 'ul'}
|
||||
class:aw-numeric-list={ordered}
|
||||
class:aw-pink-dots={!ordered}><slot /></svelte:element
|
||||
class:aw-pink-dots={!ordered}
|
||||
class:in-policy={inPolicy}><slot /></svelte:element
|
||||
>
|
||||
|
||||
<style lang="scss">
|
||||
.in-policy {
|
||||
gap: 0rem;
|
||||
margin-block-end: 2rem;
|
||||
}
|
||||
|
||||
ol.in-policy {
|
||||
:global(li) {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
:global(li:before) {
|
||||
content: counter(numeric-list) '. ';
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
|
||||
:global(.in-policy) {
|
||||
padding-inline-start: 0.5rem;
|
||||
margin-block-end: 0;
|
||||
}
|
||||
|
||||
:global(.in-policy li:before) {
|
||||
content: counter(numeric-list-level-2, lower-latin) '. ';
|
||||
padding-inline-end: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
|
||||
import { getContext, hasContext } from 'svelte';
|
||||
|
||||
const noParagraph = hasContext('no-paragraph') ? getContext('no-paragraph') : false;
|
||||
const isDocs = getContext('isDocs') ?? false;
|
||||
const inPolicy = isInsidePolicy();
|
||||
|
||||
let className = '';
|
||||
export { className as class };
|
||||
@@ -12,6 +14,8 @@
|
||||
<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>
|
||||
{/if}
|
||||
@@ -20,4 +24,8 @@
|
||||
p.aw-paragraph-lg {
|
||||
margin-block-end: 2rem;
|
||||
}
|
||||
|
||||
.in-policy {
|
||||
margin-block-end: 1rem;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,13 +1,71 @@
|
||||
<script lang="ts">
|
||||
import { setContext } from "svelte";
|
||||
|
||||
setContext("in-table", true);
|
||||
<script lang="ts" context="module">
|
||||
export const TABLE_CTX_KEY = 'in-table';
|
||||
export const getTableCtx = () => getContext<boolean>(TABLE_CTX_KEY);
|
||||
export const isInTable = () => !!getTableCtx();
|
||||
</script>
|
||||
|
||||
<div class="aw-table-wrapper">
|
||||
<script lang="ts">
|
||||
import { getPolicyCtx, isInsidePolicy } from '$markdoc/layouts/Policy.svelte';
|
||||
|
||||
import { getContext, setContext } from 'svelte';
|
||||
|
||||
setContext(TABLE_CTX_KEY, true);
|
||||
|
||||
const inPolicy = isInsidePolicy();
|
||||
const transparentCells = inPolicy ? getPolicyCtx().transparentTableCells : false;
|
||||
</script>
|
||||
|
||||
<div class="aw-table-wrapper" class:in-policy={inPolicy} class:transparentCells>
|
||||
<div class="aw-table-scroll">
|
||||
<table class="aw-table">
|
||||
<slot />
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="scss">
|
||||
.in-policy {
|
||||
font-size: 0.875rem !important;
|
||||
margin-block-end: 2.25rem;
|
||||
|
||||
:global(tr) {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:global(td) {
|
||||
background-color: transparent;
|
||||
border-right: 1px solid hsl(var(--aw-color-smooth));
|
||||
font-family: Inter;
|
||||
font-size: 0.875rem;
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
line-height: 22px; /* 157.143% */
|
||||
letter-spacing: -0.063px;
|
||||
text-transform: none;
|
||||
}
|
||||
|
||||
:global(td .aw-eyebrow) {
|
||||
all: unset;
|
||||
}
|
||||
|
||||
:global(:is(td:first-child, thead td)) {
|
||||
background-color: hsl(var(--aw-color-greyscale-850));
|
||||
color: hsl(var(--aw-color-primary));
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
:global(li::marker) {
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
:global(ul) {
|
||||
gap: 0rem;
|
||||
}
|
||||
}
|
||||
|
||||
.transparentCells {
|
||||
:global(td) {
|
||||
background-color: transparent !important ;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script lang="ts">
|
||||
import type { HTMLTdAttributes } from "svelte/elements";
|
||||
import type { HTMLTdAttributes } from 'svelte/elements';
|
||||
|
||||
export let align: HTMLTdAttributes['align'] = undefined;
|
||||
export let colspan: HTMLTdAttributes['colspan'] = undefined;
|
||||
|
||||
15
src/markdoc/tags/Arrow_Link.svelte
Normal file
@@ -0,0 +1,15 @@
|
||||
<script lang="ts">
|
||||
import { getContext } from 'svelte';
|
||||
|
||||
export let href: string;
|
||||
|
||||
const isExternal = ['http://', 'https://'].some((prefix) => href.startsWith(prefix));
|
||||
const target = isExternal ? '_blank' : undefined;
|
||||
const rel = isExternal ? 'noopener nofollow' : undefined;
|
||||
|
||||
const isDocs = getContext('isDocs') ?? false;
|
||||
</script>
|
||||
|
||||
<a class="aw-link {isDocs ? 'aw-paragraph-md' : 'aw-paragraph-lg'}" {href} {target} {rel}
|
||||
><slot /><span class="icon-cheveron-right" style:font-size="16px" /></a
|
||||
>
|
||||
9
src/markdoc/tags/Count_Section.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<section class="count-secondary-title">
|
||||
<slot />
|
||||
</section>
|
||||
|
||||
<style lang="scss">
|
||||
.count-secondary-title {
|
||||
gap: 0.25rem;
|
||||
}
|
||||
</style>
|
||||
9
src/markdoc/tags/Count_Title.svelte
Normal file
@@ -0,0 +1,9 @@
|
||||
<script lang="ts">
|
||||
export let title = '';
|
||||
|
||||
const el = title ? 'h3' : 'span';
|
||||
</script>
|
||||
|
||||
<svelte:element this={el} class="aw-sub-body-500 aw-u-color-text-primary">
|
||||
{title}
|
||||
</svelte:element>
|
||||
@@ -1,6 +1,6 @@
|
||||
<script lang="ts">
|
||||
export let icon: string;
|
||||
export let size: string = 's';
|
||||
export let size = 's';
|
||||
|
||||
const sizes: Record<string, string> = {
|
||||
s: '16px',
|
||||
@@ -10,4 +10,4 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<span class={`icon-${icon}`} style:font-size={sizes[size]} />
|
||||
<span class="icon-{icon}" style:font-size={sizes[size]} />
|
||||
|
||||
@@ -11,4 +11,7 @@
|
||||
export { default as Only_Light } from './Only_Light.svelte';
|
||||
export { default as Only_Dark } from './Only_Dark.svelte';
|
||||
export { default as Video } from './Video.svelte';
|
||||
export { default as Arrow_Link } from './Arrow_Link.svelte';
|
||||
export { default as Count_Section } from './Count_Section.svelte';
|
||||
export { default as Count_Title } from './Count_Title.svelte';
|
||||
</script>
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
import Tooltip from '$lib/components/Tooltip.svelte';
|
||||
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
|
||||
import { isMobileNavOpen } from '$lib/layouts/Main.svelte';
|
||||
import Technologies from '$lib/components/Technologies.svelte';
|
||||
|
||||
const platforms: Array<{
|
||||
name: string;
|
||||
@@ -549,27 +550,7 @@
|
||||
We support many SDKs making Appwrite flexible to your needs and ensuring
|
||||
you can code with the language you want at any time.
|
||||
</p>
|
||||
<ul
|
||||
class="u-flex u-flex-wrap u-gap-16 aw-u-margin-block-32-mobile aw-u-margin-block-40-not-mobile"
|
||||
>
|
||||
{#each platforms as platform}
|
||||
<li>
|
||||
<Tooltip>
|
||||
<a href={platform.href} class="aw-box-icon">
|
||||
<img
|
||||
src={platform.image}
|
||||
alt="{platform.name} Logo"
|
||||
width="32"
|
||||
height="32"
|
||||
/>
|
||||
</a>
|
||||
<svelte:fragment slot="tooltip"
|
||||
>{platform.name}</svelte:fragment
|
||||
>
|
||||
</Tooltip>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
<Technologies />
|
||||
<a
|
||||
href="/docs/sdks"
|
||||
class="aw-button is-secondary"
|
||||
@@ -588,7 +569,9 @@
|
||||
style:left="0"
|
||||
>
|
||||
<div style:display="grid" style:place-items="center" style:height="100%">
|
||||
<Spline loading="eager" url="/images/animations/lines.splinecode" />
|
||||
<Spline loading="eager" url="/images/animations/lines.splinecode">
|
||||
<img src="/images/bgs/diagonal-lines.png" alt="" width="512" />
|
||||
</Spline>
|
||||
</div>
|
||||
</div>
|
||||
<div class="aw-container u-position-relative">
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
const isVercel = process.env.VERCEL === '1';
|
||||
|
||||
export const prerender = isVercel;
|
||||
|
||||
const parsePosts = (posts: Record<string, any>) => {
|
||||
return Object.fromEntries(
|
||||
Object.entries(posts).map(([key, value]) => {
|
||||
return [
|
||||
key.replace('../../blog/post/', '').replace('/+page.markdoc', ''),
|
||||
value.frontmatter
|
||||
];
|
||||
})
|
||||
);
|
||||
};
|
||||
|
||||
const posts = parsePosts(
|
||||
import.meta.glob('../../blog/post/**/*.markdoc', {
|
||||
eager: true
|
||||
})
|
||||
);
|
||||
|
||||
export const GET = async () => {
|
||||
return new Response(JSON.stringify({ posts, total: Object.keys(posts).length }), {
|
||||
headers: {
|
||||
'content-type': 'application/json;charset=UTF-8'
|
||||
}
|
||||
});
|
||||
};
|
||||
@@ -1,72 +1,6 @@
|
||||
import { base } from '$app/paths';
|
||||
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 { posts, authors, categories } from './content';
|
||||
|
||||
export function load() {
|
||||
const postsGlob = import.meta.glob('./post/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
const authorsGlob = import.meta.glob('./author/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
const categoriesGlob = import.meta.glob('./category/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
|
||||
const posts = Object.entries(postsGlob)
|
||||
.map(([filepath, postList]) => {
|
||||
const { frontmatter } = postList as {
|
||||
frontmatter: PostsData;
|
||||
};
|
||||
const slug = filepath.replace('./', '').replace('/+page.markdoc', '');
|
||||
const postName = slug.slice(slug.lastIndexOf('/') + 1);
|
||||
|
||||
return {
|
||||
title: frontmatter.title,
|
||||
description: frontmatter.description,
|
||||
date: new Date(frontmatter.date),
|
||||
cover: frontmatter.cover,
|
||||
timeToRead: frontmatter.timeToRead,
|
||||
author: frontmatter.author,
|
||||
category: frontmatter.category,
|
||||
href: `${base}/blog/post/${postName}`
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return b.date.getTime() - a.date.getTime();
|
||||
});
|
||||
|
||||
const authors = Object.values(authorsGlob).map((authorList) => {
|
||||
const { frontmatter } = authorList as {
|
||||
frontmatter: AuthorData;
|
||||
};
|
||||
|
||||
return {
|
||||
name: frontmatter.name,
|
||||
slug: frontmatter.slug,
|
||||
role: frontmatter.role,
|
||||
avatar: frontmatter.avatar,
|
||||
bio: frontmatter.bio,
|
||||
twitter: frontmatter.twitter,
|
||||
linkedin: frontmatter.linkedin,
|
||||
github: frontmatter.github,
|
||||
href: `${base}/blog/author/${frontmatter.slug}`
|
||||
};
|
||||
});
|
||||
|
||||
const categories = Object.values(categoriesGlob).map((categoryList) => {
|
||||
const { frontmatter } = categoryList as {
|
||||
frontmatter: CategoryData;
|
||||
};
|
||||
|
||||
return {
|
||||
name: frontmatter.name,
|
||||
description: frontmatter.description,
|
||||
href: `${base}/blog/category/${frontmatter.name.toLowerCase()}`
|
||||
};
|
||||
});
|
||||
|
||||
return {
|
||||
posts,
|
||||
authors,
|
||||
|
||||
11
src/routes/blog/author/luke-silver/+page.markdoc
Normal file
@@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: author
|
||||
slug: luke-silver
|
||||
name: Luke B. Silver
|
||||
role: Software Engineer
|
||||
bio: Building integrations with and on Appwrite
|
||||
avatar: /images/avatars/luke.png
|
||||
twitter: https://twitter.com/lukebsilver
|
||||
github: https://github.com/loks0n
|
||||
linkedin: https://www.linkedin.com/in/lukebsilver/
|
||||
---
|
||||
91
src/routes/blog/content.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { base } from '$app/paths';
|
||||
export type CategoryData = {
|
||||
name: string;
|
||||
description: string;
|
||||
href: string;
|
||||
};
|
||||
export type AuthorData = {
|
||||
name: string;
|
||||
slug: string;
|
||||
role: string;
|
||||
avatar: string;
|
||||
bio: string;
|
||||
twitter: string;
|
||||
linkedin: string;
|
||||
github: string;
|
||||
href: string;
|
||||
};
|
||||
export type PostsData = {
|
||||
title: string;
|
||||
description: string;
|
||||
date: Date;
|
||||
cover: string;
|
||||
timeToRead: number;
|
||||
author: string;
|
||||
category: string;
|
||||
href: string;
|
||||
featured?: boolean;
|
||||
};
|
||||
|
||||
const postsGlob = import.meta.glob('./post/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
const authorsGlob = import.meta.glob('./author/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
const categoriesGlob = import.meta.glob('./category/**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
|
||||
export const posts = Object.entries(postsGlob)
|
||||
.map(([filepath, postList]) => {
|
||||
const { frontmatter } = postList as {
|
||||
frontmatter: PostsData;
|
||||
};
|
||||
const slug = filepath.replace('./', '').replace('/+page.markdoc', '');
|
||||
const postName = slug.slice(slug.lastIndexOf('/') + 1);
|
||||
|
||||
return {
|
||||
title: frontmatter.title,
|
||||
description: frontmatter.description,
|
||||
date: new Date(frontmatter.date),
|
||||
cover: frontmatter.cover,
|
||||
timeToRead: frontmatter.timeToRead,
|
||||
author: frontmatter.author,
|
||||
category: frontmatter.category,
|
||||
href: `${base}/blog/post/${postName}`
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
return b.date.getTime() - a.date.getTime();
|
||||
});
|
||||
|
||||
export const authors = Object.values(authorsGlob).map((authorList) => {
|
||||
const { frontmatter } = authorList as {
|
||||
frontmatter: AuthorData;
|
||||
};
|
||||
|
||||
return {
|
||||
name: frontmatter.name,
|
||||
slug: frontmatter.slug,
|
||||
role: frontmatter.role,
|
||||
avatar: frontmatter.avatar,
|
||||
bio: frontmatter.bio,
|
||||
twitter: frontmatter.twitter,
|
||||
linkedin: frontmatter.linkedin,
|
||||
github: frontmatter.github,
|
||||
href: `${base}/blog/author/${frontmatter.slug}`
|
||||
};
|
||||
});
|
||||
|
||||
export const categories = Object.values(categoriesGlob).map((categoryList) => {
|
||||
const { frontmatter } = categoryList as {
|
||||
frontmatter: CategoryData;
|
||||
};
|
||||
|
||||
return {
|
||||
name: frontmatter.name,
|
||||
description: frontmatter.description,
|
||||
href: `${base}/blog/category/${frontmatter.name.toLowerCase()}`
|
||||
};
|
||||
});
|
||||
12
src/routes/blog/feed.json/+server.ts
Normal file
@@ -0,0 +1,12 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { posts } from '../content';
|
||||
import { json } from '@sveltejs/kit';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: RequestHandler = () => {
|
||||
return json({
|
||||
posts,
|
||||
total: Object.keys(posts).length
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
layout: post
|
||||
title: Add a URL shortener function to your application
|
||||
description: Learning how to add the URL shortener function template to your application.
|
||||
cover: /images/blog/adding-url-shortener/cover.png
|
||||
timeToRead: 5
|
||||
date: 2023-11-08
|
||||
author: haimantika-mitra
|
||||
category: functions
|
||||
featured: false
|
||||
---
|
||||
|
||||
Appwrite Functions are user-defined functions that can start small and scale big, deploying automatically from source control. With the introduction of function templates, you can quickly add new integrations into your app without writing additional code or managing infrastructure. Function templates are pre-built Appwrite Functions that can be integrated into your Appwrite project with just a few clicks.
|
||||
|
||||
A URL shortener takes a long or complex URL and generates a shorter, more concise version. The primary purpose of a URL shortener function is to make links easier to share, manage, and remember.
|
||||
For e.g https://dev.to/appwrite/introducing-appwrite-migrations-effortless-data-migration-from-your-platforms-5dhh to http://tinyurl.com/bd5usb3n
|
||||
|
||||
In this blog, we’ll learn to build a URL shortener using Appwrite Functions template. The template benefits from the built-in scalability, reliability, and security of Appwrite Functions.
|
||||
|
||||
You can find the source code on our [templates GitHub repository](https://github.com/appwrite/templates/tree/main/node/url-shortener).
|
||||
|
||||
# Setting up the Template
|
||||
To get started, you need to navigate to the functions page on the **[Appwrite](https://appwrite.io/cloud)** console. From there, we will select the **Templates** tab, search for and select the **URL Shortener** function template.
|
||||
|
||||

|
||||
|
||||
The function requires two variables:
|
||||
- `APPWRITE_API_KEY`
|
||||
- `SHORT_BASE_URL`
|
||||
|
||||

|
||||
|
||||
Once you have populated those, you can go straight to the **Connect** step.
|
||||
|
||||

|
||||
|
||||
Select **Create a new repository** (this will generate a GitHub repository for you with the function), and leave the production branch and root settings as default to create this function.
|
||||
|
||||
# Using the Function
|
||||
|
||||
Visit the **Domains** tab on the function page and copy the domain URL to test the function.
|
||||
|
||||

|
||||
|
||||
Go to the function URL in your web browser, and you'll see a short url like the one shown below.
|
||||
|
||||
# Next Steps
|
||||
|
||||
URL shorteners often offer additional features such as tracking click-through rates, providing analytics on link usage, and allowing users to customize the shortened URL to some extent.This Function Template can be extended to perform a lot of functionalities. Some examples are:
|
||||
|
||||
1. **Link Analytics and Tracking:** Enhance the URL shortener to track click-through rates, geographical location of users, referral sources, and other analytics data. This could provide valuable insights into the popularity and effectiveness of shared links.
|
||||
2. **Link Management Dashboard:** Create a user-friendly dashboard where users can manage their shortened links, view statistics, and perform various actions on their links.
|
||||
|
||||
Be sure to check out the other available Function Templates. We’ve created many that could be of use in your projects. You can find the [templates GitHub repository here](https://github.com/appwrite/templates).
|
||||
|
||||
For more information about Appwrite and Appwrite Functions:
|
||||
|
||||
1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: These documents provide more information on how to use Appwrite Functions.
|
||||
2. **[Functions Announcement](https://dev.to/appwrite/serverless-your-way-unleashing-appwrite-functions-true-potential-2l4f)**: Read the full announcement on Functions 1.4.
|
||||
3. **[Appwrite Discord](https://discord.com/invite/appwrite)**: Connect with other developers and the Appwrite team for discussion, questions, and collaboration.
|
||||
@@ -0,0 +1,68 @@
|
||||
---
|
||||
layout: post
|
||||
title: Behind the pull request - tales from the open source world
|
||||
description: Celebrating the largest open source event, Hacktoberfest, with inspiring stories from contributors.
|
||||
cover: /images/blog/Behind_the_pull_request_Stories_from_contributors.png
|
||||
timeToRead: 4
|
||||
date: 2023-10-30
|
||||
author: haimantika-mitra
|
||||
category: hacktoberfest, contributors, open-source
|
||||
featured: false
|
||||
---
|
||||
|
||||
In the world of software development, October is more than just a month; it's a season of collaboration, innovation, and celebration.
|
||||
|
||||
# What is Hacktoberfest
|
||||
|
||||
Welcome to Hacktoberfest – a global event that unites tech enthusiasts from every corner of the globe to celebrate open-source software.
|
||||
|
||||
Hacktoberfest isn't just an event, it's a testament to the incredible impact that contributors can make in the open-source community. It's a time when the digital realm comes alive with the buzz of creativity and camaraderie, where individuals of all backgrounds and expertise levels come together to contribute their skills and passion to open-source projects.
|
||||
|
||||
In this blog, we'll explore the significance of Hacktoberfest in the open-source community. We'll highlight how it guides newcomers and seasoned developers toward collaboration and shared knowledge. Additionally, we'll showcase inspiring success stories that exemplify the true spirit of open source.
|
||||
|
||||
Now, let's dive into some real-life stories of contributors who have participated in Hacktoberfest and experienced the transformative power of open-source collaboration.
|
||||
|
||||
# Inspiring stories from the community
|
||||
|
||||
### A journey of discovery and connection
|
||||
|
||||
The first story is from [Sarthak Roy](https://www.linkedin.com/in/sarthakroy2002/), who says, “Well, I started using GitHub properly from February 2021. Hacktoberfest was the reason I found out Appwrite and many more orgs/tools/services in 2021 October. Then I started exploring various stuffs and learnt a ton. During Hacktoberfest 2022, I again contributed in multiple projects, including Appwrite. It all started with the hope of getting a swag in 2021, to enjoying open source and contributing. It also helped me connect with some amazing folks. ”
|
||||
|
||||
Sarthak's story highlights the transformative power of Hacktoberfest, from initially participating for the swag to discovering the joy of open source and connecting with amazing people.
|
||||
|
||||
### Inspiring journey: From novice to maintainer
|
||||
|
||||
Next, let's hear from Moumita Roy, who had an amazing experience with Hacktoberfest. In her own words, "Well, first, I participated in Hacktoberfest 2022 after a friend encouraged me to register for it. Before that, I only knew that GitHub was for pushing code. But through Hacktoberfest, I learned about schemas and other terminologies. Since then, I have contributed to numerous open-source projects. I'm grateful to my friend for introducing me to the world of open source. As a student, I'm still learning and excited about Hacktoberfest 2023. This year, I plan to contribute and participate as a maintainer." Now, let's dive into Moumita's inspiring journey.
|
||||
|
||||
### Embracing the open-source culture
|
||||
|
||||
The final story is from Krishnendu Dasgupta and he says, “One of the best things about Hacktoberfest for me is that it helped me become familiar with and accustomed to the open-source culture. I can still vividly remember Hacktoberfest 2018, when I was asking questions like "What is GitHub?" and learning about version control and collaboration through Git and GitHub. This experience was life-changing, as it continues to enable me to collaborate effectively with my teammates on any project.”
|
||||
|
||||
Krishnendu added: “Another valuable lesson from events like Hacktoberfest is that open source is not just about making significant feature contributions or bringing about major changes. Every contribution matters, as long as it adds value to the main codebase.Last but not least, even if I didn't directly contribute to certain repositories, simply browsing through them made me realize the importance of maintaining good coding practices.”
|
||||
|
||||
He further explained, “Overall, Hacktoberfest is definitely an event worth participating in. It was an eye-opener for me, highlighting the significance of collaboration and creating something great for others!”
|
||||
|
||||
From the above stories, one thing is common: everyone needs to start from somewhere, and for them, that somewhere was Hacktoberfest! If you are someone who is looking to get started in open source, this is your chance!
|
||||
|
||||
# Tips to start contributing
|
||||
|
||||
Now that we've heard the inspiring stories of Hacktoberfest contributors, it's time to take the next step and embark on your own journey of contribution. If you're eager to make a meaningful impact in the open-source community, but unsure of where to begin, don't worry – we've got you covered. Starting your journey as a contributor can feel overwhelming, but fear not!
|
||||
|
||||
These tips will help you make the most out of Hacktoberfest:
|
||||
|
||||
- Familiarize yourself with the official [Hacktoberfest](https://hacktoberfest.com/) rules.
|
||||
- Look for projects that align with your interests and skills.
|
||||
- Choose 1-2 projects and join their communities.
|
||||
- If you get stuck, don't hesitate to reach out to project maintainers or the community for help.
|
||||
- Take the time to learn about the project and always read its contribution guidelines before you start.
|
||||
- Begin with beginner-friendly or good-first issues.
|
||||
- Provide clear descriptions and comments when documenting your pull requests.
|
||||
- Lastly, remember to have fun and enjoy the process of contributing to open source. It's a rewarding experience that can lead to personal and professional growth.
|
||||
|
||||
Here are a few projects that are participating in Hacktoberfest this year and you can contribute to:
|
||||
|
||||
- [Appwrite](https://github.com/search?q=owner%3Aappwrite+owner%3Autopia-php+owner%3Aopen-runtimes+label%3Ahacktoberfest+is%3Aopen+is%3Aissue&type=issues&state=open)
|
||||
- [ILLA Cloud](https://github.com/illacloud/illa)
|
||||
- [Novu](https://github.com/novuhq/novu/labels/Hacktoberfest)
|
||||
|
||||
You can find more participating repositories [here](https://github.com/search?q=hacktoberfest&type=repositories)
|
||||
@@ -0,0 +1,66 @@
|
||||
---
|
||||
layout: post
|
||||
title: Deploy a PDF generation service in minutes with Appwrite functions
|
||||
description: Learn how to deploy PDF generation service in minutes with Appwrite functions.
|
||||
date: 2023-11-14
|
||||
cover: /images/blog/pdf-generation/pdf.png
|
||||
timeToRead: 5
|
||||
author: luke-silver
|
||||
category: website
|
||||
---
|
||||
Appwrite Functions allow you to extend Appwrite's functionality with just a few lines of backend code. Enabling you to build your applications as you imagined. You can start by cloning one of the quick start templates or using a template with pre-built integration to quickly implement features. Function templates are pre-built Appwrite Functions that can be integrated into your Appwrite project with just a few clicks. Using them, you can easily incorporate new features and integrations into your app without writing additional code or managing infrastructure.
|
||||
|
||||
Generating PDFs is a requirement of many applications. For example: creating invoices, generating reports, or any other form of documentation. Yet, it can be difficult to build a robust service in a short amount of time. In this blog, we’ll learn to build a robust PDF generator build with an Appwrite Functions template. The template benefits from the built-in scalability, reliability, and security of Appwrite Functions.
|
||||
|
||||
# Setting up the Template
|
||||
|
||||
The template generates an invoice using fake order data - it includes details such as an order ID, date, customer's name, list of purchased items, and the total cost. It should get you up and running, but you will need to add real data to build a useful invoice.
|
||||
|
||||
If you want to see the source code, you can find it on our [templates GitHub repository](https://github.com/appwrite/templates/tree/main/node/generate-pdf).
|
||||
|
||||
Now, let’s navigate to our functions page on **[Appwrite Cloud](https://cloud.appwrite.io)**. Head over to your Appwrite project and visit the **Functions** page. From there, we will select the **Templates** tab, search for and select the **Generate PDF** function template.
|
||||
|
||||

|
||||
|
||||
The function doesn’t have any required environment variables, so you can proceed straight to the **Connect** step. Select **Create a new repository** (this will generate a GitHub repository for you with the function), and leave the production branch and root settings as default to create this function.
|
||||
|
||||

|
||||
|
||||
# Using the Function
|
||||
|
||||
Visit the **Domains** tab on the function page and copy the domain URL to test the function.
|
||||
|
||||

|
||||
|
||||
Go to the function URL in your web browser, and you'll see a PDF invoice like the one shown below.
|
||||
|
||||

|
||||
|
||||
You can refresh the page in your browser to create a new invoice with new and random information.
|
||||
|
||||
If you’d like to let people download the invoice from your web app, you can do it using plain HTML. Use the `<a>` tag along with the `download` attribute:
|
||||
|
||||
```html
|
||||
<a href="[YOUR_FUNCTION_URL]" download>
|
||||
Download My Invoice
|
||||
</a>
|
||||
```
|
||||
|
||||
On Flutter you can use the [download](https://pub.dev/packages/download), or [open_filex](https://pub.dev/packages/open_filex/) package to achieve a similar result.
|
||||
|
||||
The next step is extend the template to show real invoice data. Here’s a high-level overview of what’s required:
|
||||
|
||||
1. Change the function to take the order ID as a query parameter in the function.
|
||||
2. Get the order document from your Appwrite database, or external data source. Unsure about this? View code recipes on the **[Appwrite Function Docs](https://appwrite.io/docs/functions).**
|
||||
3. If necessary, use the order document to ensure the user has permission to see the order. For example, compare the `x-appwrite-user-id` header with the document user ID.
|
||||
4. Change the function to populate the PDF template with the real order document.
|
||||
|
||||
# Next Steps
|
||||
|
||||
We’ve covered the basics, and now it’s your time to shine! With a few changes, you can extend this template to fit your application. Be sure to check out the other available Function Templates. We’ve created many that could be of use in your projects. You can find the [templates GitHub repository here](https://github.com/appwrite/templates).
|
||||
|
||||
For more information about Appwrite and Appwrite Functions:
|
||||
|
||||
1. **[Appwrite Function Docs](https://appwrite.io/docs/functions)**: These documents provide more information on how to use Appwrite Functions.
|
||||
2. **[Appwrite Cloud](https://appwrite.io/cloud)**: Try our cloud service to get started quickly.
|
||||
3. **[Appwrite Discord](https://discord.com/invite/appwrite)**: Connect with other developers and the Appwrite team for discussion, questions, and collaboration.
|
||||
69
src/routes/blog/post/hf-2023-journey/+page.markdoc
Normal file
@@ -0,0 +1,69 @@
|
||||
---
|
||||
layout: post
|
||||
title: Appwrite's Hacktoberfest 2023 journey
|
||||
description: Summing up Appwrite's participation in Hacktoberfest 2023.
|
||||
date: 2023-11-01
|
||||
cover: /images/blog/hf-2023-journey/cover.png
|
||||
timeToRead: 5
|
||||
author: aditya-oberai
|
||||
category: hacktoberfest
|
||||
---
|
||||
|
||||
**October** is our favorite month of the year because it brings with it **Hacktoberfest**, the largest celebration of open source in the world! And this year, we returned to support DigitalOcean and **Hacktoberfest 2023** as a sponsor to invest in the upliftment of the open-source world and to rejoice the 10th anniversary of this wonderful initiative. Hacktoberfest brought moments of nostalgia seeing developers from different walks of life join in to take their first steps in the open-source world.
|
||||
|
||||
We had an absolute blast interacting with all the new contributors and hosting all the events throughout the month! Our **[Discord server](https://appwrite.io/discord)** was full of **AMAs** and **PR Review parties**, which enabled us to demonstrate our PR review workflow and allowed the community to really appreciate the work that goes into maintaining open-source projects. We were also able to educate new contributors on various facets of open source, usage of tools like Git and GitHub, the process of submitting a PR, communication best practices, and so much more!
|
||||
|
||||
Not only did we see multiple virtual events, but we also hosted an [in-person Hacktoberfest Kickoff meetup](https://photos.app.goo.gl/n5YQjK56CCgqyrj68) in collaboration with DigitalOcean in Bengaluru, India. Overall, this Hacktoberfest was one of the most wholesome and fulfilling experiences for our team, and we cannot wait to share our highlights from this edition.
|
||||
|
||||

|
||||
|
||||
## Our history with Hacktoberfest
|
||||
|
||||
Before we jump into some amazing open-source contributions from Hacktoberfest this year, we would love to share why our participation in Hacktoberfest matters far more to us than we can express. **Appwrite was first released back in September 2019** with one mission, to make software development accessible and enjoyable for all. **Hacktoberfest 2019** was truly a game-changer for us, as we saw **over 200 contributions** from some lovely members of the open-source fraternity that October. The stars on our project doubled that month and entered the 4-digit range for the very first time.
|
||||
|
||||
That one month truly enabled us to lay the foundations of a budding organization with the sole purpose of helping developers build faster and better. And for that, no thank-yous will ever be enough to express our gratitude for this amazing open source community that chose to carry us on their shoulders at our inception. This year, as a sponsor of Hacktoberfest, we set out with the mission to **uplift the ever-growing open-source community** and **enable more newcomers** to step in and grow in this ecosystem.
|
||||
|
||||
## Some of our favorite contributions
|
||||
|
||||
This Hacktoberfest, we focused on collaborating with our contributors to make impactful contributions. Some of them really stood out to us, and we thought to share them here for you to check out too!
|
||||
|
||||
- [Security Scans in the CI pipeline using GitLeaks](https://github.com/appwrite/appwrite/pull/6492)
|
||||
|
||||
- [Functions template to implement Sync with MeiliSearch in Kotlin](https://github.com/appwrite/templates/pull/234)
|
||||
|
||||
- [Messaging adapter for Postmark](https://github.com/utopia-php/messaging/pull/26)
|
||||
|
||||
- [Storage adapter for IBM Cloud Object Storage](https://github.com/utopia-php/storage/pull/90)
|
||||
|
||||
- [Unit tests for the Android and Kotlin SDKs](https://github.com/appwrite/sdk-generator/pull/729)
|
||||
|
||||
The Appwrite team also participated in a number of livestreams throughout the month.
|
||||
|
||||
- [Open Source Friday with Appwrite](https://www.youtube.com/watch?v=VJvawxeUhN4) by GitHub
|
||||
|
||||
- [Learn healthy contribution practices with Aditya Oberai!](https://www.twitch.tv/videos/1947516149) by GitHub Education
|
||||
|
||||
- [Building Appwrite Functions](https://www.twitch.tv/videos/1953276802) by MLH
|
||||
|
||||
## Milestones achieved so far
|
||||
|
||||
Together, we made a lot of noise during Hacktoberfest, leading to some amazing results for the Appwrite community!
|
||||
|
||||
- **135+** Hacktoberfest issues
|
||||
- **65+** accepted Pull Requests
|
||||
- **30+** new GitHub contributors
|
||||
- **8** Hacktoberfest PR Review Parties
|
||||
|
||||
All these achievements were made possible through the strong collaboration and active participation of the community through a variety of contributions, spanning **adapters**, **function templates**, and more!
|
||||
|
||||
## What’s next for Appwrite
|
||||
|
||||
We have a lot of exciting features that we’re working towards as we gear up to make **Appwrite Cloud** generally available. Appwrite Cloud is the hosted Appwrite solution managed by our team, so developers like you can focus on building their applications. We’re currently running Appwrite Cloud in Public Beta, so [sign up and start building](https://cloud.appwrite.io/register) as soon as you can!
|
||||
|
||||
Besides that, this Hacktoberfest, one new product that we saw a number of folks contribute to is **Appwrite Messaging**. Stay tuned for more information on the same.
|
||||
|
||||
## How can you contribute after Hacktoberfest?
|
||||
|
||||
Hacktoberfest may be over, but you don’t have to stop contributing! We have **lots of open issues** that you can find on our [GitHub repos](https://github.com/appwrite/appwrite/issues). You can also **write articles**, **create tutorials**, or **build demo apps** and add them to our [Awesome Appwrite repo](https://github.com/appwrite/awesome-appwrite) and the [Built With Appwrite website](https://builtwith.appwrite.io). There are always new ways to support the [community](https://appwrite.io/community), and we truly love all the contributions you make. If you need help with Appwrite or would like to explore some interesting ways to contribute, join us on our [Discord server](https://appwrite.io/discord) and connect with the Appwrite community.
|
||||
|
||||
Thank you so much once again for joining us during Hacktoberfest. We hope you enjoyed contributing to open source as much as we do, and we can’t wait to have you all with us next year!
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: post
|
||||
title: Make open source healthier by being a better contributor
|
||||
description: How you can improve community health of an open-source project through healthy contribution practices
|
||||
description: How you can improve community health of an open-source project through healthy contribution practices.
|
||||
date: 2023-10-23
|
||||
cover: /images/blog/make-open-source-healthier.png
|
||||
timeToRead: 5
|
||||
@@ -17,16 +17,16 @@ In the context of open-source communities, **community health** refers to the ov
|
||||
|
||||
A healthy open-source community typically has the following characteristics:
|
||||
|
||||
- **Active participation** and **continuous engagement**
|
||||
- An **inclusive** and **open environment** for everyone, regardless of their backgrounds, skills, etc.
|
||||
- **Effective communication channels**, such as mailing lists, forums, or chat rooms
|
||||
- **Transparent decision-making** about the project's direction, priorities, and governance
|
||||
- **Strong leadership** to guide the community, set the vision, and help resolve conflicts
|
||||
- A well-defined **code of conduct** to establish expectations for behavior
|
||||
- Well-maintained **documentation** for the project and its processes
|
||||
- **Recognition** and **appreciation** of contributors' efforts
|
||||
- **Collaboration** and **teamwork** among members
|
||||
- **Opportunities** to **learn** and **grow** as a professional
|
||||
- Active participation and continuous engagement
|
||||
- An inclusive and open environment for everyone, regardless of their backgrounds, skills, etc.
|
||||
- Effective communication channels, such as mailing lists, forums, or chat rooms
|
||||
- Transparent decision-making about the project's direction, priorities, and governance
|
||||
- Strong leadership to guide the community, set the vision, and help resolve conflicts
|
||||
- A well-defined code of conduct to establish expectations for behavior
|
||||
- Well-maintained documentation for the project and its processes
|
||||
- Recognition and appreciation of contributors' efforts
|
||||
- Collaboration and teamwork among members
|
||||
- Opportunities to learn and grow as a professional
|
||||
|
||||
While some of these characteristics, like strong leadership, transparent decision-making, and creating a code of conduct, are responsibilities primarily held by maintainers, most of these are shared between maintainers and contributors.
|
||||
|
||||
@@ -34,43 +34,43 @@ While some of these characteristics, like strong leadership, transparent decisio
|
||||
|
||||
Having discussed what community health means, it is necessary to then discuss what contributors can do to improve the community experience for everyone involved in the contribution process. Here are some practices I recommend from my experience as a contributor and maintainer:
|
||||
|
||||
- **Wait till you’re assigned** an issue
|
||||
- **Wait till you’re assigned an issue**
|
||||
|
||||
The majority of open-source projects have processes of selecting what issue is necessary to work on and who works on what issue. This is why it is important to ensure that your raised issue is either selected or you’re assigned to work on one. Not doing so before you start contributing may cause you to repeat someone else’s work.
|
||||
|
||||
- Make **objectively valuable** contributions
|
||||
- **Make objectively valuable contributions**
|
||||
|
||||
When making a contribution, it is important to ensure that your contribution is objectively valuable and not subjective. Only then does it create value for everyone in the community and not a specific group only. For example, fixing spelling mistakes in documentation is objectively valuable, but changing a paragraph from active to passive voice is not.
|
||||
|
||||
- **Focus on quality** rather than quantity
|
||||
- **Focus on quality rather than quantity**
|
||||
|
||||
Rather than making too many contributions quickly and carelessly, it is better to focus on a smaller number and ensure they’re in the best shape possible. This way, your work leads to lesser maintenance debt and more positive value addition.
|
||||
|
||||
- **Review** previously merged contributions
|
||||
- **Review previously merged contributions**
|
||||
|
||||
Looking through past accepted contributions gives you a better idea of the project's development practices and communication processes. Better clarity on these makes it much easier for you to integrate within the operations of the projects.
|
||||
|
||||
- **Don’t call dibs** on issues
|
||||
- **Don’t call dibs on issues**
|
||||
|
||||
There’s plenty of work to go around for every person in the community, whether around code, documentation, maintenance, etc. Let’s make sure we don’t end up adding more barriers in the process by spamming requests to get assigned on every issue we find.
|
||||
|
||||
- **Wait for a couple of days** before you ask for an issue to be **reassigned**
|
||||
- **Wait for a couple of days before you ask for an issue to be reassigned**
|
||||
|
||||
When contributing to open source, it is necessary to remember that most people participate here outside of their day-to-day lives. With everything else in our lives, with work, academics, families, etc., sometimes people can’t prioritize open source immediately. A little patience here goes a long way for everyone.
|
||||
|
||||
- **Communicate your progress** with the maintainers
|
||||
- **Communicate your progress with the maintainers**
|
||||
|
||||
After you are assigned an issue, it’s good to update the project’s maintainers at regular intervals. This way, everyone in the process can track progress and challenges, thus keeping transparency for the community.
|
||||
|
||||
- **Be patient** with the maintainers
|
||||
- **Be patient with the maintainers**
|
||||
|
||||
Most open-source work is unpaid and voluntary. Maintainers do it out of their love for the technology and community. And as lovely as maintaining their project can be, as the communities around their projects grow, it can very easily become overwhelming. Therefore, if your contribution has been under review for a while, patience goes a long way.
|
||||
|
||||
- **Help** with Pull Request **reviews**
|
||||
- **Help with Pull Request reviews**
|
||||
|
||||
Most open-source projects tend to have a much higher ratio of contributors to maintainers. Therefore, once you know what maintainers look for in changes, you can leave appropriate feedback for the project to help make life easier for everyone.
|
||||
|
||||
- Give and accept **feedback** **constructively** and **humbly**
|
||||
- **Give and accept feedback constructively and humbly**
|
||||
|
||||
The beauty of open source is that you could end up collaborating with people from drastically different places, walks of life, or knowledge levels. You never know whether the person you’re working with is a college sophomore or a veteran software engineer. Giving constructive feedback means that you enable others to learn and grow from your experiences. Accepting it with humility enables you to achieve the same.
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
layout: post
|
||||
title: Migrate Firebase projects to Appwrite
|
||||
description: Migrate your Firebase projects to Appwrite Cloud in just one click.
|
||||
date: 2023-11-06
|
||||
cover: /images/blog/migrate-firebase-projects-to-appwrite/migrate-firebase-projects-to-appwrite.png
|
||||
timeToRead: 10
|
||||
author: vincent-ge
|
||||
category: migration
|
||||
---
|
||||
If you’re ready to move from Firebase to Appwrite, or you just want to explore your BaaS options, we can give you a jump start with Appwrite Migrations.
|
||||
|
||||

|
||||
|
||||
Moving is frustrating. Packing, unpacking, renting a truck, wondering if your bed fits through the door. Moving data between cloud platforms feels much the same, so we built Migrations to help lighten the load. Migrations will help you move users, data, and files out of Firebase and into your Appwrite project.
|
||||
|
||||
## Limitations
|
||||
|
||||
Just like moving houses, not every piece of old furniture will suit your new house. You’ll need some creative rearranging or maybe some new furniture. Appwrite is different from Firebase in a similar sense.
|
||||
|
||||
- Appwrite automatically moves user accounts or files, but documents and cloud functions will require some work to move.
|
||||
- Nested documents will require some clever engineering to be transformed into Appwrite relationships. In a future version, they might be added automatically, but you will need to migrate them with a script for now.
|
||||
- Appwrite Functions also use different syntax and deployment methods than Firebase’s Cloud Functions. These will need the most work to migrate and likely need to be partially rewritten.
|
||||
|
||||
## Prepare for your move
|
||||
|
||||
To help you with your move, Appwrite Migrations will require keys to your old home to access your project’s users, files, and data. This means Appwrite will need your Firebase service account’s API key.
|
||||
|
||||

|
||||
|
||||
The quickest way to get a key is to use your **Firebase Admin SDK** service account and key, found under **Project settings > Service accounts > Generate private key**.
|
||||
|
||||
If you want to use an API key with a more fine-grained permission scope, checkout the [Appwrite documentation on this topic](/docs/advanced/migrations/firebase).
|
||||
|
||||
## Starting the migration
|
||||
|
||||
The Migration process is simple. Give Appwrite your keys, and it’ll pack up and move everything for you.
|
||||
|
||||
1. Create a new project and click on the **Migrations** tab in **Project Settings**.
|
||||
2. Click on the **Create Migration** button and select **Firebase** as your source.
|
||||
3. Paste your Firebase API key’s JSON contents into the account credentials box and click **Next.**
|
||||
4. Select which resources you want Migrations to import and click **Create** to start the migration.
|
||||
|
||||
Migrations will run in the background, get a cup of tea or coffee, and return in a few minutes.
|
||||
|
||||
## Wrapping up the move
|
||||
|
||||
After your migration is completed, you’ll need to do a few more things to wrap up your move to Appwrite.
|
||||
|
||||
First, you register your existing web, mobile, and native apps as platforms. Follow one of these [quick starts](/docs/quick-starts) to learn how.
|
||||
|
||||
Next, checkout our docs for [Database relationships](/docs/products/databases/relationships) and install one of Appwrite’s [Server SDKs](/docs/sdks#server) to migrate your nested data.
|
||||
|
||||
Finally, learn about [Appwrite Functions](/docs/products/functions/quick-start) to migrate your Firebase functions. Appwrite’s functions support more runtimes, more flexible execution schemes, and follow patters found in HTTP controllers that you’re already familiar with.
|
||||
|
||||
## Join the discussion
|
||||
|
||||
We’re always having a blast on [Discord](/discord). With members in the community from all over the world, you’ll always find someone to support and share your Appwrite journey.
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: post
|
||||
title: Demystifying password hashing algorithms
|
||||
description: What are password hashing algorithms and how they help secure user credentials in your application
|
||||
description: What are password hashing algorithms and how they help secure user credentials in your application.
|
||||
date: 2023-10-20
|
||||
cover: /images/blog/password-hashing-algorithms.png
|
||||
timeToRead: 7
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
layout: post
|
||||
title: "Password protection for developers: importance and best practices"
|
||||
description: Why it is necessary to implement strong password protection policies in your app and best practices to follow
|
||||
description: Why it is necessary to implement strong password protection policies in your app and best practices to follow.
|
||||
date: 2023-10-11
|
||||
cover: /images/blog/password-protection.png
|
||||
timeToRead: 6
|
||||
|
||||
25
src/routes/blog/rss.xml/+server.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import type { RequestHandler } from './$types';
|
||||
import { posts } from '../content';
|
||||
|
||||
export const prerender = true;
|
||||
|
||||
export const GET: RequestHandler = () => {
|
||||
const feed = `<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
|
||||
<channel>
|
||||
<title>Appwrite</title>
|
||||
<link>https://appwrite.io</link>
|
||||
<description>Appwrite is an open-source platform for building applications at any scale, using your preferred programming languages and tools.</description>
|
||||
${posts.map((post) => `<item>
|
||||
<title>${post.title}</title>
|
||||
<pubDate>${post.date.toUTCString()}</pubDate>
|
||||
<link>https://appwrite.io${post.href}</link>
|
||||
<guid>https://appwrite.io${post.href}</guid>
|
||||
<description>${post.description}</description>
|
||||
</item>
|
||||
`).join('')}
|
||||
</channel>
|
||||
</rss>`;
|
||||
|
||||
return new Response(feed);
|
||||
};
|
||||
@@ -12,55 +12,69 @@
|
||||
import EventCard from './EventCard.svelte';
|
||||
import type { ProjectCardProps } from './ProjectCard.svelte';
|
||||
import ProjectCard from './ProjectCard.svelte';
|
||||
import MetricCard from '$lib/components/MetricCard.svelte';
|
||||
|
||||
export let data;
|
||||
|
||||
const events: EventCardProps[] = [
|
||||
{
|
||||
href: '/discord',
|
||||
cover: {
|
||||
src: '/images/community/events/office-hours.png',
|
||||
src: '/images/community/events/office-hours2.png',
|
||||
alt: ''
|
||||
},
|
||||
date: 'Sep 28th',
|
||||
date: 'November 2nd',
|
||||
location: 'Discord',
|
||||
title: 'Office hours',
|
||||
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
|
||||
buttonText: 'View event'
|
||||
},
|
||||
{
|
||||
href: 'https://www.twitch.tv/wesscope',
|
||||
href: '/discord',
|
||||
cover: {
|
||||
src: '/images/community/events/live-coding.png',
|
||||
src: '/images/community/events/office-hours3.png',
|
||||
alt: ''
|
||||
},
|
||||
date: 'Sep 28th',
|
||||
location: 'Twitch',
|
||||
title: 'Live coding with Wess',
|
||||
description: "Pluck is going mobile. Join us in building Pluck.io's Flutter app.",
|
||||
date: 'November 9th',
|
||||
location: 'Discord',
|
||||
title: 'Office hours',
|
||||
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
|
||||
buttonText: 'View event'
|
||||
},
|
||||
{
|
||||
href: 'https://lu.ma/hf-kickoff-blr',
|
||||
href: '/discord',
|
||||
cover: {
|
||||
src: '/images/community/events/hf-kickoff-bangalore.png',
|
||||
alt: 'Hacktoberfest kickoff event'
|
||||
src: '/images/community/events/office-hours4.png',
|
||||
alt: ''
|
||||
},
|
||||
date: 'Oct 1st',
|
||||
location: 'Bengaluru',
|
||||
title: 'Hacktoberfest kickoff event',
|
||||
description: 'Join us for our Hacktoberfest kickoff event in Bengaluru.',
|
||||
date: 'November 16th',
|
||||
location: 'Discord',
|
||||
title: 'Office hours',
|
||||
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
|
||||
buttonText: 'View event'
|
||||
},
|
||||
{
|
||||
href: 'https://hacktoberfest.com/',
|
||||
href: '/discord',
|
||||
cover: {
|
||||
src: '/images/community/events/oss-celebration.png',
|
||||
alt: 'Hacktoberfest kickoff event'
|
||||
src: '/images/community/events/office-hours5.png',
|
||||
alt: ''
|
||||
},
|
||||
date: 'Oct',
|
||||
location: 'Virtual',
|
||||
title: 'Hacktoberfest 10',
|
||||
description:
|
||||
'Join us for a month long celebration of open source in collaboration with DigitalOcean.',
|
||||
date: 'November 23rd',
|
||||
location: 'Discord',
|
||||
title: 'Office hours',
|
||||
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
|
||||
buttonText: 'View event'
|
||||
},
|
||||
{
|
||||
href: '/discord',
|
||||
cover: {
|
||||
src: '/images/community/events/office-hours6.png',
|
||||
alt: ''
|
||||
},
|
||||
date: 'November 30th',
|
||||
location: 'Discord',
|
||||
title: 'Office hours',
|
||||
description: 'Join us for an exciting hour of technical conversations around Appwrite.',
|
||||
buttonText: 'View event'
|
||||
}
|
||||
];
|
||||
@@ -68,7 +82,7 @@
|
||||
const projects: ProjectCardProps[] = [
|
||||
{
|
||||
title: 'Auth UI',
|
||||
description: 'Appwirte-powered authentication screens generator for any application.',
|
||||
description: 'Appwrite-powered authentication screens generator for any application.',
|
||||
image: {
|
||||
src: 'https://cloud.appwrite.io/v1/storage/buckets/thumbnails/files/64803bb4f34eb4b05ee3/preview?width=800&output=webp&project=builtWithAppwrite',
|
||||
alt: 'Auth UI: Fully customizable login flow for your applications'
|
||||
@@ -96,6 +110,17 @@
|
||||
}
|
||||
];
|
||||
|
||||
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' }
|
||||
];
|
||||
|
||||
let name = '';
|
||||
let email = '';
|
||||
let submitted = false;
|
||||
@@ -178,7 +203,7 @@
|
||||
>
|
||||
<span aria-hidden="true" class="aw-icon-star" />
|
||||
<span>Star on GitHub</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">36.8K</span>
|
||||
<span class="aw-inline-tag aw-sub-body-400">37.9K</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -188,54 +213,11 @@
|
||||
<div class="aw-big-padding-section-level-2">
|
||||
<section class="aw-container">
|
||||
<ul class="aw-grid-row-4 aw-grid-row-4-mobile-2" style="--gap-mobile:1.5rem;">
|
||||
{#each metrics as props}
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">33K+</div>
|
||||
<div class="aw-description">GitHub Stars</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">8K+</div>
|
||||
<div class="aw-description">Pull Requests</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">15K+</div>
|
||||
<div class="aw-description">Commits</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">2.5K+</div>
|
||||
<div class="aw-description">Issues</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">400+</div>
|
||||
<div class="aw-description">Open Issues</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">1.9K+</div>
|
||||
<div class="aw-description">Closed Issues</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">4.9K+</div>
|
||||
<div class="aw-description">Forks</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-card is-normal has-border-gradient">
|
||||
<div class="aw-title aw-u-color-text-primary">20K+</div>
|
||||
<div class="aw-description">Contributors</div>
|
||||
</div>
|
||||
<MetricCard {...props} />
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
@@ -342,135 +324,37 @@
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="aw-table-line-body">
|
||||
{#each data.issues as issue}
|
||||
<tr class="aw-table-line-row">
|
||||
<td class="aw-table-line-cell u-un-break-text">
|
||||
<span class="aw-caption-400">#5232</span>
|
||||
<span class="aw-caption-400"
|
||||
>#{issue.number}</span
|
||||
>
|
||||
</td>
|
||||
<td class="aw-table-line-cell">
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/appwrite/appwrite/issues/5232"
|
||||
href={issue.url}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-link aw-sub-body-500"
|
||||
>
|
||||
Feature: Security Scans like SAST, DAST,
|
||||
FOSS, CAST in pipeline
|
||||
{issue.title}
|
||||
</a>
|
||||
<span>(appwrite/appwrite)</span>
|
||||
<span>({issue.repository})</span>
|
||||
</div>
|
||||
<ul
|
||||
class="u-flex u-flex-wrap u-gap-8 u-margin-block-start-8"
|
||||
>
|
||||
{#each issue.tags as tag}
|
||||
<li>
|
||||
<div class="aw-tag">Enhancement</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Help Wanted</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Discussion</div>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="aw-table-line-row">
|
||||
<td class="aw-table-line-cell u-un-break-text">
|
||||
<span class="aw-caption-400">#5748</span>
|
||||
</td>
|
||||
<td class="aw-table-line-cell">
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/appwrite/appwrite/issues/5748"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-link aw-sub-body-500"
|
||||
>
|
||||
Feature: overload Feature for
|
||||
Storage.CreateFile for using Blob instead of
|
||||
File
|
||||
</a>
|
||||
<span>(appwrite/appwrite)</span>
|
||||
</div>
|
||||
<ul
|
||||
class="u-flex u-flex-wrap u-gap-8 u-margin-block-start-8"
|
||||
>
|
||||
<li>
|
||||
<div class="aw-tag">Enhancement</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Help Wanted</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Discussion</div>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="aw-table-line-row">
|
||||
<td class="aw-table-line-cell u-un-break-text">
|
||||
<span class="aw-caption-400">#680</span>
|
||||
</td>
|
||||
<td class="aw-table-line-cell">
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/appwrite/sdk-generator/issues/680"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-link aw-sub-body-500"
|
||||
>
|
||||
Feature: Unit Tests
|
||||
</a>
|
||||
<span>(appwrite/sdk-generator)</span>
|
||||
</div>
|
||||
<ul
|
||||
class="u-flex u-flex-wrap u-gap-8 u-margin-block-start-8"
|
||||
>
|
||||
<li>
|
||||
<div class="aw-tag">Enhancement</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Help Wanted</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Discussion</div>
|
||||
</li>
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="aw-table-line-row">
|
||||
<td class="aw-table-line-cell u-un-break-text">
|
||||
<span class="aw-caption-400">#679</span>
|
||||
</td>
|
||||
<td class="aw-table-line-cell">
|
||||
<div>
|
||||
<a
|
||||
href="https://github.com/appwrite/sdk-generator/issues/679"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="aw-link aw-sub-body-500"
|
||||
>
|
||||
Feature: overload Feature for
|
||||
Storage.CreateFile for using Blob instead of
|
||||
File
|
||||
</a>
|
||||
<span>(appwrite/sdk-generator)</span>
|
||||
</div>
|
||||
<ul
|
||||
class="u-flex u-flex-wrap u-gap-8 u-margin-block-start-8"
|
||||
>
|
||||
<li>
|
||||
<div class="aw-tag">Enhancement</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Help Wanted</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="aw-tag">Discussion</div>
|
||||
<div class="aw-tag">{tag}</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@@ -700,8 +584,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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">
|
||||
<div class="aw-grid-1-1-opt-2 u-gap-32">
|
||||
@@ -801,7 +685,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="aw-big-padding-section-level-2 is-margin-replace-padding u-position-relative"
|
||||
|
||||
63
src/routes/community/+page.ts
Normal file
@@ -0,0 +1,63 @@
|
||||
import { dev } from '$app/environment';
|
||||
|
||||
type Issue = {
|
||||
number: number;
|
||||
url: string;
|
||||
title: string;
|
||||
repository: string;
|
||||
tags: string[];
|
||||
};
|
||||
|
||||
const mockIssues: Issue[] = [
|
||||
{
|
||||
number: 5232,
|
||||
url: 'https://github.com/appwrite/appwrite/issues/5232',
|
||||
title: 'Feature: Security Scans like SAST, DAST, FOSS, CAST in pipeline',
|
||||
repository: 'appwrite/appwrite',
|
||||
tags: ['Enhancement', 'Help Wanted', 'Discussion']
|
||||
},
|
||||
{
|
||||
number: 5748,
|
||||
url: 'https://github.com/appwrite/appwrite/issues/5748',
|
||||
title: 'Feature: overload Feature for Storage.CreateFile for using Blob instead of File',
|
||||
repository: 'appwrite/appwrite',
|
||||
tags: ['Enhancement', 'Help Wanted', 'Discussion']
|
||||
},
|
||||
{
|
||||
number: 680,
|
||||
url: 'https://github.com/appwrite/sdk-generator/issues/680',
|
||||
title: 'Feature: Unit Tests',
|
||||
repository: 'appwrite/sdk-generator',
|
||||
tags: ['Enhancement', 'Help Wanted', 'Discussion']
|
||||
},
|
||||
{
|
||||
number: 679,
|
||||
url: 'https://github.com/appwrite/sdk-generator/issues/679',
|
||||
title: 'Feature: overload Feature for Storage.CreateFile for using Blob instead of File',
|
||||
repository: 'appwrite/sdk-generator',
|
||||
tags: ['Enhancement', 'Help Wanted', 'Discussion']
|
||||
}
|
||||
];
|
||||
|
||||
export const load = async () => {
|
||||
// if (dev) {
|
||||
// return { issues: mockIssues };
|
||||
// }
|
||||
|
||||
// fetch issues from github, appwrite/appwrite repo
|
||||
const response = await fetch('https://api.github.com/repos/appwrite/appwrite/issues');
|
||||
const issues = await response.json();
|
||||
|
||||
// map issues to our format
|
||||
return {
|
||||
issues: issues
|
||||
.map((issue: any) => ({
|
||||
number: issue.number,
|
||||
url: issue.html_url,
|
||||
title: issue.title,
|
||||
repository: issue.repository_url.replace('https://api.github.com/repos/', ''),
|
||||
tags: issue.labels.map((label: any) => label.name)
|
||||
}))
|
||||
.slice(0, 6)
|
||||
};
|
||||
};
|
||||
@@ -134,7 +134,7 @@
|
||||
<ul class="u-flex u-gap-8">
|
||||
<li>
|
||||
<a
|
||||
href="https://appwrite.io/discord"
|
||||
href="/discord"
|
||||
class="aw-icon-button"
|
||||
aria-label="Appwrite on aw-icon-discord"
|
||||
target="_blank"
|
||||
|
||||
464
src/routes/cookies/+page.markdoc
Normal file
@@ -0,0 +1,464 @@
|
||||
---
|
||||
layout: policy
|
||||
title: Cookies Policy
|
||||
transparentTableCells: true
|
||||
---
|
||||
|
||||
We use in our site [Appwrite (https://appwrite.io/)](https://appwrite.io/) ("**Site**") cookies and similar files or technologies to automatically collect and store information about your computer, device, and Site usage, in order to improve their performance and enhance your user experience. We use the general term "**cookies**" in this policy to refer to these technologies and all such similar technologies that collect information automatically when you are using our Site where this policy is posted. You can find out more about cookies and how to control them in the information below.
|
||||
|
||||
If you do not accept the use of these cookies, please disable them using the instructions in this cookie policy or by changing your browser settings so that cookies from this Site cannot be placed on your computer or mobile device. Important: disabling cookies on this Site may impair certain Site features.
|
||||
|
||||
In this Cookies Policy, we use the term Appwio "we", "us" and "our") to refer to Appwrite Code Ltd. Our Privacy Policy is available at [Appwrite Privacy Policy (https://appwrite.io/policy/privacy)](https://appwrite.io/policy/privacy).
|
||||
|
||||
## What is a cookie?
|
||||
|
||||
Cookies are computer files containing small amounts of information which are downloaded to your computer or mobile device when you visit a website. Cookies can then be sent back to the originating website on each subsequent visit, or to another website that recognizes that cookie. Cookies are widely used to make websites work more efficiently, provide information to the owners of the website, and improve the user experience. Cookies may tell us, for example, whether you have visited our Site before or whether you are a new visitor.
|
||||
|
||||
There are two broad categories of cookies:
|
||||
- **First party cookies**, served directly by us to your computer or mobile device.
|
||||
- **Third party cookies**, which are served by a third party on our behalf. We use third-party cookies for functionality, performance/analytics, marketing, unclassified, and other technologies, and social media purposes. The third-party cookies are outside of our control. The third parties may, at any time, change their terms of service, purpose, and use of cookies, etc. See below additional information on how to manage such cookies.
|
||||
|
||||
Cookies can remain on your computer or mobile device for different periods of time. Some cookies are 'session cookies', meaning that they exist only while your browser is open. These are deleted automatically once you close your browser. Other cookies are 'permanent cookies', meaning that they survive after your browser is closed. They can be used by websites to recognize your computer when you open your browser and browse the Internet again.
|
||||
|
||||
## Web beacons
|
||||
|
||||
Cookies are not the only way to recognize or track visitors to a website. We may use other, similar technologies from time to time, like web beacons (sometimes called "tracking pixels" or "clear gifs"). These are small graphics files that contain a unique identifier that enables us to recognize when someone has visited our website. This allows us, for example, to monitor the traffic patterns of users from one page within our website to another, to deliver or communicate with cookies, to understand whether you have come to our website from an online advertisement displayed on a third-party website, to improve website performance, and to measure the success of email marketing campaigns. In most instances, these technologies are reliant on cookies to function, and therefore declining cookies prevents them from functioning.
|
||||
|
||||
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:
|
||||
- Track traffic flow and patterns of travel in connection with our Site;
|
||||
- Understand the total number of visitors to our Sites on an ongoing basis and the types of internet browsers (e.g. Chrome, Firefox, Safari, or Internet Explorer) and operating systems (e.g. Windows or Mac) used by our visitors;
|
||||
- Monitor the performance of our Site and to continually improve it; and
|
||||
- Customize and enhance your online experience.
|
||||
|
||||
What types of cookies do we use?
|
||||
|
||||
The types of cookies used by us in connection with the Site can be considered 'essential website cookies', 'functionality cookies', 'analytics and performance cookies', 'marketing', 'unclassified', and 'other technologies'. We've set out some further information below, and the purposes of the cookies we set in the following table.
|
||||
|
||||
# Cookies necessary for essential website purposes
|
||||
|
||||
These cookies are essential to provide you with services available through this Site and to use some of its features, such as access to secure areas. Without these cookies, we will not be able to provide services that you require, such as transactional pages and secure login accounts.
|
||||
|
||||
{% table %}
|
||||
* Cookie name
|
||||
* Source
|
||||
* Expiry (In days)
|
||||
* Purpose
|
||||
---
|
||||
* a_session_console
|
||||
* 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
|
||||
|
||||
We use performance/analytics cookies to analyze how the website is accessed, used, or is performing. We do this in order to provide you with a better user experience and to maintain, operate, and continually improve the website. For example, these cookies allow us to:
|
||||
|
||||
- Better understand our website visitors so that we can improve how we present our content
|
||||
- Test different design ideas for particular pages, such as our homepage;
|
||||
- Collect information about Site visitors such as where they are located and what browsers they are using;
|
||||
- Determine the number of unique users of the website;
|
||||
- Improve the website by measuring any errors that occur; and
|
||||
- Conduct research and diagnostics to improve product offerings.
|
||||
|
||||
{% table %}
|
||||
* Cookie name
|
||||
* Source
|
||||
* 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
|
||||
* Bug tracking and debugging
|
||||
---
|
||||
* _lr5_
|
||||
* Logrocket
|
||||
* Session
|
||||
* Bug tracking and debugging
|
||||
---
|
||||
* _lr5_
|
||||
* Logrocket
|
||||
* Session
|
||||
* Bug tracking and debugging
|
||||
---
|
||||
* _lr_hb_-rgthvf%2Fappwrite
|
||||
* Logrocket
|
||||
* Session
|
||||
* Bug tracking and debugging
|
||||
---
|
||||
* _lr_tabs_-rgthvf%2Fappwrite
|
||||
* Logrocket
|
||||
* Session
|
||||
* Bug tracking and debugging
|
||||
---
|
||||
* cookiecookie
|
||||
* Logrocket
|
||||
* Session
|
||||
* For debugging actions on our the console
|
||||
{% /table %}
|
||||
|
||||
# Marketing
|
||||
|
||||
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.
|
||||
|
||||
{% table %}
|
||||
* Cookie name
|
||||
* Source
|
||||
* Expiry (In days)
|
||||
* Purpose
|
||||
---
|
||||
* cb_user_id
|
||||
* Clearbit: B2B Marketing Intelligence
|
||||
* 364
|
||||
* Tracks page views and traits for Clearbit
|
||||
---
|
||||
* pt_
|
||||
* appwrite.io
|
||||
* Session
|
||||
* NA
|
||||
---
|
||||
* _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
|
||||
{% /table %}
|
||||
|
||||
# How to control or delete cookies
|
||||
|
||||
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.
|
||||
|
||||
As another way of controlling cookies, most browsers allow you to change your cookie settings. These settings will typically be found in the “options” or “preferences” menu of your browser. In order to understand these settings and learn how to use them, please consult the “Help” function of your browser, or the documentation published online for your particular browser type and version.
|
||||
|
||||
However, please note that if you choose to refuse cookies you may not be able to use the full functionality of our Site.
|
||||
|
||||
The following pages have information on how to change your cookies settings for the different browsers:
|
||||
|
||||
- [Cookie settings in Chrome](https://support.google.com/chrome/answer/95647?hl=en&ref_topic=14666) and [Chrome mobile](https://support.google.com/chrome/answer/95647?hl=en)
|
||||
- [Cookie settings in Firefox](https://support.mozilla.org/en-US/kb/cookies-information-websites-store-on-your-computer?redirectlocale=en-US&redirectslug=Cookies) and [Firefox mobile](https://support.mozilla.org/en-US/kb/clearing-cookies-private-data-history-and-settings)
|
||||
- [Cookie settings in Internet Explorer](https://support.microsoft.com/en-us/windows/delete-and-manage-cookies-168dab11-0753-043d-7c16-ede5947fc64d#ie=ie-10) and [Microsoft Edge](https://support.microsoft.com/en-gb/microsoft-edge/delete-cookies-in-microsoft-edge-63947406-40ac-c3b8-57b9-2a946a29ae09)
|
||||
- [Cookie settings in Safari](https://support.apple.com/guide/safari/manage-cookies-sfri11471/mac) and [Safari mobile](https://support.apple.com/en-gb/HT201265)
|
||||
- [Cookie settings in iOS](https://support.apple.com/en-gb/HT201265)
|
||||
- [Cookie settings in Opera](https://help.opera.com/en/latest/web-preferences/)
|
||||
- [Cookie settings in Apple Safari](https://support.apple.com/en-gb/safari)
|
||||
|
||||
If you use another browser, you can check if the procedure for your browser is mentioned in [this explanatory website](https://allaboutcookies.org/how-to-manage-cookies).
|
||||
|
||||
To opt out of being tracked by Google Analytics across all websites, visit [here](https://tools.google.com/dlpage/gaoptout).
|
||||
|
||||
# Third party websites' cookies
|
||||
|
||||
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).
|
||||
|
||||
# Need more information?
|
||||
|
||||
If you would like to find out more about cookies and their use on the Internet, you may find the following link useful: [All about cookies](https://allaboutcookies.org/).
|
||||
|
||||
# Cookies that have been set in the past
|
||||
|
||||
If you have disabled one or more Cookies, we may still use information collected from cookies prior to your disabled preference being set; however, we will stop using the disabled cookie to collect any further information.
|
||||
|
||||
# Contact us
|
||||
|
||||
If you have any questions or comments about this cookies policy or privacy matters generally, please contact us via email at [privacy@appwrite.io](mailto:privacy@appwrite.io).
|
||||
|
||||
|
||||
|
||||
@@ -15,46 +15,31 @@
|
||||
const tutorials: CodeCardProps[] = [
|
||||
{
|
||||
href: '/docs/tutorials/react',
|
||||
cover: {
|
||||
src: '/images/tutorials/react.png',
|
||||
alt: 'react logo'
|
||||
},
|
||||
cover: '/images/tutorials/react.png',
|
||||
title: 'React tutorial',
|
||||
description: 'Learn Appwrite Auth, Databases, and more with React.'
|
||||
},
|
||||
{
|
||||
href: '/docs/tutorials/sveltekit',
|
||||
cover: {
|
||||
src: '/images/tutorials/svelte.png',
|
||||
alt: 'svelte logo'
|
||||
},
|
||||
cover: '/images/tutorials/svelte.png',
|
||||
title: 'SvelteKit tutorial',
|
||||
description: 'Learn Appwrite Auth, Databases, and more with SvelteKit.'
|
||||
},
|
||||
{
|
||||
href: '/docs/tutorials/vue',
|
||||
cover: {
|
||||
src: '/images/tutorials/vue.png',
|
||||
alt: 'Vue logo'
|
||||
},
|
||||
cover: '/images/tutorials/vue.png',
|
||||
title: 'Vue tutorial',
|
||||
description: 'Learn Appwrite Auth, Databases, and more with Vue.'
|
||||
},
|
||||
{
|
||||
href: '/docs/tutorials/android',
|
||||
cover: {
|
||||
src: '/images/tutorials/android.png',
|
||||
alt: 'Android logo'
|
||||
},
|
||||
cover: '/images/tutorials/android.png',
|
||||
title: 'Android tutorial',
|
||||
description: 'Learn Appwrite Auth, Databases, and more with Android.'
|
||||
},
|
||||
{
|
||||
href: '/docs/tutorials/flutter',
|
||||
cover: {
|
||||
src: '/images/tutorials/flutter.png',
|
||||
alt: 'Flutter logo'
|
||||
},
|
||||
cover: '/images/tutorials/flutter.png',
|
||||
title: 'Flutter tutorial',
|
||||
description: 'Learn Appwrite Auth, Databases, and more with Flutter.'
|
||||
}
|
||||
@@ -80,9 +65,6 @@
|
||||
|
||||
<Docs variant="default">
|
||||
<Sidebar />
|
||||
<div class="u-position-absolute u-inset-inline-end-0 aw-u-opacity-40-mobile" style="">
|
||||
<img src="/images/bgs/docs-blur-1.svg" alt="" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="u-position-absolute aw-is-not-mobile u-only-light"
|
||||
@@ -100,26 +82,11 @@
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div
|
||||
class="u-position-absolute aw-is-not-mobile u-only-dark"
|
||||
style:inline-size="768px"
|
||||
style:block-size="768px"
|
||||
style:inset-block-start="26rem"
|
||||
style:z-index="-1"
|
||||
style:left="calc(50% - 384px + 600px)"
|
||||
>
|
||||
<Spline url="/images/animations/dark-scene.splinecode" width={768} height={768}>
|
||||
<img
|
||||
src="/images/animations/tech-dark.png"
|
||||
width="768"
|
||||
height="768"
|
||||
alt=""
|
||||
style="position: absolute;"
|
||||
/>
|
||||
</Spline>
|
||||
<main class="aw-main-section u-position-relative">
|
||||
<div class="u-position-absolute aw-u-opacity-40-mobile bg-blur">
|
||||
<img src="/images/bgs/docs-blur-1.svg" alt="" />
|
||||
</div>
|
||||
|
||||
<main class="aw-main-section u-position-relative">
|
||||
<div
|
||||
class="u-position-absolute u-inset-inline-start-0 u-inset-block-start-16 aw-u-opacity-40-mobile"
|
||||
style=" margin-inline-start: -30px;"
|
||||
@@ -129,25 +96,40 @@
|
||||
|
||||
<section class="aw-hero is-align-start u-position-relative">
|
||||
<h1 class="aw-display aw-u-color-text-primary u-max-width-600">
|
||||
Learn how to build like a team of hundreds
|
||||
Learn how to build like a team of hundreds<span class="aw-u-color-text-accent"
|
||||
>_
|
||||
</span>
|
||||
</h1>
|
||||
<p class="aw-description u-max-width-600">
|
||||
Appwrite is a backend development platform built by hundreds of open-source
|
||||
developers, where you can build any application at any scale using the coding
|
||||
languages and tools you want.
|
||||
Appwrite helps you build secure and scalable apps, faster. Leverage Appwrite's
|
||||
powerful APIs to stop fighting technologies and start delivering value.
|
||||
</p>
|
||||
</section>
|
||||
<section class="aw-hero is-align-start">
|
||||
<section class="aw-hero is-align-start tech-hero">
|
||||
<h2 class="aw-title aw-u-color-text-primary u-max-width-600">
|
||||
Get started with your technologies
|
||||
</h2>
|
||||
<p class="aw-description u-max-width-600">
|
||||
Follow a quick start guide on your web, mobile, and native frameworks.
|
||||
Start building with your preferred web, mobile, and native frameworks by following a
|
||||
quick start guide.
|
||||
</p>
|
||||
<Technologies />
|
||||
<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">
|
||||
<Spline url="/images/animations/dark-scene.splinecode" width={660} height={660}>
|
||||
<img
|
||||
src="/images/animations/tech-dark.png"
|
||||
width="660"
|
||||
height="660"
|
||||
alt=""
|
||||
style="position: absolute;"
|
||||
/>
|
||||
</Spline>
|
||||
</div>
|
||||
<div class="bg-boi" />
|
||||
</section>
|
||||
<section>
|
||||
<h2 class="aw-title aw-u-color-text-primary u-max-width-600">Show me some code</h2>
|
||||
@@ -428,3 +410,64 @@
|
||||
<MainFooter variant="docs" />
|
||||
</main>
|
||||
</Docs>
|
||||
|
||||
<style lang="scss">
|
||||
.aw-main-section {
|
||||
max-inline-size: unset;
|
||||
margin-inline: unset;
|
||||
padding-inline-start: 3rem; // 48px
|
||||
}
|
||||
|
||||
.bg-blur {
|
||||
inset-inline-end: -300px;
|
||||
inset-block-start: -100px;
|
||||
}
|
||||
|
||||
.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,
|
||||
hsl(var(--aw-color-smooth)) 0%,
|
||||
hsl(var(--aw-color-smooth)) 90%,
|
||||
transparent
|
||||
);
|
||||
|
||||
max-inline-size: unset;
|
||||
margin-block-start: 2rem;
|
||||
margin-inline-start: -3rem;
|
||||
padding-block: 5rem;
|
||||
padding-inline-start: 3rem;
|
||||
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
|
||||
.spline-wrapper {
|
||||
--p-size: 700px;
|
||||
inline-size: var(--p-size);
|
||||
block-size: var(--p-size);
|
||||
inset-block-start: -3rem;
|
||||
z-index: -1;
|
||||
left: calc(50% - 384px + 600px);
|
||||
}
|
||||
|
||||
.bg-boi {
|
||||
position: absolute;
|
||||
background: linear-gradient(to right, #19191c00 0%, #19191c00 10%, #19191c);
|
||||
|
||||
top: 0;
|
||||
right: 10rem;
|
||||
translate: 100%;
|
||||
width: 25rem;
|
||||
height: 100%;
|
||||
content: '';
|
||||
z-index: 9999;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
<script lang="ts" context="module">
|
||||
export type CodeCardProps = {
|
||||
href: string;
|
||||
cover: {
|
||||
src: string;
|
||||
alt: string;
|
||||
};
|
||||
cover: string;
|
||||
title: string;
|
||||
description: string;
|
||||
};
|
||||
@@ -20,7 +17,7 @@
|
||||
|
||||
<a class="aw-grid-articles-item is-full-color" {href}>
|
||||
<div class="aw-grid-articles-item-image">
|
||||
<img src={cover.src} alt={cover.alt} loading="lazy" />
|
||||
<img src={cover} alt='' loading="lazy" />
|
||||
</div>
|
||||
<article class="aw-grid-articles-item-content">
|
||||
<header class="aw-articles-item-header">
|
||||
|
||||
@@ -6,7 +6,7 @@ description: Harness the power of events in Appwrite. Explore event-driven archi
|
||||
|
||||
Appwrite provides a variety of events that allows your application to react to changes as they happen.
|
||||
A event will fire when a change occurs in your Appwrite project, like when a new user registers or a new file is uploaded to Appwrite.
|
||||
You can subscribe to these events to with Appwrite [Functions](/docs/products/functions), [Realtime](/docs/apis/realtime), or [Webhooks](/docs/advanced/platform/webhooks).
|
||||
You can subscribe to these events with Appwrite [Functions](/docs/products/functions), [Realtime](/docs/apis/realtime), or [Webhooks](/docs/advanced/platform/webhooks).
|
||||
|
||||
You can subscribe to events for specific resources using their ID or subscribe to changes of all resources of the same type by using a wildcard character * instead of an ID.
|
||||
You can also filter for events of specific actions like create, update, or delete.
|
||||
|
||||
@@ -12,7 +12,7 @@ As the name suggests, read permission allows a user to read a resource, create a
|
||||
|
||||
All permissions can be granted to individuals or groups of users, entire teams, or only to team members with a specific role. Permission can also be granted based on authentication status, such as to all users, only authenticated users, or only guest users.
|
||||
|
||||
A project user can only grant permissions to a resource that they own. For example, if a user is trying to share a document with a team that they are not a member of, they will encounter a 401 not authorized error. If your app needs users to grant access to teams they're not a member of, you can create Appwrite Functions with a [Server SDK](/docs/sdks#server) to achieve this functionality.
|
||||
A project user can only grant permissions to a resource that they have. For example, if a user is trying to share a document with a team that they are not a member of, they will encounter a 401 not authorized error. If your app needs users to grant access to teams they're not a member of, you can create Appwrite Functions with a [Server SDK](/docs/sdks#server) to achieve this functionality.
|
||||
|
||||
# Appwrite resource {% #appwrite-resource %}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="install" \
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
{% /tabsitem %}
|
||||
|
||||
@@ -46,7 +46,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="install" ^
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
## Powershell
|
||||
```powershell
|
||||
@@ -54,7 +54,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="install" `
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
{% /tabsitem %}
|
||||
{% /tabs %}
|
||||
@@ -104,7 +104,7 @@ Make these configurations to unlock the full power of Appwrite.
|
||||
|
||||
# Manual (Docker Compose) {% #manual %}
|
||||
|
||||
For advanced Docker users, the manual installation might seem more familiar. To set up Appwrite manually, download the Appwrite base `docker-compose.yml` and `.env` files, then move them inside a directory named `appwrite`. After the download completes, update the different environment variables as you wish in the `.env` file and start the Appwrite stack using the following Docker command:
|
||||
For advanced Docker users, the manual installation might seem more familiar. To set up Appwrite manually, download the Appwrite base `docker-compose.yml` and `.env` files from the [Appwrite repo](https://github.com/appwrite/appwrite), then move them inside a directory named `appwrite`. After the download completes, update the different environment variables as you wish in the `.env` file and start the Appwrite stack using the following Docker command:
|
||||
|
||||
```bash
|
||||
docker compose up -d --remove-orphans
|
||||
|
||||
@@ -34,7 +34,9 @@ docker logs [CONTAINER-ID]
|
||||
|
||||
Appwrite uses conventional HTTP response codes to indicate the success or failure of an API request. In general: Codes in the 2xx range indicate success. Codes in the 4xx range indicate an error that failed given the information provided (e.g., a required parameter was omitted, invalid input, etc.). Codes in the 5xx range indicate an error with the Appwrite server, but these are rare.
|
||||
|
||||
[Learn more about Appwrite status codes {% icon icon="cheveron-right" /%}](/docs/advanced/platform/response-codes)
|
||||
{% arrow_link href="/docs/advanced/platform/response-codes" %}
|
||||
Learn more about Appwrite status codes
|
||||
{% /arrow_link %}
|
||||
|
||||
# Development mode {% #development-mode%}
|
||||
|
||||
|
||||
@@ -29,7 +29,9 @@ Appwrite provides three different methods to limit access to your Appwrite Conso
|
||||
|
||||
By default, only the first user can sign up on the Appwrite instance's dashboard. All other users must be added to the dashboard through invitation.
|
||||
|
||||
[Learn more about environment variables {% icon icon="cheveron-right" /%}](/docs/advanced/self-hosting/environment-variables)
|
||||
{% arrow_link href="/docs/advanced/self-hosting/environment-variables" %}
|
||||
Learn more about environment variables
|
||||
{% /arrow_link %}
|
||||
|
||||
# Scaling {% #scaling %}
|
||||
|
||||
@@ -47,7 +49,9 @@ If you disabled rate limits during development, make sure you re-enable them whe
|
||||
|
||||
Rate limits are an important mechanism to protect your app. Without rate limits, malicious actors can spam your APIs to perform [denial-of-service type attacks](https://en.wikipedia.org/wiki/Denial-of-service_attack) or brute-force user passwords.
|
||||
|
||||
[Learn more about environment variables {% icon icon="cheveron-right" /%}](/docs/advanced/self-hosting/environment-variables)
|
||||
{% arrow_link href="/docs/advanced/self-hosting/environment-variables" %}
|
||||
Learn more about environment variables
|
||||
{% /arrow_link %}
|
||||
|
||||
# Emails {% #emails %}
|
||||
|
||||
@@ -78,7 +82,9 @@ To monitor errors in production, add a third party monitoring service by setting
|
||||
|
||||
In production, it is highly recommended to turn error reporting off. To do so, make sure the Appwrite container environment variable `_APP_ENV` is set to `production` and not `development`.
|
||||
|
||||
[Learn more about environment variables {% icon icon="cheveron-right" /%}](/docs/advanced/self-hosting/environment-variables)
|
||||
{% arrow_link href="/docs/advanced/self-hosting/environment-variables" %}
|
||||
Learn more about environment variables
|
||||
{% /arrow_link %}
|
||||
|
||||
# Security {% #security %}
|
||||
|
||||
|
||||
@@ -14,7 +14,9 @@ Some of these services can be self-hosted, just like Appwrite.
|
||||
|
||||
You can select which storage adapter to use by setting the `_APP_STORAGE_DEVICE` environment variable. Valid values are `local`, `s3`, `dospaces`, `backblaze`, `linode`, and `wasabi`. Each storage adapter requires its own set of additional environment variables to configure.
|
||||
|
||||
[Learn more about storage environment variables {% icon icon="cheveron-right" /%}](/docs/advanced/self-hosting/environment-variables)
|
||||
{% arrow_link href="/docs/advanced/self-hosting/environment-variables" %}
|
||||
Learn more about storage environment variables
|
||||
{% /arrow_link %}
|
||||
|
||||
# Maximum file size {% #adapters %}
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ docker run -it --rm \
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock \
|
||||
--volume "$(pwd)"/appwrite:/usr/src/code/appwrite:rw \
|
||||
--entrypoint="upgrade" \
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
|
||||
## CMD
|
||||
@@ -45,7 +45,7 @@ docker run -it --rm ^
|
||||
--volume //var/run/docker.sock:/var/run/docker.sock ^
|
||||
--volume "%cd%"/appwrite:/usr/src/code/appwrite:rw ^
|
||||
--entrypoint="upgrade" ^
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
|
||||
## PowerShell
|
||||
@@ -55,7 +55,7 @@ docker run -it --rm `
|
||||
--volume /var/run/docker.sock:/var/run/docker.sock `
|
||||
--volume ${pwd}/appwrite:/usr/src/code/appwrite:rw `
|
||||
--entrypoint="upgrade" `
|
||||
appwrite/appwrite:1.4.7
|
||||
appwrite/appwrite:1.4.9
|
||||
```
|
||||
|
||||
This will pull the `docker-compose.yml` file for the new version and perform the installation.
|
||||
|
||||
@@ -12,4 +12,6 @@ Authentication makes it easy to build secure and robust authentication with supp
|
||||
You can manage user accounts with user preferences, user labeling, or organizing users into teams.
|
||||
Combined with a robust permissions system, Appwrite Authentication provides everything you need to authenticate and manage users.
|
||||
|
||||
[Quick start {% icon icon="cheveron-right" /%}](/docs/products/auth/quick-start)
|
||||
{% arrow_link href="/docs/products/auth/quick-start" %}
|
||||
Quick start
|
||||
{% /arrow_link %}
|
||||
|
||||
@@ -184,5 +184,8 @@ individual users using the `Role.user(<USER_ID>, <STATUS>)` role.
|
||||
| Unverified user | `Role.user(<USER_ID>, 'unverified')` |
|
||||
|
||||
|
||||
{% arrow_link href="/docs/advanced/platform/permissions" %}
|
||||
Learn more about permissions
|
||||
{% /arrow_link %}
|
||||
|
||||
|
||||
[Learn more about permissions {% icon icon="cheveron-right" /%}](/docs/advanced/platform/permissions)
|
||||
|
||||
@@ -82,10 +82,18 @@ Anonymous users cannot sign back in. If the session expires, they move to anothe
|
||||
|
||||
Create an account with any of these methods to transition from an anonymous session to a user account session.
|
||||
|
||||
[Email and password {% icon icon="cheveron-right" /%}](/docs/products/auth/email-password)
|
||||
{% arrow_link href="/docs/products/auth/email-password" %}
|
||||
Email and password
|
||||
{% /arrow_link %}
|
||||
|
||||
[Phone (SMS) {% icon icon="cheveron-right" /%}](/docs/products/auth/phone-sms)
|
||||
{% arrow_link href="/docs/products/auth/phone-sms" %}
|
||||
Phone (SMS)
|
||||
{% /arrow_link %}
|
||||
|
||||
[Magic URL {% icon icon="cheveron-right" /%}](/docs/products/auth/magic-url)
|
||||
{% arrow_link href="/docs/products/auth/magic-url" %}
|
||||
Magic URL
|
||||
{% /arrow_link %}
|
||||
|
||||
[OAuth2 {% icon icon="cheveron-right" /%}](/docs/products/auth/oauth2)
|
||||
{% arrow_link href="/docs/products/auth/oauth2" %}
|
||||
OAuth2
|
||||
{% /arrow_link %}
|
||||
|
||||
@@ -183,4 +183,6 @@ This would correspond with the permissions below.
|
||||
| Delete | `Permissions.delete(Role.label('subscriber'))` |
|
||||
| Create | `Permissions.create(Role.label('subscriber'))` |
|
||||
|
||||
[Learn more about permissions {% icon icon="cheveron-right" /%}](/docs/advanced/platform/permissions)
|
||||
{% arrow_link href="/docs/advanced/platform/permissions" %}
|
||||
Learn more about permissions
|
||||
{% /arrow_link %}
|
||||
@@ -198,4 +198,6 @@ individual roles in the team using the `Role.team(<TEAM_ID>, [<ROLE_1>, <ROLE_2>
|
||||
| Select roles | `Role.team(<TEAM_ID>, [<ROLE_1>, <ROLE_2>, ...])`|
|
||||
|
||||
|
||||
[Learn more about permissions {% icon icon="cheveron-right" /%}](/docs/advanced/platform/permissions)
|
||||
{% arrow_link href="/docs/advanced/platform/permissions" %}
|
||||
Learn more about permissions
|
||||
{% /arrow_link %}
|
||||
@@ -12,4 +12,6 @@ If you need to act on behalf of users through an Appwrite Function or your own b
|
||||
|
||||
The users API can be used to create users, import users, update user info, get user audit logs, and remove users.
|
||||
|
||||
[Learn more in the Users API references {% icon icon="cheveron-right" /%}](/docs/references/cloud/server-nodejs/users)
|
||||
{% arrow_link href="/docs/references/cloud/server-nodejs/users" %}
|
||||
Learn more in the Users API references
|
||||
{% /arrow_link %}
|
||||
|
||||
@@ -14,4 +14,6 @@ Databases store data, if you need to store files like images, PDFs or videos, us
|
||||
You can organize data into databases, collections, and documents. You can also paginate, order, and query documents.
|
||||
For complex business logic, Appwrite supports relationships to help you model your data.
|
||||
|
||||
[Quick start {% icon icon="cheveron-right" /%}](/docs/products/databases/quick-start)
|
||||
{% arrow_link href="/docs/products/databases/quick-start" %}
|
||||
Quick start
|
||||
{% /arrow_link %}
|
||||
@@ -21,4 +21,6 @@ Start exploring by cloning one of the quick start templates or using a template
|
||||

|
||||
{% /only_light %}
|
||||
|
||||
[Quick start {% icon icon="cheveron-right" /%}](/docs/products/functions/quick-start)
|
||||
{% arrow_link href="/docs/products/functions/quick-start" %}
|
||||
Quick start
|
||||
{% /arrow_link %}
|
||||
@@ -47,7 +47,7 @@ export default async ({ req, res, log, error }) => {
|
||||
|
||||
// `res.json()` is a handy helper for sending JSON
|
||||
return res.json({
|
||||
motto: 'Build Fast. Scale Big. All in One Place.',
|
||||
motto: 'Build like a team of hundreds_',
|
||||
learn: 'https://appwrite.io/docs',
|
||||
connect: 'https://appwrite.io/discord',
|
||||
getInspired: 'https://builtwith.appwrite.io',
|
||||
@@ -87,7 +87,7 @@ return function ($context) {
|
||||
|
||||
// `res.json()` is a handy helper for sending JSON
|
||||
return $context->res->json([
|
||||
'motto' => 'Build Fast. Scale Big. All in One Place.',
|
||||
'motto' => 'Build like a team of hundreds_',
|
||||
'learn' => 'https://appwrite.io/docs',
|
||||
'connect' => 'https://appwrite.io/discord',
|
||||
'getInspired' => 'https://builtwith.appwrite.io',
|
||||
@@ -126,7 +126,7 @@ def main(context):
|
||||
|
||||
# `context.res.json()` is a handy helper for sending JSON
|
||||
return context.res.json({
|
||||
"motto": "Build Fast. Scale Big. All in One Place.",
|
||||
"motto": "Build like a team of hundreds_",
|
||||
"learn": "https://appwrite.io/docs",
|
||||
"connect": "https://appwrite.io/discord",
|
||||
"getInspired": "https://builtwith.appwrite.io",
|
||||
@@ -161,7 +161,7 @@ def main(context)
|
||||
|
||||
# `context.res.json()` is a handy helper for sending JSON
|
||||
return context.res.json({
|
||||
"motto": "Build Fast. Scale Big. All in One Place.",
|
||||
"motto": "Build like a team of hundreds_",
|
||||
"learn": "https://appwrite.io/docs",
|
||||
"connect": "https://appwrite.io/discord",
|
||||
"getInspired": "https://builtwith.appwrite.io",
|
||||
@@ -196,7 +196,7 @@ export default ({ req, res, log, error }: any) => {
|
||||
|
||||
// `res.json()` is a handy helper for sending JSON
|
||||
return res.json({
|
||||
motto: "Build Fast. Scale Big. All in One Place.",
|
||||
motto: "Build like a team of hundreds_",
|
||||
learn: "https://appwrite.io/docs",
|
||||
connect: "https://appwrite.io/discord",
|
||||
getInspired: "https://builtwith.appwrite.io",
|
||||
@@ -232,7 +232,7 @@ Future main(final context) async {
|
||||
|
||||
// `res.json()` is a handy helper for sending JSON
|
||||
return context.res.json({
|
||||
'motto': 'Build Fast. Scale Big. All in One Place.',
|
||||
'motto': 'Build like a team of hundreds_',
|
||||
'learn': 'https://appwrite.io/docs',
|
||||
'connect': 'https://appwrite.io/discord',
|
||||
'getInspired': 'https://builtwith.appwrite.io',
|
||||
@@ -272,7 +272,7 @@ class Main {
|
||||
|
||||
// `context.res.json()` is a handy helper for sending JSON
|
||||
return context.res.json(mutableMapOf(
|
||||
"motto" to "Build Fast. Scale Big. All in One Place.",
|
||||
"motto" to "Build like a team of hundreds_",
|
||||
"learn" to "https://appwrite.io/docs",
|
||||
"connect" to "https://appwrite.io/discord",
|
||||
"getInspired" to "https://builtwith.appwrite.io"
|
||||
@@ -314,7 +314,7 @@ public class Main {
|
||||
}
|
||||
|
||||
Map json = new HashMap<>();
|
||||
json.put("motto", "Build Fast. Scale Big. All in One Place.");
|
||||
json.put("motto", "Build like a team of hundreds_");
|
||||
json.put("learn", "https://appwrite.io/docs");
|
||||
json.put("connect", "https://appwrite.io/discord");
|
||||
json.put("getInspired", "https://builtwith.appwrite.io");
|
||||
@@ -354,7 +354,7 @@ func main(context: RuntimeContext) async throws -> RuntimeOutput {
|
||||
|
||||
// `context.res.json()` is a handy helper for sending JSON
|
||||
return try context.res.json([
|
||||
"motto": "Build Fast. Scale Big. All in One Place.",
|
||||
"motto": "Build like a team of hundreds_",
|
||||
"learn": "https://appwrite.io/docs",
|
||||
"connect": "https://appwrite.io/discord",
|
||||
"getInspired": "https://builtwith.appwrite.io",
|
||||
@@ -397,7 +397,7 @@ public class Handler {
|
||||
// `Context.Res.Json()` is a handy helper for sending JSON
|
||||
return Context.Res.Json(new Dictionary()
|
||||
{
|
||||
{ "motto", "Build Fast. Scale Big. All in One Place." },
|
||||
{ "motto", "Build like a team of hundreds_" },
|
||||
{ "learn", "https://appwrite.io/docs" },
|
||||
{ "connect", "https://appwrite.io/discord" },
|
||||
{ "getInspired", "https://builtwith.appwrite.io" },
|
||||
@@ -1414,8 +1414,8 @@ include Appwrite
|
||||
def main(context)
|
||||
client = Client.new
|
||||
.set_endpoint('https://cloud.appwrite.io/v1')
|
||||
.set_project(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
|
||||
.set_key(req.variables['APPWRITE_API_KEY'])
|
||||
.set_project(ENV['APPWRITE_FUNCTION_PROJECT_ID'])
|
||||
.set_key(ENV['APPWRITE_API_KEY'])
|
||||
|
||||
databases = Appwrite::Databases.new(client)
|
||||
|
||||
@@ -1740,7 +1740,7 @@ include Appwrite
|
||||
def main(context)
|
||||
client = Client.new
|
||||
.set_endpoint('https://cloud.appwrite.io/v1')
|
||||
.set_project(req.variables['APPWRITE_FUNCTION_PROJECT_ID'])
|
||||
.set_project(ENV['APPWRITE_FUNCTION_PROJECT_ID'])
|
||||
|
||||
if context.request.headers['x-appwrite-user-jwt']
|
||||
client.set_jwt(context.request.headers['x-appwrite-user-jwt'])
|
||||
|
||||
@@ -154,7 +154,7 @@ return function ($context) {
|
||||
$amountInRupees = floatval($context->req->query['amount']);
|
||||
$response = $client->get('https://api.exchangerate.host/latest?base=INR&symbols=USD');
|
||||
$data = $response->json();
|
||||
$amountInDollars = amountInRupees * $data['rates']['USD'];
|
||||
$amountInDollars = $amountInRupees * $data['rates']['USD'];
|
||||
return $context->res->send(strval($amountInDollars));
|
||||
}
|
||||
|
||||
|
||||
@@ -15,4 +15,6 @@ Appwrite Storage stores files like images, PDFs or videos. If you need to store
|
||||
# Get started {% #get-started %}
|
||||
Get started with Appwrite Storage. Learn to setup up a bucket, upload, and download your first file.
|
||||
|
||||
[Quick start {% icon icon="cheveron-right" /%}](/docs/products/storage/quick-start)
|
||||
{% arrow_link href="/docs/products/storage/quick-start" %}
|
||||
Quick start
|
||||
{% /arrow_link %}
|
||||
@@ -231,7 +231,7 @@ This means users can't create new files or read, update, and delete existing fil
|
||||
|
||||
[Learn about configuring permissions](/docs/products/storage/permissions).
|
||||
|
||||
# Encrption {% #encryption %}
|
||||
# Encryption {% #encryption %}
|
||||
Appwrite provides added security settings for your buckets. Enable encryption under your bucket's **Settings** > **Security settings**.
|
||||
You can enable encryption to encrypt files in your buckets. If your files are leaked, encrypted files cannot be read by the malicious actor.
|
||||
Files bigger than 20MB cannot be encrypted.
|
||||
|
||||
@@ -123,8 +123,6 @@ To upload a file, add this to you web, Flutter, Apple, or Android app.
|
||||
```
|
||||
|
||||
{% /multicode %}
|
||||
During testing, you might prefer to create documents in the Appwrite Console.
|
||||
To do so, navigate to the **Documents** tab of your collection and click the **Add document** button.
|
||||
|
||||
|
||||
# Download file {% #download-file %}
|
||||
|
||||
@@ -119,8 +119,6 @@ You can also upload files programmatically using our SDKs:
|
||||
```
|
||||
|
||||
{% /multicode %}
|
||||
During testing, you might prefer to create documents in the Appwrite Console.
|
||||
To do so, navigate to the **Documents** tab of your collection and click the **Add document** button.
|
||||
|
||||
# Large files {% #large-files %}
|
||||
When you are trying to upload any files above 5MB, you will need to upload them in chunks for better reliability and performance.
|
||||
|
||||
@@ -72,12 +72,12 @@ export { ID } from 'appwrite';
|
||||
```
|
||||
{% /section %}
|
||||
{% section #step-5 step=5 title="Create a login page" %}
|
||||
Create or update `app/page.jsx` file and add the following code to it.
|
||||
Create or update `app/page.js` file and add the following code to it.
|
||||
|
||||
```js
|
||||
"use client";
|
||||
import { useState } from "react";
|
||||
import { account, ID } from "appwrite";
|
||||
import { account, ID } from "./appwrite";
|
||||
|
||||
const LoginPage = () => {
|
||||
const [loggedInUser, setLoggedInUser] = useState(null);
|
||||
|
||||
@@ -35,7 +35,7 @@ You can skip optional steps.
|
||||
Create a SvelteKit project.
|
||||
|
||||
```sh
|
||||
npm create svelte@next my-app && cd my-app
|
||||
npm create svelte@next my-app && cd my-app && npm install
|
||||
```
|
||||
{% /section %}
|
||||
{% section #step-3 step=3 title="Install Appwrite" %}
|
||||
@@ -58,7 +58,7 @@ Find your project's ID in the **Settings** page.
|
||||
Create a new file `src/lib/appwrite.js` and add the following code to it, replace `<YOUR_PROJECT_ID>` with your project ID.
|
||||
|
||||
```js
|
||||
import { Client, Account} from 'appwrite';
|
||||
import { Client, Account } from 'appwrite';
|
||||
|
||||
export const client = new Client();
|
||||
|
||||
@@ -71,7 +71,7 @@ export { ID } from 'appwrite';
|
||||
```
|
||||
{% /section %}
|
||||
{% section #step-5 step=5 title="Create a login page" %}
|
||||
Create a new file `src/routes/index.svelte` and add the following code to it.
|
||||
Replace the contents of `src/routes/+page.svelte` with the following code.
|
||||
|
||||
```html
|
||||
<script>
|
||||
@@ -79,45 +79,52 @@ Create a new file `src/routes/index.svelte` and add the following code to it.
|
||||
|
||||
let loggedInUser = null;
|
||||
|
||||
let email = '';
|
||||
let password = '';
|
||||
let name = '';
|
||||
|
||||
async function login(email, password) {
|
||||
await account.createEmailSession(email, password);
|
||||
loggedInUser = await account.get();
|
||||
}
|
||||
|
||||
async function register(email, password) {
|
||||
await account.create(ID.unique(), email, password);
|
||||
login(email, password);
|
||||
}
|
||||
|
||||
function submit(e) {
|
||||
e.preventDefault();
|
||||
const formData = new FormData(e.target);
|
||||
const type = e.submitter.dataset.type;
|
||||
|
||||
if (type === "login") {
|
||||
login(formData.get('email'), formData.get('password'));
|
||||
} else if (type === "register") {
|
||||
register(formData.get('email'), formData.get('password'));
|
||||
}
|
||||
}
|
||||
|
||||
function logout() {
|
||||
await account.deleteSession('current');
|
||||
loggedInUser = null;
|
||||
}
|
||||
</script>
|
||||
|
||||
<p>
|
||||
{loggedInUser ? `Logged in as ${loggedInUser.name}` : 'Not logged in'}
|
||||
</p>
|
||||
|
||||
<form>
|
||||
<input type="email" placeholder="Email" bind:value={email} />
|
||||
<input type="password" placeholder="Password" bind:value={password} />
|
||||
<input type="text" placeholder="Name" bind:value={name} />
|
||||
<form on:submit={submit}>
|
||||
<input type="email" placeholder="Email" name="email" required />
|
||||
<input type="password" placeholder="Password" name="password" required />
|
||||
|
||||
<button type="button" on:click={() => login(email, password)}>Login</button>
|
||||
<button
|
||||
type="button"
|
||||
on:click={async () => {
|
||||
await account.create(ID.unique(), email, password, name);
|
||||
login(email, password);
|
||||
}}>Register</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
on:click={async () => {
|
||||
await account.deleteSession('current');
|
||||
loggedInUser = null;
|
||||
}}>Logout</button>
|
||||
<button type="submit" data-type="login">Login</button>
|
||||
<button type="submit" data-type="register">Register</button>
|
||||
</form>
|
||||
|
||||
<button on:click={logout}>Logout</button>
|
||||
```
|
||||
{% /section %}
|
||||
|
||||
{% section #step-6 step=6 title="All set" %}
|
||||
Run your project with `npm run dev -- --open --port 3000` and open [Localhost on Port 3000](http://localhost:3000) in your browser.
|
||||
Run your project with `npm run dev` and open [localhost on port 5173](http://localhost:5173) in your browser.
|
||||
{% /section %}
|
||||
|
||||
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
<script lang="ts" context="module">
|
||||
export function getReferencesContext() {
|
||||
return getContext<Writable<boolean>>('references-expandable');
|
||||
}
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { page } from '$app/stores';
|
||||
import Docs from '$lib/layouts/Docs.svelte';
|
||||
import { preferredPlatform, preferredVersion } from '$lib/utils/references';
|
||||
import { writable, type Writable } from 'svelte/store';
|
||||
import { getContext, setContext } from 'svelte';
|
||||
import Sidebar, { type NavParent, type NavTree } from '$lib/layouts/Sidebar.svelte';
|
||||
import { preferredPlatform, preferredVersion } from '$lib/utils/references';
|
||||
|
||||
const expandable = setContext('references-expandable', writable(false));
|
||||
$: expandable = !!$page.url.pathname.match(/\/docs\/references\/.*?\/.*?\/.*?\/?/);
|
||||
|
||||
$: prefix = `/docs/references/${$preferredVersion ?? $page.params?.version ?? 'cloud'}/${
|
||||
$preferredPlatform ?? $page.params?.platform ?? 'client-web'
|
||||
@@ -96,7 +88,7 @@
|
||||
};
|
||||
</script>
|
||||
|
||||
<Docs variant={$expandable ? 'expanded' : 'two-side-navs'} isReferences={$expandable}>
|
||||
<Sidebar {navigation} expandable={$expandable} {parent} />
|
||||
<Docs variant={expandable ? 'expanded' : 'two-side-navs'} isReferences={expandable}>
|
||||
<Sidebar {navigation} {expandable} {parent} />
|
||||
<slot />
|
||||
</Docs>
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from 'svelte';
|
||||
import { getReferencesContext } from '$routes/docs/references/+layout.svelte';
|
||||
|
||||
const expandable = getReferencesContext();
|
||||
|
||||
onMount(() => {
|
||||
expandable.set(true);
|
||||
|
||||
return () => {
|
||||
expandable.set(false);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
@@ -98,9 +98,9 @@
|
||||
<div class="aw-inline-code">{platformType}</div>
|
||||
</div>
|
||||
<div class="aw-article-header-end">
|
||||
<div class="u-flex u-gap-24 aw-u-color-text-primary">
|
||||
<div class="u-flex u-gap-24 aw-u-flex-vertical-mobile aw-u-color-text-primary">
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
<label class="u-small is-not-mobile" for="platform">Platform</label>
|
||||
<label class="u-small aw-is-not-mobile" for="platform">Platform</label>
|
||||
<Select
|
||||
--min-width="10rem"
|
||||
id="platform"
|
||||
@@ -126,7 +126,7 @@
|
||||
/>
|
||||
</div>
|
||||
<div class="u-flex u-cross-center u-gap-8">
|
||||
<label class="u-small is-not-mobile" for="version">Version</label>
|
||||
<label class="u-small aw-is-not-mobile" for="version">Version</label>
|
||||
|
||||
<Select
|
||||
nativeMobile
|
||||
@@ -340,7 +340,7 @@
|
||||
use:scrollToTop
|
||||
>
|
||||
<span class="aw-icon-arrow-up" aria-hidden="true" />
|
||||
<span class="aw-sub-body-500">Back to top</span>
|
||||
<span class="aw-caption-400">Back to top</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -115,11 +115,17 @@ We are always looking to add new SDKs to our platform. If the SDK you are lookin
|
||||
|
||||
Appwrite supports multiple API protocols for maximum flexibility and developer convenience. You can learn more about how to integrate directly with them using one of the following available guides:
|
||||
|
||||
[Integrate with the Appwrite Realtime API {% icon icon="cheveron-right" /%}](/docs/apis/realtime)
|
||||
{% arrow_link href="/docs/apis/realtime" %}
|
||||
Integrate with the Appwrite Realtime API
|
||||
{% /arrow_link %}
|
||||
|
||||
[Integrate with the Appwrite REST API {% icon icon="cheveron-right" /%}](/docs/apis/rest)
|
||||
{% arrow_link href="/docs/apis/rest" %}
|
||||
Integrate with the Appwrite REST API
|
||||
{% /arrow_link %}
|
||||
|
||||
[Integrate with the Appwrite GraphQL API {% icon icon="cheveron-right" /%}](/docs/apis/graphql)
|
||||
{% arrow_link href="/docs/apis/graphql" %}
|
||||
Integrate with the Appwrite GraphQL API
|
||||
{% /arrow_link %}
|
||||
|
||||
# Community {% #community %}
|
||||
If you have created your own framework or any other technology specific integration and would like us to list it here please [contact us](/contact-us).
|
||||
|
||||
@@ -113,9 +113,9 @@ Here's a complete list of all configurable options in `appwrite.json`:
|
||||
| `runtime` | string | Function runtime, must be enabled. [Learn more about runtimes](/docs/products/functions/runtimes#available-runtimes). |
|
||||
| `path` | string | Path to folder containing the function's source code. |
|
||||
| `entrypoint` | string | Entry point of the function relative to the folder specified in path. |
|
||||
| `ignore` | array of strings | Files to ignore inside the function. [Learn more about ignoring files](/docs). |
|
||||
| `ignore` | array of strings | Files from your source code that should be ignored when creating a deployment. |
|
||||
| `execute` | array of strings | Grants execute permissions to users. [Learn more about role strings](/docs/apis/rest#permissions). |
|
||||
| `events` | array of strings | Events that trigger the function to execute. [Learn more about events](/docs). |
|
||||
| `events` | array of strings | Events that trigger the function to execute. [Learn more about events](/docs/advanced/platform/events). |
|
||||
| `schedule` | string | Execute schedule for the function. [Learn more about scheduled executions](/docs/products/functions/execution#schedule). |
|
||||
| `timeout` | int | Execution timeout of the function in seconds, with a maximum configurable limit of 900 seconds. |
|
||||
| `variables` | JSON object | Variables provided to the function on execution stored as a key-value JSON object. |
|
||||
|
||||
@@ -1,12 +1,32 @@
|
||||
<script lang="ts">
|
||||
import { MainFooter } from '$lib/components';
|
||||
import { DEFAULT_DESCRIPTION, DEFAULT_HOST } from '$lib/utils/metadata';
|
||||
import { themeInUse } from '$routes/+layout.svelte';
|
||||
import { DOCS_TITLE_SUFFIX } from '$routes/titles';
|
||||
|
||||
const title = 'Tutorials' + DOCS_TITLE_SUFFIX;
|
||||
const description = DEFAULT_DESCRIPTION;
|
||||
const ogImage = DEFAULT_HOST + '/images/open-graph/docs.png';
|
||||
|
||||
export let data;
|
||||
|
||||
type MappedTutorial = (typeof data.tutorials)[number];
|
||||
|
||||
const iconMap: Record<string, string> = {
|
||||
react: 'icon-react',
|
||||
vue: 'aw-icon-vue',
|
||||
angular: 'icon-angular',
|
||||
svelte: 'icon-svelte',
|
||||
sveltekit: 'icon-svelte',
|
||||
android: 'icon-android',
|
||||
apple: 'icon-apple',
|
||||
flutter: 'icon-flutter',
|
||||
refine: 'icon-refine'
|
||||
};
|
||||
|
||||
const getIcon = (tutorial: MappedTutorial) => {
|
||||
if (!tutorial.framework) return ''; // TODO: Default icon
|
||||
return iconMap[tutorial.framework.toLowerCase()];
|
||||
};
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
@@ -39,78 +59,43 @@
|
||||
<div class="aw-article-content aw-u-gap-80">
|
||||
<section class="u-flex-vertical u-gap-24">
|
||||
<h2 class="aw-eyebrow">Client</h2>
|
||||
<ul class="aw-grid-row-4 aw-grid-row-4-mobile-2">
|
||||
<li class="is-mobile-col-span-2">
|
||||
<a href="/docs/tutorials/react" class="aw-card is-normal">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="icon-react aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">React</h4>
|
||||
<ul class="tutorial-grid">
|
||||
{#each data.tutorials as tutorial}
|
||||
<li>
|
||||
<a href={tutorial.href} class="aw-card is-normal">
|
||||
<header>
|
||||
<span
|
||||
class="{getIcon(tutorial)} aw-u-font-size-24"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h3 class="aw-sub-body-500 aw-u-color-text-primary">
|
||||
{tutorial.framework}
|
||||
</h3>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">
|
||||
Learn Appwrite Auth, Databases, and more with React.
|
||||
{tutorial.title}
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<a href="/docs/tutorials/vue" class="aw-card is-normal">
|
||||
<header class="u-flex u-cross-center u-gap-4">
|
||||
<img src="/images/platforms/{$themeInUse}/vue.svg" alt="" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Vue</h4>
|
||||
{/each}
|
||||
</ul>
|
||||
<ul class="tutorial-grid">
|
||||
{#each data.drafts as draft}
|
||||
<li>
|
||||
<a href={draft.href} class="aw-card is-normal draft">
|
||||
<header>
|
||||
<span
|
||||
class="{getIcon(draft)} aw-u-font-size-24"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<h3 class="aw-sub-body-500 aw-u-color-text-primary">
|
||||
{draft.framework}
|
||||
</h3>
|
||||
<span class="badge aw-caption-400">Coming Soon</span>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">
|
||||
Learn Appwrite Auth, Databases, and more with Vue.
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<a href="/docs/tutorials/sveltekit" class="aw-card is-normal">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="icon-svelte aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">SvelteKit</h4>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">
|
||||
Learn Appwrite Auth, Databases, and more with SvelteKit.
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<a href="/docs/tutorials/refine" class="aw-card is-normal">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="aw-icon-refine aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Refine</h4>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">
|
||||
Learn Appwrite Auth, Databases, and more with Refine.
|
||||
</p>
|
||||
</a>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<article class="aw-card is-full-color">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="icon-flutter aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Flutter</h4>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">Coming soon...</p>
|
||||
</article>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<article class="aw-card is-full-color">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="icon-apple aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Apple</h4>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">Coming soon...</p>
|
||||
</article>
|
||||
</li>
|
||||
<li class="is-mobile-col-span-2">
|
||||
<article class="aw-card is-full-color">
|
||||
<header class="u-flex u-cross-baseline u-gap-4">
|
||||
<span class="icon-android aw-u-font-size-24" aria-hidden="true" />
|
||||
<h4 class="aw-sub-body-500 aw-u-color-text-primary">Android</h4>
|
||||
</header>
|
||||
<p class="aw-sub-body-400 u-margin-block-start-4">Coming soon...</p>
|
||||
</article>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
</div>
|
||||
@@ -118,3 +103,45 @@
|
||||
|
||||
<MainFooter variant="docs" />
|
||||
</main>
|
||||
|
||||
<style lang="scss">
|
||||
.tutorial-grid {
|
||||
display: grid;
|
||||
gap: 1.5rem; // 24px
|
||||
grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
|
||||
|
||||
.aw-card {
|
||||
padding: 1.25rem;
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
align-items: center;
|
||||
|
||||
> [class*='icon'] {
|
||||
position: relative;
|
||||
width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
|
||||
&::before {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, calc(-50%));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.badge {
|
||||
border-radius: 0.25rem;
|
||||
background: rgba(253, 54, 110, 0.24);
|
||||
padding: 0.0625rem 0.375rem;
|
||||
margin-inline-start: 0.25rem;
|
||||
}
|
||||
|
||||
.draft {
|
||||
opacity: 0.4;
|
||||
}
|
||||
</style>
|
||||
52
src/routes/docs/tutorials/+page.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { base } from '$app/paths';
|
||||
import type { Tutorial } from '$markdoc/layouts/Tutorial.svelte';
|
||||
|
||||
const framework_order = ['React', 'Vue', 'SvelteKit'];
|
||||
|
||||
export async function load() {
|
||||
const tutorialsGlob = import.meta.glob('./**/step-1/+page.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
|
||||
const allTutorials = Object.entries(tutorialsGlob)
|
||||
.map(([filepath, tutorialList]: [string, unknown]) => {
|
||||
const { frontmatter } = tutorialList as {
|
||||
frontmatter: Tutorial;
|
||||
};
|
||||
const slug = filepath
|
||||
.replace('./', '')
|
||||
.replace('/+page.markdoc', '')
|
||||
.replace('/step-1', '');
|
||||
const tutorialName = slug.slice(slug.lastIndexOf('/') + 1);
|
||||
|
||||
return {
|
||||
title: frontmatter.title,
|
||||
framework: frontmatter.framework,
|
||||
draft: frontmatter.draft,
|
||||
href: `${base}/docs/tutorials/${tutorialName}`
|
||||
};
|
||||
})
|
||||
.sort((a, b) => {
|
||||
// Sort by framework order
|
||||
const frameworkIndexA = framework_order.indexOf(a.framework as string);
|
||||
const frameworkIndexB = framework_order.indexOf(b.framework as string);
|
||||
|
||||
if (frameworkIndexA > frameworkIndexB) {
|
||||
return 1;
|
||||
}
|
||||
if (frameworkIndexA < frameworkIndexB) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Else, sort by title
|
||||
return a.title.toLowerCase().localeCompare(b.title.toLowerCase());
|
||||
});
|
||||
|
||||
const drafts = allTutorials.filter((tutorial) => tutorial.draft);
|
||||
const tutorials = allTutorials.filter((tutorial) => !tutorial.draft);
|
||||
|
||||
return {
|
||||
tutorials,
|
||||
drafts
|
||||
};
|
||||
}
|
||||
@@ -2,7 +2,9 @@
|
||||
layout: tutorial
|
||||
title: Coming soon
|
||||
description: Learn to build an Android app with no backend code using an Appwrite backend.
|
||||
framework: Android
|
||||
step: 1
|
||||
draft: true
|
||||
---
|
||||
|
||||
Improve the docs, add this guide.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
layout: tutorial
|
||||
title: Coming soon
|
||||
description: Learn to build an Apple app with no backend code using an Appwrite backend.
|
||||
framework: Apple
|
||||
step: 1
|
||||
draft: true
|
||||
---
|
||||
|
||||
Improve the docs, add this guide.
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
layout: tutorial
|
||||
title: Coming soon
|
||||
description: Learn to build an Flutter app with no backend code using an Appwrite backend.
|
||||
framework: Flutter
|
||||
step: 1
|
||||
draft: true
|
||||
---
|
||||
|
||||
Improve the docs, add this guide.
|
||||
|
||||
@@ -5,6 +5,7 @@ description: Learn to build a React app with no backend code using an Appwrite b
|
||||
step: 1
|
||||
difficulty: beginner
|
||||
readtime: 10
|
||||
framework: React
|
||||
---
|
||||
|
||||
**Idea tracker**: an app to track all the side project ideas that you'll start, but probably never finish.
|
||||
|
||||
@@ -46,7 +46,7 @@ export function Home() {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() =>
|
||||
ideas.add({ userId: user.current.id, title, description })
|
||||
ideas.add({ userId: user.current.$id, title, description })
|
||||
}
|
||||
>
|
||||
Submit
|
||||
|
||||
10
src/routes/docs/tutorials/sveltekit-auth/+layout.svelte
Normal file
@@ -0,0 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { globToTutorial } from '$lib/utils/tutorials.js';
|
||||
import { setContext } from 'svelte';
|
||||
|
||||
export let data;
|
||||
const tutorials = globToTutorial(data);
|
||||
setContext('tutorials', tutorials);
|
||||
</script>
|
||||
|
||||
<slot />
|
||||
11
src/routes/docs/tutorials/sveltekit-auth/+layout.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import type { LayoutLoad } from './$types';
|
||||
|
||||
export const load: LayoutLoad = ({ url }) => {
|
||||
const tutorials = import.meta.glob('./**/*.markdoc', {
|
||||
eager: true
|
||||
});
|
||||
return {
|
||||
tutorials,
|
||||
pathname: url.pathname
|
||||
};
|
||||
};
|
||||